2 #include "../port/lib.h"
7 #include "../port/error.h"
8 #include "../port/audioif.h"
10 typedef struct Ring Ring;
11 typedef struct Hwdesc Hwdesc;
12 typedef struct Ctlr Ctlr;
26 Bufsize = 32768, /* bytes, must be divisible by ndesc */
27 Blocksize = Bufsize/Ndesc,
28 Maxbusywait = 500000, /* microseconds, roughly */
44 /* keep these first, they want to be 8-aligned */
46 Hwdesc outdesc[Ndesc];
47 Hwdesc micdesc[Ndesc];
57 Ring inring, micring, outring;
71 Bar = 0x00, /* Base address register, 8-byte aligned */
72 /* a 32-bit read at 0x04 can be used to get civ:lvi:sr in one step */
73 Civ = 0x04, /* current index value (desc being processed) */
74 Lvi = 0x05, /* Last valid index (index of first unused entry!) */
75 Sr = 0x06, /* status register */
76 Fifoe = 1<<4, /* fifo error (r/wc) */
77 Bcis = 1<<3, /* buffer completion interrupt status (r/wc) */
78 Lvbci = 1<<2, /* last valid buffer completion(in)/fetched(out) interrupt (r/wc) */
79 Celv = 1<<1, /* current equals last valid (ro) */
80 Dch = 1<<0, /* dma controller halted (ro) */
81 Picb = 0x08, /* position in current buffer */
82 Piv = 0x0a, /* prefetched index value */
83 Cr = 0x0b, /* control register */
84 Ioce = 1<<4, /* interrupt on buffer completion (if bit set in hwdesc.size) (rw) */
85 Feie = 1<<3, /* fifo error interrupt enable (rw) */
86 Lvbie = 1<<2, /* last valid buffer interrupt enable (rw) */
87 RR = 1<<1, /* reset busmaster related regs, excl. ioce,feie,lvbie (rw) */
88 Rpbm = 1<<0, /* run/pause busmaster. 0 stops, 1 starts (rw) */
89 Cnt = 0x2c, /* global control */
96 Sr2ie = 1<<6, /* sdin2 interrupt enable (rw) */
97 Srie = 1<<5, /* sdin1 interrupt enable (rw) */
98 Prie = 1<<4, /* sdin0 interrupt enable (rw) */
99 Aclso = 1<<3, /* ac link shut-off (rw) */
100 Acwr = 1<<2, /* ac 97 warm reset (rw) */
101 Accr = 1<<1, /* ac 97 cold reset (rw) */
102 GPIie = 1<<0, /* GPI interrupt enable (rw) */
103 Sta = 0x30, /* global status */
106 Md3 = 1<<17, /* modem powerdown semaphore */
107 Ad3 = 1<<16, /* audio powerdown semaphore */
108 Rcs = 1<<15, /* read completion status (r/wc) */
109 S2ri = 1<<29, /* sdin2 resume interrupt (r/wc) */
110 Sri = 1<<11, /* sdin1 resume interrupt (r/wc) */
111 Pri = 1<<10, /* sdin0 resume interrupt (r/wc) */
112 S2cr = 1<<28, /* sdin2 codec ready (ro) */
113 Scr = 1<<9, /* sdin1 codec ready (ro) */
114 Pcr = 1<<8, /* sdin0 codec ready (ro) */
115 Mint = 1<<7, /* microphone in inetrrupt (ro) */
116 Point = 1<<6, /* pcm out interrupt (ro) */
117 Piint = 1<<5, /* pcm in interrupt (ro) */
118 Moint = 1<<2, /* modem out interrupt (ro) */
119 Miint = 1<<1, /* modem in interrupt (ro) */
120 Gsci = 1<<0, /* GPI status change interrupt */
121 Cas = 0x34, /* codec access semaphore */
122 Casp = 1<<0, /* set to 1 on read if zero, cleared by hardware */
135 return r->nbuf - (ri - wi);
143 m = (r->nbuf - BytesPerSample) - buffered(r);
150 readring(Ring *r, uchar *p, long n)
156 if((m = buffered(r)) <= 0)
161 if(r->ri + m > r->nbuf)
163 memmove(p, r->buf + r->ri, m);
166 r->ri = (r->ri + m) % r->nbuf;
173 writering(Ring *r, uchar *p, long n)
179 if((m = available(r)) <= 0)
184 if(r->wi + m > r->nbuf)
186 memmove(r->buf + r->wi, p, m);
189 r->wi = (r->wi + m) % r->nbuf;
196 csr8r(Ctlr *c, int r){
198 return *(uchar*)(c->mmreg+r);
199 return inb(c->port+r);
203 csr16r(Ctlr *c, int r){
205 return *(ushort*)(c->mmreg+r);
206 return ins(c->port+r);
210 csr32r(Ctlr *c, int r){
212 return *(ulong*)(c->mmreg+r);
213 return inl(c->port+r);
217 csr8w(Ctlr *c, int r, uchar v){
219 *(uchar*)(c->mmreg+r) = v;
221 outb(c->port+r, (int)v);
225 csr16w(Ctlr *c, int r, ushort v){
227 *(ushort*)(c->mmreg+r) = v;
233 csr32w(Ctlr *c, int r, ulong v){
235 *(ulong*)(c->mmreg+r) = v;
241 extern void ac97mixreset(Audio *,
242 void (*wr)(Audio*,int,ushort),
243 ushort (*rr)(Audio*,int));
246 ac97waitcodec(Audio *adev)
251 for(i = 0; i <= Maxbusywait/10; i++){
252 if((csr8r(ctlr, Cas) & Casp) == 0)
256 print("#A%d: ac97 exhausted waiting codec access\n", adev->ctlrno);
260 ac97mixw(Audio *adev, int port, ushort val)
266 *(ushort*)(ctlr->mmmix+port) = val;
268 outs(ctlr->mixport+port, val);
272 ac97mixr(Audio *adev, int port)
278 return *(ushort*)(ctlr->mmmix+port);
279 return ins(ctlr->mixport+port);
283 ac97interrupt(Ureg *, void *arg)
291 stat = csr32r(ctlr, Sta);
292 stat &= S2ri | Sri | Pri | Mint | Point | Piint | Moint | Miint | Gsci;
293 if(stat & (Point|Piint|Mint)){
296 ctlr->outring.ri = csr8r(ctlr, Out + Civ) * Blocksize;
297 wakeup(&ctlr->outring.r);
300 csr16w(ctlr, Out + Picb, csr16r(ctlr, Out + Picb) & ~Dch);
302 csr16w(ctlr, Out + Sr, csr16r(ctlr, Out + Sr) & ~Dch);
306 ctlr->inring.wi = csr8r(ctlr, In + Civ) * Blocksize;
307 wakeup(&ctlr->inring.r);
310 csr16w(ctlr, In + Picb, csr16r(ctlr, In + Picb) & ~Dch);
312 csr16w(ctlr, In + Sr, csr16r(ctlr, In + Sr) & ~Dch);
316 ctlr->micring.wi = csr8r(ctlr, Mic + Civ) * Blocksize;
317 wakeup(&ctlr->micring.r);
320 csr16w(ctlr, Mic + Picb, csr16r(ctlr, Mic + Picb) & ~Dch);
322 csr16w(ctlr, Mic + Sr, csr16r(ctlr, Mic + Sr) & ~Dch);
327 if(stat) /* have seen 0x400, which is sdin0 resume */
328 iprint("#A%d: ac97 unhandled interrupt(s): stat 0x%lux\n",
333 ac97buffered(Audio *adev)
335 Ctlr *ctlr = adev->ctlr;
336 return buffered(&ctlr->outring);
340 ac97status(Audio *adev, void *a, long n, vlong)
342 Ctlr *ctlr = adev->ctlr;
343 return snprint((char*)a, n, "bufsize %6d buffered %6ld\n",
344 Blocksize, buffered(&ctlr->outring));
351 return buffered(r) > 0;
358 return available(r) > 0;
365 int delay = ctlr->adev->delay*BytesPerSample;
366 return (delay <= 0) || (buffered(&ctlr->outring) <= delay);
370 ac97read(Audio *adev, void *vp, long n, vlong)
380 ring = &ctlr->inring;
382 oi = ring->ri / Blocksize;
383 if((n = readring(ring, p, e - p)) <= 0){
384 csr8w(ctlr, In + Lvi, (oi - 1) % Ndesc);
385 csr8w(ctlr, In + Cr, Ioce | Rpbm);
386 sleep(&ring->r, inavail, ring);
389 ni = ring->ri / Blocksize;
391 csr8w(ctlr, In + Lvi, (oi - 1) % Ndesc);
392 csr8w(ctlr, In + Cr, Ioce | Rpbm);
393 oi = (oi + 1) % Ndesc;
397 return p - (uchar*)vp;
401 ac97write(Audio *adev, void *vp, long n, vlong)
411 ring = &ctlr->outring;
413 oi = ring->wi / Blocksize;
414 if((n = writering(ring, p, e - p)) <= 0){
415 sleep(&ring->r, outavail, ring);
418 ni = ring->wi / Blocksize;
420 csr8w(ctlr, Out+Lvi, oi);
421 csr8w(ctlr, Out+Cr, Ioce | Rpbm);
422 oi = (oi + 1) % Ndesc;
426 while(outrate(ctlr) == 0)
427 sleep(&ring->r, outrate, ctlr);
428 return p - (uchar*)vp;
432 ac97close(Audio *adev, int mode)
441 ring = &ctlr->outring;
442 while(ring->wi % Blocksize)
443 if(writering(ring, (uchar*)"", 1) <= 0)
450 /* not all of the matched devices have been tested */
451 while(p = pcimatch(p, 0, 0))
452 switch((p->vid<<16)|p->did){
453 case (0x1039<<16)|0x7012:
454 case (0x1022<<16)|0x746d:
455 case (0x1022<<16)|0x7445:
456 case (0x10de<<16)|0x01b1:
457 case (0x10de<<16)|0x006a:
458 case (0x10de<<16)|0x00da:
459 case (0x10de<<16)|0x00ea:
460 case (0x8086<<16)|0x2415:
461 case (0x8086<<16)|0x2425:
462 case (0x8086<<16)|0x2445:
463 case (0x8086<<16)|0x2485:
464 case (0x8086<<16)|0x24c5:
465 case (0x8086<<16)|0x24d5:
466 case (0x8086<<16)|0x25a6:
467 case (0x8086<<16)|0x266e:
468 case (0x8086<<16)|0x7195:
475 sethwp(Ctlr *ctlr, long off, void *ptr)
477 csr8w(ctlr, off+Cr, RR);
478 csr32w(ctlr, off+Bar, PCIWADDR(ptr));
479 csr8w(ctlr, off+Lvi, 0);
483 ac97reset(Audio *adev)
485 static Ctlr *cards = nil;
491 /* make a list of all ac97 cards if not already done */
494 while(p = ac97match(p)){
495 ctlr = xspanalloc(sizeof(Ctlr), 8, 0);
496 memset(ctlr, 0, sizeof(Ctlr));
503 /* pick a card from the list */
504 for(ctlr = cards; ctlr; ctlr = ctlr->next){
505 if(p = ctlr->pcidev){
516 /* ICH4 through ICH7 may use memory-type base address registers */
517 if(p->vid == 0x8086 &&
518 (p->did == 0x24c5 || p->did == 0x24d5 || p->did == 0x266e || p->did == 0x27de) &&
519 (p->mem[2].bar != 0 && p->mem[3].bar != 0) &&
520 ((p->mem[2].bar & 1) == 0 && (p->mem[3].bar & 1) == 0)){
521 ctlr->mmmix = vmap(p->mem[2].bar & ~0xf, p->mem[2].size);
522 if(ctlr->mmmix == nil){
523 print("ac97: vmap failed for mmmix 0x%08lux\n", p->mem[2].bar);
526 ctlr->mmreg = vmap(p->mem[3].bar & ~0xf, p->mem[3].size);
527 if(ctlr->mmreg == nil){
528 print("ac97: vmap failed for mmreg 0x%08lux\n", p->mem[3].bar);
529 vunmap(ctlr->mmmix, p->mem[2].size);
534 if((p->mem[0].bar & 1) == 0 || (p->mem[1].bar & 1) == 0){
535 print("ac97: not i/o regions 0x%04lux 0x%04lux\n", p->mem[0].bar, p->mem[1].bar);
539 if(p->vid == 0x1039 && p->did == 0x7012){
540 ctlr->sis7012 = 1; /* i/o bars swapped? */
543 ctlr->port = p->mem[1].bar & ~3;
544 if(ioalloc(ctlr->port, p->mem[1].size, 0, "ac97") < 0){
545 print("ac97: ioalloc failed for port 0x%04lux\n", ctlr->port);
548 ctlr->mixport = p->mem[0].bar & ~3;
549 if(ioalloc(ctlr->mixport, p->mem[0].size, 0, "ac97mix") < 0){
550 print("ac97: ioalloc failed for mixport 0x%04lux\n", ctlr->mixport);
559 print("#A%d: ac97 port 0x%04lux mixport 0x%04lux irq %d\n",
560 adev->ctlrno, ctlr->port, ctlr->mixport, irq);
565 ctlr->micring.buf = xspanalloc(Bufsize, 8, 0);
566 ctlr->micring.nbuf = Bufsize;
567 ctlr->micring.ri = 0;
568 ctlr->micring.wi = 0;
570 ctlr->inring.buf = xspanalloc(Bufsize, 8, 0);
571 ctlr->inring.nbuf = Bufsize;
575 ctlr->outring.buf = xspanalloc(Bufsize, 8, 0);
576 ctlr->outring.nbuf = Bufsize;
577 ctlr->outring.ri = 0;
578 ctlr->outring.wi = 0;
580 for(i = 0; i < Ndesc; i++){
581 int size, off = i * Blocksize;
586 size = Blocksize / 2;
587 ctlr->micdesc[i].addr = PCIWADDR(ctlr->micring.buf + off);
588 ctlr->micdesc[i].size = Ioc | size;
589 ctlr->indesc[i].addr = PCIWADDR(ctlr->inring.buf + off);
590 ctlr->indesc[i].size = Ioc | size;
591 ctlr->outdesc[i].addr = PCIWADDR(ctlr->outring.buf + off);
592 ctlr->outdesc[i].size = Ioc | size;
595 ctl = csr32r(ctlr, Cnt);
596 ctl &= ~(EnaRESER | Aclso);
598 if((ctl & Accr) == 0){
599 print("#A%d: ac97 cold reset\n", adev->ctlrno);
602 print("#A%d: ac97 warm reset\n", adev->ctlrno);
606 csr32w(ctlr, Cnt, ctl);
607 for(i = 0; i < Maxbusywait; i++){
608 if((csr32r(ctlr, Cnt) & Acwr) == 0)
613 print("#A%d: ac97 gave up waiting Acwr to go down\n", adev->ctlrno);
615 for(i = 0; i < Maxbusywait; i++){
616 if((stat = csr32r(ctlr, Sta)) & (Pcr | Scr | S2cr))
621 print("#A%d: ac97 gave up waiting codecs become ready\n", adev->ctlrno);
623 print("#A%d: ac97 codecs ready:%s%s%s\n", adev->ctlrno,
624 (stat & Pcr) ? " sdin0" : "",
625 (stat & Scr) ? " sdin1" : "",
626 (stat & S2cr) ? " sdin2" : "");
628 print("#A%d: ac97 codecs resumed:%s%s%s\n", adev->ctlrno,
629 (stat & Pri) ? " sdin0" : "",
630 (stat & Sri) ? " sdin1" : "",
631 (stat & S2ri) ? " sdin2" : "");
633 sethwp(ctlr, In, ctlr->indesc);
634 sethwp(ctlr, Out, ctlr->outdesc);
635 sethwp(ctlr, Mic, ctlr->micdesc);
637 csr8w(ctlr, In+Cr, Ioce); /* | Lvbie | Feie */
638 csr8w(ctlr, Out+Cr, Ioce); /* | Lvbie | Feie */
639 csr8w(ctlr, Mic+Cr, Ioce); /* | Lvbie | Feie */
641 ac97mixreset(adev, ac97mixw, ac97mixr);
643 adev->read = ac97read;
644 adev->write = ac97write;
645 adev->close = ac97close;
646 adev->buffered = ac97buffered;
647 adev->status = ac97status;
649 intrenable(irq, ac97interrupt, adev, tbdf, adev->name);
657 addaudiocard("ac97", ac97reset);