]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/audioac97.c
pc/ether*: use 64-bit physical addresses and check pci membar types and sizes
[plan9front.git] / sys / src / 9 / pc / audioac97.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "../port/error.h"
8 #include "../port/audioif.h"
9
10 typedef struct Ring Ring;
11 typedef struct Hwdesc Hwdesc;
12 typedef struct Ctlr Ctlr;
13
14 enum {
15         Ioc     =       1<<31,
16         Bup     =       1<<30,
17 };
18
19 struct Hwdesc {
20         ulong addr;
21         ulong size;
22 };
23
24 enum {
25         Ndesc = 32,
26         Bufsize = 32768,        /* bytes, must be divisible by ndesc */
27         Blocksize = Bufsize/Ndesc,
28         Maxbusywait = 500000, /* microseconds, roughly */
29         BytesPerSample = 4,
30 };
31
32 struct Ring
33 {
34         Rendez r;
35
36         uchar   *buf;
37         ulong   nbuf;
38
39         ulong   ri;
40         ulong   wi;
41 };
42
43 struct Ctlr {
44         /* keep these first, they want to be 8-aligned */
45         Hwdesc indesc[Ndesc];
46         Hwdesc outdesc[Ndesc];
47         Hwdesc micdesc[Ndesc];
48
49         Lock;
50
51         ulong port;
52         ulong mixport;
53         uchar *mmreg;
54         uchar *mmmix;
55         int ismmio;
56
57         Ring inring, micring, outring;
58
59         int sis7012;
60
61         /* for probe */
62         Audio *adev;
63         Pcidev *pcidev;
64         Ctlr *next;
65 };
66
67 enum {
68         In = 0x00,
69         Out = 0x10,
70         Mic = 0x20,
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 */
90                 Ena16bit = 0x0<<22,
91                 Ena20bit = 0x1<<22,
92                 Ena2chan = 0x0<<20,
93                 Ena4chan = 0x1<<20,
94                 Enam6chan = 0x2<<20,
95                 EnaRESER = 0x3<<20,
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 */
104                 Cap6chan = 1<<21,
105                 Cap4chan = 1<<20,
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 */
123 };
124
125 static long
126 buffered(Ring *r)
127 {
128         ulong ri, wi;
129
130         ri = r->ri;
131         wi = r->wi;
132         if(wi >= ri)
133                 return wi - ri;
134         else
135                 return r->nbuf - (ri - wi);
136 }
137
138 static long
139 available(Ring *r)
140 {
141         long m;
142
143         m = (r->nbuf - BytesPerSample) - buffered(r);
144         if(m < 0)
145                 m = 0;
146         return m;
147 }
148
149 static long
150 readring(Ring *r, uchar *p, long n)
151 {
152         long n0, m;
153
154         n0 = n;
155         while(n > 0){
156                 if((m = buffered(r)) <= 0)
157                         break;
158                 if(m > n)
159                         m = n;
160                 if(p){
161                         if(r->ri + m > r->nbuf)
162                                 m = r->nbuf - r->ri;
163                         memmove(p, r->buf + r->ri, m);
164                         p += m;
165                 }
166                 r->ri = (r->ri + m) % r->nbuf;
167                 n -= m;
168         }
169         return n0 - n;
170 }
171
172 static long
173 writering(Ring *r, uchar *p, long n)
174 {
175         long n0, m;
176
177         n0 = n;
178         while(n > 0){
179                 if((m = available(r)) <= 0)
180                         break;
181                 if(m > n)
182                         m = n;
183                 if(p){
184                         if(r->wi + m > r->nbuf)
185                                 m = r->nbuf - r->wi;
186                         memmove(r->buf + r->wi, p, m);
187                         p += m;
188                 }
189                 r->wi = (r->wi + m) % r->nbuf;
190                 n -= m;
191         }
192         return n0 - n;
193 }
194
195 static uchar
196 csr8r(Ctlr *c, int r){
197         if(c->ismmio)
198                 return *(uchar*)(c->mmreg+r);           
199         return inb(c->port+r);
200 }
201
202 static ushort
203 csr16r(Ctlr *c, int r){
204         if(c->ismmio)
205                 return *(ushort*)(c->mmreg+r);          
206         return ins(c->port+r);
207 }
208
209 static ulong
210 csr32r(Ctlr *c, int r){
211         if(c->ismmio)
212                 return *(ulong*)(c->mmreg+r);           
213         return inl(c->port+r);
214 }
215
216 static void
217 csr8w(Ctlr *c, int r, uchar v){
218         if(c->ismmio)
219                 *(uchar*)(c->mmreg+r) = v;
220         else
221                 outb(c->port+r, (int)v);
222 }
223
224 static void
225 csr16w(Ctlr *c, int r, ushort v){
226         if(c->ismmio)
227                 *(ushort*)(c->mmreg+r) = v;
228         else
229                 outs(c->port+r, v);
230 }
231
232 static void
233 csr32w(Ctlr *c, int r, ulong v){
234         if(c->ismmio)
235                 *(ulong*)(c->mmreg+r) = v;
236         else
237                 outl(c->port+r, v);
238 }
239
240 /* audioac97mix */
241 extern void ac97mixreset(Audio *,
242         void (*wr)(Audio*,int,ushort), 
243         ushort (*rr)(Audio*,int));
244
245 static void
246 ac97waitcodec(Audio *adev)
247 {
248         Ctlr *ctlr;
249         int i;
250         ctlr = adev->ctlr;
251         for(i = 0; i <= Maxbusywait/10; i++){
252                 if((csr8r(ctlr, Cas) & Casp) == 0)
253                         return;
254                 microdelay(10);
255         }
256         print("#A%d: ac97 exhausted waiting codec access\n", adev->ctlrno);
257 }
258
259 static void
260 ac97mixw(Audio *adev, int port, ushort val)
261 {
262         Ctlr *ctlr;
263         ac97waitcodec(adev);
264         ctlr = adev->ctlr;
265         if(ctlr->ismmio)
266                 *(ushort*)(ctlr->mmmix+port) = val;
267         else
268                 outs(ctlr->mixport+port, val);
269 }
270
271 static ushort
272 ac97mixr(Audio *adev, int port)
273 {
274         Ctlr *ctlr;
275         ac97waitcodec(adev);
276         ctlr = adev->ctlr;
277         if(ctlr->ismmio)
278                 return *(ushort*)(ctlr->mmmix+port);
279         return ins(ctlr->mixport+port);
280 }
281
282 static void
283 ac97interrupt(Ureg *, void *arg)
284 {
285         Audio *adev;
286         Ctlr *ctlr;
287         ulong stat;
288
289         adev = arg;
290         ctlr = adev->ctlr;
291         stat = csr32r(ctlr, Sta);
292         stat &= S2ri | Sri | Pri | Mint | Point | Piint | Moint | Miint | Gsci;
293         if(stat & (Point|Piint|Mint)){
294                 ilock(ctlr);
295                 if(stat & Point){
296                         ctlr->outring.ri = csr8r(ctlr, Out + Civ) * Blocksize;
297                         wakeup(&ctlr->outring.r);
298
299                         if(ctlr->sis7012)
300                                 csr16w(ctlr, Out + Picb, csr16r(ctlr, Out + Picb) & ~Dch);
301                         else
302                                 csr16w(ctlr, Out + Sr, csr16r(ctlr, Out + Sr) & ~Dch);
303                         stat &= ~Point;
304                 }
305                 if(stat & Piint){
306                         ctlr->inring.wi = csr8r(ctlr, In + Civ) * Blocksize;
307                         wakeup(&ctlr->inring.r);
308
309                         if(ctlr->sis7012)
310                                 csr16w(ctlr, In + Picb, csr16r(ctlr, In + Picb) & ~Dch);
311                         else
312                                 csr16w(ctlr, In + Sr, csr16r(ctlr, In + Sr) & ~Dch);
313                         stat &= ~Piint;
314                 }
315                 if(stat & Mint){
316                         ctlr->micring.wi = csr8r(ctlr, Mic + Civ) * Blocksize;
317                         wakeup(&ctlr->micring.r);
318
319                         if(ctlr->sis7012)
320                                 csr16w(ctlr, Mic + Picb, csr16r(ctlr, Mic + Picb) & ~Dch);
321                         else
322                                 csr16w(ctlr, Mic + Sr, csr16r(ctlr, Mic + Sr) & ~Dch);
323                         stat &= ~Mint;
324                 }
325                 iunlock(ctlr);
326         }
327         if(stat) /* have seen 0x400, which is sdin0 resume */
328                 iprint("#A%d: ac97 unhandled interrupt(s): stat 0x%lux\n",
329                         adev->ctlrno, stat);
330 }
331
332 static long
333 ac97buffered(Audio *adev)
334 {
335         Ctlr *ctlr = adev->ctlr;
336         return buffered(&ctlr->outring);
337 }
338
339 static long
340 ac97status(Audio *adev, void *a, long n, vlong)
341 {
342         Ctlr *ctlr = adev->ctlr;
343         return snprint((char*)a, n, "bufsize %6d buffered %6ld\n",
344                 Blocksize, buffered(&ctlr->outring));
345 }
346
347 static int
348 inavail(void *arg)
349 {
350         Ring *r = arg;
351         return buffered(r) > 0;
352 }
353
354 static int
355 outavail(void *arg)
356 {
357         Ring *r = arg;
358         return available(r) > 0;
359 }
360
361 static int
362 outrate(void *arg)
363 {
364         Ctlr *ctlr = arg;
365         int delay = ctlr->adev->delay*BytesPerSample;
366         return (delay <= 0) || (buffered(&ctlr->outring) <= delay);
367 }
368
369 static long
370 ac97read(Audio *adev, void *vp, long n, vlong)
371 {
372         uchar *p, *e;
373         Ctlr *ctlr;
374         Ring *ring;
375         ulong oi, ni;
376
377         p = vp;
378         e = p + n;
379         ctlr = adev->ctlr;
380         ring = &ctlr->inring;
381         while(p < e) {
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);
387                         continue;
388                 }
389                 ni = ring->ri / Blocksize;
390                 while(oi != ni){
391                         csr8w(ctlr, In + Lvi, (oi - 1) % Ndesc);
392                         csr8w(ctlr, In + Cr, Ioce | Rpbm);
393                         oi = (oi + 1) % Ndesc;
394                 }
395                 p += n;
396         }
397         return p - (uchar*)vp;
398 }
399
400 static long
401 ac97write(Audio *adev, void *vp, long n, vlong)
402 {
403         uchar *p, *e;
404         Ctlr *ctlr;
405         Ring *ring;
406         ulong oi, ni;
407
408         p = vp;
409         e = p + n;
410         ctlr = adev->ctlr;
411         ring = &ctlr->outring;
412         while(p < e) {
413                 oi = ring->wi / Blocksize;
414                 if((n = writering(ring, p, e - p)) <= 0){
415                         sleep(&ring->r, outavail, ring);
416                         continue;
417                 }
418                 ni = ring->wi / Blocksize;
419                 while(oi != ni){
420                         csr8w(ctlr, Out+Lvi, oi);
421                         csr8w(ctlr, Out+Cr, Ioce | Rpbm);
422                         oi = (oi + 1) % Ndesc;
423                 }
424                 p += n;
425         }
426         while(outrate(ctlr) == 0)
427                 sleep(&ring->r, outrate, ctlr);
428         return p - (uchar*)vp;
429 }
430
431 static void
432 ac97close(Audio *adev, int mode)
433 {
434         Ctlr *ctlr;
435         Ring *ring;
436
437         if(mode == OREAD)
438                 return;
439
440         ctlr = adev->ctlr;
441         ring = &ctlr->outring;
442         while(ring->wi % Blocksize)
443                 if(writering(ring, (uchar*)"", 1) <= 0)
444                         break;
445 }
446
447 static Pcidev*
448 ac97match(Pcidev *p)
449 {
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:
469                         return p;
470                 }
471         return nil;
472 }
473
474 static void
475 sethwp(Ctlr *ctlr, long off, void *ptr)
476 {
477         csr8w(ctlr, off+Cr, RR);
478         csr32w(ctlr, off+Bar, PCIWADDR(ptr));
479         csr8w(ctlr, off+Lvi, 0);
480 }
481
482 static int
483 ac97reset(Audio *adev)
484 {
485         static Ctlr *cards = nil;
486         Pcidev *p;
487         int i, irq, tbdf;
488         Ctlr *ctlr;
489         ulong ctl, stat = 0;
490
491         /* make a list of all ac97 cards if not already done */
492         if(cards == nil){
493                 p = nil;
494                 while(p = ac97match(p)){
495                         ctlr = xspanalloc(sizeof(Ctlr), 8, 0);
496                         memset(ctlr, 0, sizeof(Ctlr));
497                         ctlr->pcidev = p;
498                         ctlr->next = cards;
499                         cards = ctlr;
500                 }
501         }
502
503         /* pick a card from the list */
504         for(ctlr = cards; ctlr; ctlr = ctlr->next){
505                 if(p = ctlr->pcidev){
506                         ctlr->pcidev = nil;
507                         goto Found;
508                 }
509         }
510         return -1;
511
512 Found:
513         adev->ctlr = ctlr;
514         ctlr->adev = adev;
515
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);
524                         return -1;
525                 }
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);
530                         return -1;
531                 }
532                 ctlr->ismmio = 1;
533         }else{
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);
536                         return -1;
537                 }
538
539                 if(p->vid == 0x1039 && p->did == 0x7012){
540                         ctlr->sis7012 = 1;      /* i/o bars swapped? */
541                 }
542
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);
546                         return -1;
547                 }
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);
551                         iofree(ctlr->port);
552                         return -1;
553                 }
554         }
555
556         irq = p->intl;
557         tbdf = p->tbdf;
558
559         print("#A%d: ac97 port 0x%04lux mixport 0x%04lux irq %d\n",
560                 adev->ctlrno, ctlr->port, ctlr->mixport, irq);
561
562         pcisetbme(p);
563         pcisetioe(p);
564
565         ctlr->micring.buf = xspanalloc(Bufsize, 8, 0);
566         ctlr->micring.nbuf = Bufsize;
567         ctlr->micring.ri = 0;
568         ctlr->micring.wi = 0;
569
570         ctlr->inring.buf = xspanalloc(Bufsize, 8, 0);
571         ctlr->inring.nbuf = Bufsize;
572         ctlr->inring.ri = 0;
573         ctlr->inring.wi = 0;
574
575         ctlr->outring.buf = xspanalloc(Bufsize, 8, 0);
576         ctlr->outring.nbuf = Bufsize;
577         ctlr->outring.ri = 0;
578         ctlr->outring.wi = 0;
579
580         for(i = 0; i < Ndesc; i++){
581                 int size, off = i * Blocksize;
582                 
583                 if(ctlr->sis7012)
584                         size = Blocksize;
585                 else
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;
593         }
594
595         ctl = csr32r(ctlr, Cnt);
596         ctl &= ~(EnaRESER | Aclso);
597
598         if((ctl & Accr) == 0){
599                 print("#A%d: ac97 cold reset\n", adev->ctlrno);
600                 ctl |= Accr;
601         }else{
602                 print("#A%d: ac97 warm reset\n", adev->ctlrno);
603                 ctl |= Acwr;
604         }
605
606         csr32w(ctlr, Cnt, ctl);
607         for(i = 0; i < Maxbusywait; i++){
608                 if((csr32r(ctlr, Cnt) & Acwr) == 0)
609                         break;
610                 microdelay(1);
611         }
612         if(i == Maxbusywait)
613                 print("#A%d: ac97 gave up waiting Acwr to go down\n", adev->ctlrno);
614
615         for(i = 0; i < Maxbusywait; i++){
616                 if((stat = csr32r(ctlr, Sta)) & (Pcr | Scr | S2cr))
617                         break;
618                 microdelay(1);
619         }
620         if(i == Maxbusywait)
621                 print("#A%d: ac97 gave up waiting codecs become ready\n", adev->ctlrno);
622
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" : "");
627
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" : "");
632
633         sethwp(ctlr, In, ctlr->indesc);
634         sethwp(ctlr, Out, ctlr->outdesc);
635         sethwp(ctlr, Mic, ctlr->micdesc);
636
637         csr8w(ctlr, In+Cr, Ioce);       /*  | Lvbie | Feie */
638         csr8w(ctlr, Out+Cr, Ioce);      /*  | Lvbie | Feie */
639         csr8w(ctlr, Mic+Cr, Ioce);      /*  | Lvbie | Feie */
640
641         ac97mixreset(adev, ac97mixw, ac97mixr);
642
643         adev->read = ac97read;
644         adev->write = ac97write;
645         adev->close = ac97close;
646         adev->buffered = ac97buffered;
647         adev->status = ac97status;
648
649         intrenable(irq, ac97interrupt, adev, tbdf, adev->name);
650
651         return 0;
652 }
653
654 void
655 audioac97link(void)
656 {
657         addaudiocard("ac97", ac97reset);
658 }