2 #include "../port/lib.h"
7 #include "../port/error.h"
8 #include "../port/audioif.h"
10 typedef struct Hwdesc Hwdesc;
11 typedef struct Ctlr Ctlr;
26 Bufsize = 32768, /* bytes, must be divisible by ndesc */
27 Maxbusywait = 500000, /* microseconds, roughly */
32 /* keep these first, they want to be 8-aligned */
34 Hwdesc outdesc[Ndesc];
35 Hwdesc micdesc[Ndesc];
51 /* shared variables, ilock to access */
56 /* interrupt handler alone */
76 #define iorl(c, r) (inl((c)->port+(r)))
77 #define iowl(c, r, l) (outl((c)->port+(r), (ulong)(l)))
83 Bar = 0x00, /* Base address register, 8-byte aligned */
84 /* a 32-bit read at 0x04 can be used to get civ:lvi:sr in one step */
85 Civ = 0x04, /* current index value (desc being processed) */
86 Lvi = 0x05, /* Last valid index (index of first unused entry!) */
87 Sr = 0x06, /* status register */
88 Fifoe = 1<<4, /* fifo error (r/wc) */
89 Bcis = 1<<3, /* buffer completion interrupt status (r/wc) */
90 Lvbci = 1<<2, /* last valid buffer completion(in)/fetched(out) interrupt (r/wc) */
91 Celv = 1<<1, /* current equals last valid (ro) */
92 Dch = 1<<0, /* dma controller halted (ro) */
93 Picb = 0x08, /* position in current buffer */
94 Piv = 0x0a, /* prefetched index value */
95 Cr = 0x0b, /* control register */
96 Ioce = 1<<4, /* interrupt on buffer completion (if bit set in hwdesc.size) (rw) */
97 Feie = 1<<3, /* fifo error interrupt enable (rw) */
98 Lvbie = 1<<2, /* last valid buffer interrupt enable (rw) */
99 RR = 1<<1, /* reset busmaster related regs, excl. ioce,feie,lvbie (rw) */
100 Rpbm = 1<<0, /* run/pause busmaster. 0 stops, 1 starts (rw) */
101 Cnt = 0x2c, /* global control */
108 Sr2ie = 1<<6, /* sdin2 interrupt enable (rw) */
109 Srie = 1<<5, /* sdin1 interrupt enable (rw) */
110 Prie = 1<<4, /* sdin0 interrupt enable (rw) */
111 Aclso = 1<<3, /* ac link shut-off (rw) */
112 Acwr = 1<<2, /* ac 97 warm reset (rw) */
113 Accr = 1<<1, /* ac 97 cold reset (rw) */
114 GPIie = 1<<0, /* GPI interrupt enable (rw) */
115 Sta = 0x30, /* global status */
118 Md3 = 1<<17, /* modem powerdown semaphore */
119 Ad3 = 1<<16, /* audio powerdown semaphore */
120 Rcs = 1<<15, /* read completion status (r/wc) */
121 S2ri = 1<<29, /* sdin2 resume interrupt (r/wc) */
122 Sri = 1<<11, /* sdin1 resume interrupt (r/wc) */
123 Pri = 1<<10, /* sdin0 resume interrupt (r/wc) */
124 S2cr = 1<<28, /* sdin2 codec ready (ro) */
125 Scr = 1<<9, /* sdin1 codec ready (ro) */
126 Pcr = 1<<8, /* sdin0 codec ready (ro) */
127 Mint = 1<<7, /* microphone in inetrrupt (ro) */
128 Point = 1<<6, /* pcm out interrupt (ro) */
129 Piint = 1<<5, /* pcm in interrupt (ro) */
130 Moint = 1<<2, /* modem out interrupt (ro) */
131 Miint = 1<<1, /* modem in interrupt (ro) */
132 Gsci = 1<<0, /* GPI status change interrupt */
133 Cas = 0x34, /* codec access semaphore */
134 Casp = 1<<0, /* set to 1 on read if zero, cleared by hardware */
137 #define csr8r(c, r) (inb((c)->port+(r)))
138 #define csr16r(c, r) (ins((c)->port+(r)))
139 #define csr32r(c, r) (inl((c)->port+(r)))
140 #define csr8w(c, r, b) (outb((c)->port+(r), (int)(b)))
141 #define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w)))
142 #define csr32w(c, r, w) (outl((c)->port+(r), (ulong)(w)))
145 extern void ac97mixreset(Audio *,
146 void (*wr)(Audio*,int,ushort),
147 ushort (*rr)(Audio*,int));
150 ac97waitcodec(Audio *adev)
155 for(i = 0; i < Maxbusywait/10; i++){
156 if((csr8r(ctlr, Cas) & Casp) == 0)
161 print("#A%d: ac97 exhausted waiting codec access\n", adev->ctlrno);
165 ac97mixw(Audio *adev, int port, ushort val)
170 outs(ctlr->mixport+port, val);
174 ac97mixr(Audio *adev, int port)
179 return ins(ctlr->mixport+port);
187 return ctlr->outavail > 0;
191 ac97interrupt(Ureg *, void *arg)
201 stat = csr32r(ctlr, Sta);
202 stat &= S2ri | Sri | Pri | Mint | Point | Piint | Moint | Miint | Gsci;
206 csr16w(ctlr, Out + Picb, csr16r(ctlr, Out + Picb) & ~Dch);
208 csr16w(ctlr, Out + Sr, csr16r(ctlr, Out + Sr) & ~Dch);
210 civ = csr8r(ctlr, Out + Civ);
212 while(ctlr->outciv != civ){
213 ctlr->civstat[ctlr->outciv++]++;
214 if(ctlr->outciv == Ndesc)
222 ctlr->tsout[i] = now - ctlr->tsoutp;
223 ctlr->tsouti = (i + 1) % Nts;
227 if(ctlr->outavail > Bufsize/2)
232 if(stat) /* have seen 0x400, which is sdin0 resume */
233 iprint("#A%d: ac97 unhandled interrupt(s): stat 0x%lux\n",
238 off2lvi(char *base, char *p)
242 return lvi / (Bufsize/Ndesc);
246 ac97medianoutrate(Audio *adev)
254 for(i = 0; i < Nts; i++)
255 if(ctlr->tsout[i] > 0)
256 ts[i] = (ctlr->tsoutb[i] * hz) / ctlr->tsout[i];
259 for(i = 1; i < Nts; i++){
262 while(j > 0 && ts[j-1] > t){
268 return ts[Nts/2] / BytesPerSample;
272 ac97status(Audio *adev, void *a, long n, vlong)
283 p += snprint(p, e - p, "median rate %lud\n", ac97medianoutrate(adev));
284 p += snprint(p, e - p, "civ stats");
285 for(i = 0; i < Ndesc; i++)
286 p += snprint(p, e - p, " %lud", ctlr->civstat[i]);
287 p += snprint(p, e - p, "\n");
289 p += snprint(p, e - p, "lvi stats");
290 for(i = 0; i < Ndesc; i++)
291 p += snprint(p, e - p, " %lud", ctlr->lvistat[i]);
292 p += snprint(p, e - p, "\n");
298 ac97buffered(Audio *adev)
302 return Bufsize - Bufsize/Ndesc - ctlr->outavail;
306 ac97kick(Ctlr *ctlr, long reg)
308 csr8w(ctlr, reg+Cr, Ioce | Rpbm);
312 ac97write(Audio *adev, void *a, long nwr, vlong)
324 ep = ctlr->out + Bufsize;
325 olvi = off2lvi(ctlr->out, p);
329 if(ctlr->outavail < len)
330 len = ctlr->outavail;
333 ctlr->outavail -= len;
342 lvi = off2lvi(ctlr->out, p);
348 csr8w(ctlr, Out+Lvi, t);
353 if(ctlr->outavail == 0){
356 sleep(&ctlr->outr, outavail, ctlr);
368 /* not all of the matched devices have been tested */
369 while(p = pcimatch(p, 0, 0))
370 switch((p->vid<<16)|p->did){
371 case (0x1039<<16)|0x7012:
372 case (0x1022<<16)|0x746d:
373 case (0x1022<<16)|0x7445:
374 case (0x10de<<16)|0x01b1:
375 case (0x10de<<16)|0x006a:
376 case (0x10de<<16)|0x00da:
377 case (0x10de<<16)|0x00ea:
378 case (0x8086<<16)|0x2415:
379 case (0x8086<<16)|0x2425:
380 case (0x8086<<16)|0x2445:
381 case (0x8086<<16)|0x2485:
382 case (0x8086<<16)|0x24c5:
383 case (0x8086<<16)|0x24d5:
384 case (0x8086<<16)|0x25a6:
385 case (0x8086<<16)|0x266e:
386 case (0x8086<<16)|0x7195:
393 sethwp(Ctlr *ctlr, long off, void *ptr)
395 csr8w(ctlr, off+Cr, RR);
396 csr32w(ctlr, off+Bar, PCIWADDR(ptr));
397 csr8w(ctlr, off+Lvi, 0);
401 ac97reset(Audio *adev)
403 static Ctlr *cards = nil;
409 /* make a list of all ac97 cards if not already done */
412 while(p = ac97match(p)){
413 ctlr = xspanalloc(sizeof(Ctlr), 8, 0);
414 memset(ctlr, 0, sizeof(Ctlr));
421 /* pick a card from the list */
422 for(ctlr = cards; ctlr; ctlr = ctlr->next){
423 if(p = ctlr->pcidev){
432 if(p->vid == 0x1039 && p->did == 0x7012)
435 if(p->mem[0].size == 64){
436 ctlr->port = p->mem[0].bar & ~3;
437 ctlr->mixport = p->mem[1].bar & ~3;
438 } else if(p->mem[1].size == 64){
439 ctlr->port = p->mem[1].bar & ~3;
440 ctlr->mixport = p->mem[0].bar & ~3;
441 } else if(p->mem[0].size == 256){ /* sis7012 */
442 ctlr->port = p->mem[1].bar & ~3;
443 ctlr->mixport = p->mem[0].bar & ~3;
444 } else if(p->mem[1].size == 256){
445 ctlr->port = p->mem[0].bar & ~3;
446 ctlr->mixport = p->mem[1].bar & ~3;
452 print("#A%d: ac97 port 0x%04lux mixport 0x%04lux irq %d\n",
453 adev->ctlrno, ctlr->port, ctlr->mixport, irq);
458 ctlr->mic = xspanalloc(Bufsize, 8, 0);
459 ctlr->in = xspanalloc(Bufsize, 8, 0);
460 ctlr->out = xspanalloc(Bufsize, 8, 0);
462 for(i = 0; i < Ndesc; i++){
463 int size, off = i * (Bufsize/Ndesc);
466 size = (Bufsize/Ndesc);
468 size = (Bufsize/Ndesc) / 2;
470 ctlr->micdesc[i].addr = PCIWADDR(ctlr->mic + off);
471 ctlr->micdesc[i].size = Ioc | size;
472 ctlr->indesc[i].addr = PCIWADDR(ctlr->in + off);
473 ctlr->indesc[i].size = Ioc | size;
474 ctlr->outdesc[i].addr = PCIWADDR(ctlr->out + off);
475 ctlr->outdesc[i].size = Ioc | size;
478 ctlr->outavail = Bufsize - Bufsize/Ndesc;
479 ctlr->outp = ctlr->out;
481 ctl = csr32r(ctlr, Cnt);
482 ctl &= ~(EnaRESER | Aclso);
484 if((ctl & Accr) == 0){
485 print("#A%d: ac97 cold reset\n", adev->ctlrno);
488 print("#A%d: ac97 warm reset\n", adev->ctlrno);
492 csr32w(ctlr, Cnt, ctl);
493 for(i = 0; i < Maxbusywait; i++){
494 if((csr32r(ctlr, Cnt) & Acwr) == 0)
499 print("#A%d: ac97 gave up waiting Acwr to go down\n", adev->ctlrno);
501 for(i = 0; i < Maxbusywait; i++){
502 if((stat = csr32r(ctlr, Sta)) & (Pcr | Scr | S2cr))
507 print("#A%d: ac97 gave up waiting codecs become ready\n", adev->ctlrno);
509 print("#A%d: ac97 codecs ready:%s%s%s\n", adev->ctlrno,
510 (stat & Pcr) ? " sdin0" : "",
511 (stat & Scr) ? " sdin1" : "",
512 (stat & S2cr) ? " sdin2" : "");
514 print("#A%d: ac97 codecs resumed:%s%s%s\n", adev->ctlrno,
515 (stat & Pri) ? " sdin0" : "",
516 (stat & Sri) ? " sdin1" : "",
517 (stat & S2ri) ? " sdin2" : "");
519 sethwp(ctlr, In, ctlr->indesc);
520 sethwp(ctlr, Out, ctlr->outdesc);
521 sethwp(ctlr, Mic, ctlr->micdesc);
523 csr8w(ctlr, In+Cr, Ioce); /* | Lvbie | Feie */
524 csr8w(ctlr, Out+Cr, Ioce); /* | Lvbie | Feie */
525 csr8w(ctlr, Mic+Cr, Ioce); /* | Lvbie | Feie */
527 ac97mixreset(adev, ac97mixw, ac97mixr);
529 adev->write = ac97write;
530 adev->status = ac97status;
531 adev->buffered = ac97buffered;
533 intrenable(irq, ac97interrupt, adev, tbdf, adev->name);
541 addaudiocard("ac97", ac97reset);