]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/audiohda.c
audiohda: make it work with sb600 onboard sound
[plan9front.git] / sys / src / 9 / pc / audiohda.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 Codec Codec;
11 typedef struct Ctlr Ctlr;
12 typedef struct Bld Bld;
13 typedef struct Ring Ring;
14 typedef struct Id Id;
15 typedef struct Widget Widget;
16 typedef struct Codec Codec;
17 typedef struct Fungroup Fungroup;
18 typedef struct Pinprop Pinprop;
19
20 enum {
21         Gcap = 0x00,
22         Gctl = 0x08,
23                 Rst = 1,
24                 Flush = 2,
25                 Acc = 1<<8,
26         Wakeen = 0x0c,
27         Statests = 0x0e,
28                 Sdiwake = 1 | 2 | 4,
29         Intctl = 0x20,
30                 Gie = 1<<31,
31                 Cie = 1<<30,
32         Intsts = 0x24,
33                 Gis = 1<<31,
34                 Cis = 1<<30,
35         Walclk = 0x30,
36         Corblbase = 0x40,
37         Corbubase = 0x44,
38         Corbwp = 0x48,
39         Corbrp = 0x4a,
40                 Corbptrrst = 1<<15,
41         Corbctl = 0x4c,
42                 Corbdma = 2,
43                 Corbint = 1,
44         Corbsts = 0x4d,
45                 Cmei = 1,
46         Corbsz = 0x4e,
47         Rirblbase = 0x50,
48         Rirbubase = 0x54,
49         Rirbwp = 0x58,
50                 Rirbptrrst = 1<<15,
51         Rintcnt = 0x5a,
52         Rirbctl = 0x5c,
53                 Rirbover = 4,
54                 Rirbdma = 2,
55                 Rirbint = 1,
56         Rirbsts = 0x5d,
57                 Rirbrover = 4,
58                 Rirbrint = 1,
59         Rirbsz = 0x5e,
60         Immcmd = 0x60,
61         Immresp = 0x64,
62         Immstat = 0x68,
63         Dplbase = 0x70,
64         Dpubase = 0x74,
65         /* Warning: Sdctl is 24bit register */
66         Sdctl0 = 0x80,
67                 Srst = 1<<0,
68                 Srun = 1<<1,
69                 Scie = 1<<2,
70                 Seie = 1<<3,
71                 Sdie = 1<<4,
72                 Stagbit = 20,
73         Sdsts = 0x03,
74                 Scompl = 1<<2,
75                 Sfifoerr = 1<<3,
76                 Sdescerr = 1<<4,
77                 Sfifordy = 1<<5,
78         Sdlpib = 0x04,
79         Sdcbl =  0x08,
80         Sdlvi =  0x0c,
81         Sdfifow = 0x0e,
82         Sdfifos = 0x10,
83         Sdfmt = 0x12,
84                 Fmtmono = 0,
85                 Fmtstereo = 1,
86                 Fmtsampw = 1<<4,
87                 Fmtsampb = 0<<4,
88                 Fmtdiv1 = 0<<8,
89                 Fmtmul1 = 0<<11,
90                 Fmtbase441 = 1<<14,
91                 Fmtbase48 = 0<<14,
92         Sdbdplo = 0x18,
93         Sdbdphi = 0x1c,
94 };
95
96 enum {
97         Bufsize = 64 * 1024 * 4,
98         Nblocks = 256,
99         Blocksize = Bufsize / Nblocks,
100         BytesPerSample = 4,
101
102         Maxrirbwait = 1000, /* microseconds */
103         Maxwaitup = 500, /* microseconds */
104         Codecdelay = 1000, /* microseconds */
105 };
106
107 enum {
108         /* 12-bit cmd + 8-bit payload */
109         Getparm = 0xf00,
110                 Vendorid = 0x00,
111                 Revid = 0x02,
112                 Subnodecnt = 0x04,
113                 Fungrtype = 0x05,
114                         Graudio = 0x01,
115                         Grmodem = 0x02,
116                 Fungrcap = 0x08,
117                 Widgetcap = 0x09,
118                         Waout = 0,
119                         Wain = 1,
120                         Wamix = 2,
121                         Wasel = 3,
122                         Wpin = 4,
123                         Wpower = 5,
124                         Wknob = 6,
125                         Wbeep = 7,
126                         Winampcap = 0x0002,
127                         Woutampcap = 0x0004,
128                         Wampovrcap = 0x0008,
129                         Wfmtovrcap = 0x0010,
130                         Wstripecap = 0x0020,
131                         Wproccap = 0x0040,
132                         Wunsolcap = 0x0080,
133                         Wconncap = 0x0100,
134                         Wdigicap = 0x0200,
135                         Wpwrcap = 0x0400,
136                         Wlrcap = 0x0800,
137                         Wcpcap = 0x1000,                         
138                 Streamrate = 0x0a,
139                 Streamfmt = 0x0b,
140                 Pincap = 0x0c,
141                         Psense = 1<<0,
142                         Ptrigreq = 1<<1,
143                         Pdetect = 1<<2,
144                         Pheadphone = 1<<3,
145                         Pout = 1<<4,
146                         Pin = 1<<5,
147                         Pbalanced = 1<<6,
148                         Phdmi = 1<<7,
149                 Inampcap = 0x0d,
150                 Outampcap = 0x12,
151                 Connlistlen = 0x0e,
152                 Powerstates = 0x0f,
153                 Processcap = 0x10,
154                 Gpiocount = 0x11,
155                 Knobcap = 0x13,
156         Getconn = 0xf01,
157         Setconn = 0x701,
158         Getconnlist = 0xf02,
159         Getstate = 0xf03,
160         Setstate = 0x703,
161         Getstream = 0xf06,
162         Setstream = 0x706,
163         Getpinctl = 0xf07,
164         Setpinctl = 0x707,
165                 Pinctlin = 1<<5,
166                 Pinctlout = 1<<6,
167         Getunsolresp = 0xf08,
168         Setunsolresp = 0x708,
169         Getpinsense = 0xf09,
170         Exepinsense = 0x709,
171         Getgpi = 0xf10,
172         Setgpi = 0x710,
173         Getbeep = 0xf0a,
174         Setbeep = 0x70a,
175         Getknob = 0xf0f,
176         Setknob = 0x70f,
177         Getdefault = 0xf1c,
178         Funreset = 0x7ff,
179         Getchancnt = 0xf2d,
180         Setchancnt = 0x72d,
181         
182         /* 4-bit cmd + 16-bit payload */
183         Getcoef = 0xd,
184         Setcoef = 0x5,
185         Getproccoef = 0xc,
186         Setproccoef = 0x4,
187         Getamp = 0xb,
188         Setamp = 0x3,
189                 Asetout = 1<<15,
190                 Asetin = 1<<14,
191                 Asetleft = 1<<13,
192                 Asetright = 1<<12,
193                 Asetmute = 1<<7,
194                 Asetidx = 8,
195                 Agetin = 0<<15,
196                 Agetout = 1<<15,
197                 Agetleft = 1<<13,
198                 Agetright = 1<<15,
199                 Agetidx = 0,
200                 Again = 0,
201                 Againmask = 0x7f,
202         Getconvfmt = 0xa,
203         Setconvfmt = 0x2,
204 };
205
206 enum {
207         Maxcodecs = 16,
208         Maxwidgets = 256,
209 };
210
211 struct Ring {
212         Rendez r;
213
214         uchar   *buf;
215         ulong   nbuf;
216
217         ulong   ri;
218         ulong   wi;
219 };
220
221 struct Id {
222         Ctlr *ctlr;
223         uint codec, nid;
224 };
225
226 struct Widget {
227         Id id;
228         Fungroup *fg;
229         uint cap, type;
230         uint nlist;
231         Widget **list;
232         union {
233                 struct {
234                         uint pin, pincap;
235                 };
236                 struct {
237                         uint convrate, convfmt;
238                 };
239         };
240         Widget *next;
241         Widget *from;
242 };
243
244 struct Fungroup {
245         Id id;
246         Codec *codec;
247         uint type;
248         Widget *first;
249         Widget *mixer;
250         Widget *src, *dst;
251         Fungroup *next;
252 };
253
254 struct Codec {
255         Id id;
256         uint vid, rid;
257         Widget *widgets[Maxwidgets];
258         Fungroup *fgroup;
259 };
260
261 /* hardware structures */
262
263 struct Bld {
264         ulong addrlo;
265         ulong addrhi;
266         ulong len;
267         ulong flags;
268 };
269
270 struct Ctlr {
271         Ctlr *next;
272         uint no;
273
274         Lock;                   /* interrupt lock */
275         QLock;                  /* command lock */
276
277         Audio *adev;
278         Pcidev *pcidev;
279         
280         uchar *mem;
281         ulong size;
282         
283         Queue *q;
284         ulong *corb;
285         ulong corbsize;
286         ulong *rirb;
287         ulong rirbsize;
288         
289         uint sdctl;
290         uint sdintr;
291         uint sdnum;
292
293         Bld *blds;
294
295         Ring ring;
296
297         uint iss, oss, bss;
298
299         uint codecmask; 
300         Codec *codec[Maxcodecs];
301
302         Widget *amp, *src;
303         uint pin;
304         uint cad;
305
306         int active;
307         uint afmt, atag;
308 };
309
310 #define csr32(c, r)     (*(ulong *)&(c)->mem[r])
311 #define csr16(c, r)     (*(ushort *)&(c)->mem[r])
312 #define csr8(c, r)      (*(uchar *)&(c)->mem[r])
313
314 static char *pinport[] = {
315         "jack",
316         "nothing",
317         "fix",
318         "jack+fix",
319 };
320
321 static char *pinfunc[] = {
322         "lineout",
323         "speaker",
324         "hpout",
325         "cd",
326         "spdifout",
327         "digiout",
328         "modemline",
329         "modemhandset",
330         "linein",
331         "aux",
332         "micin",
333         "telephony",
334         "spdifin",
335         "digiin",
336         "resvd",
337         "other",
338 };
339
340
341 static char *pincol[] = {
342         "?",
343         "black",
344         "grey",
345         "blue",
346         "green",
347         "red",
348         "orange",
349         "yellow",
350         "purple",
351         "pink",
352         "resvd",
353         "resvd",
354         "resvd",
355         "resvd",
356         "white",
357         "other",
358 };
359
360 static char *pinloc[] = {
361         "N/A",
362         "rear",
363         "front",
364         "left",
365         "right",
366         "top",
367         "bottom",
368         "special",
369         "special",
370         "special",
371         "resvd",
372         "resvd",
373         "resvd",
374         "resvd",
375         "resvd",
376         "resvd",
377 };
378
379 static char *pinloc2[] = {
380         "ext",
381         "int",
382         "sep",
383         "other",
384 };
385
386 Ctlr *lastcard;
387
388 static int
389 waitup8(Ctlr *ctlr, int reg, uchar mask, uchar set)
390 {
391         int i;
392         for(i=0; i<Maxwaitup; i++){
393                 if((csr8(ctlr, reg) & mask) == set)
394                         return 0;
395                 microdelay(1);
396         }
397         print("#A%d: waitup timeout for reg=%x, mask=%x, set=%x\n",
398                 ctlr->no, reg, mask, set);
399         return -1;
400 }
401
402 static int
403 waitup16(Ctlr *ctlr, int reg, ushort mask, ushort set)
404 {
405         int i;
406         for(i=0; i<Maxwaitup; i++){
407                 if((csr16(ctlr, reg) & mask) == set)
408                         return 0;
409                 microdelay(1);
410         }
411         print("#A%d: waitup timeout for reg=%x, mask=%x, set=%x\n",
412                 ctlr->no, reg, mask, set);
413         return -1;
414 }
415
416 static int
417 waitup32(Ctlr *ctlr, int reg, uint mask, uint set)
418 {
419         int i;
420         for(i=0; i<Maxwaitup; i++){
421                 if((csr32(ctlr, reg) & mask) == set)
422                         return 0;
423                 microdelay(1);
424         }
425         print("#A%d: waitup timeout for reg=%x, mask=%x, set=%x\n",
426                 ctlr->no, reg, mask, set);
427         return -1;
428 }
429
430 static int
431 hdacmd(Ctlr *ctlr, uint request, uint reply[2])
432 {
433         uint rp, wp;
434         uint re;
435         int wait;
436         
437         re = csr16(ctlr, Rirbwp);
438         rp = csr16(ctlr, Corbrp);
439         wp = (csr16(ctlr, Corbwp) + 1) % ctlr->corbsize;
440         if(rp == wp){
441                 print("#A%d: corb full\n", ctlr->no);
442                 return -1;
443         }
444         ctlr->corb[wp] = request;
445         coherence();
446         csr16(ctlr, Corbwp) = wp;
447         for(wait=0; wait < Maxrirbwait; wait++){
448                 if(csr16(ctlr, Rirbwp) != re){
449                         re = (re + 1) % ctlr->rirbsize;
450                         memmove(reply, &ctlr->rirb[re*2], 8);
451                         return 1;
452                 }
453                 microdelay(1);
454         }
455         return 0;
456 }
457
458 static int
459 cmderr(Id id, uint verb, uint par, uint *ret)
460 {
461         uint q, w[2];
462         q = (id.codec << 28) | (id.nid << 20);
463         if((verb & 0x700) == 0x700)
464                 q |= (verb << 8) | par;
465         else
466                 q |= (verb << 16) | par;
467         if(hdacmd(id.ctlr, q, w) != 1)
468                 return -1;
469         if(w[1] != id.codec)
470                 return -1;
471         *ret = w[0];
472         return 0;
473 }
474
475 static uint
476 cmd(Id id, uint verb, uint par)
477 {
478         uint w[2];
479         if(cmderr(id, verb, par, w) == -1)
480                 return ~0;
481         return w[0];
482 }
483
484 static Id
485 newnid(Id id, uint nid)
486 {
487         id.nid = nid;
488         return id;
489 }
490
491 static uint
492 getoutamprange(Widget *w)
493 {
494         uint r;
495         r = cmd(w->id, Getparm, Outampcap);
496         return (r >> 8) & 0x7f;
497 }
498
499 static void
500 getoutamp(Widget *w, int vol[2])
501 {
502         vol[0] = vol[1] = 0;
503         if((w->cap & Woutampcap) == 0)
504                 return;
505         vol[0] = cmd(w->id, Getamp, Agetout | Agetleft) & Againmask;
506         vol[1] = cmd(w->id, Getamp, Agetout | Agetright) & Againmask;
507 }
508
509 /* vol is 0...range or nil for 0dB; mute is 0/1 */
510 static void
511 setoutamp(Widget *w, int mute, int *vol)
512 {
513         uint q, r, i;
514         uint zerodb;
515
516         if((w->cap & Woutampcap) == 0)
517                 return;
518
519         r = cmd(w->id, Getparm, Outampcap);
520         zerodb = r & 0x7f;
521         
522         for(i=0; i<2; i++){
523                 q = Asetout | (i == 0 ? Asetleft : Asetright);
524                 if(mute)
525                         q |= Asetmute;
526                 else if(vol == nil)
527                         q |= zerodb << Again;
528                 else
529                         q |= vol[i] << Again;
530                 cmd(w->id, Setamp, q);
531         }
532 }
533
534 /* vol is 0...range or nil for 0dB; mute is 0/1; in is widget or nil for all */
535 static void
536 setinamp(Widget *w, Widget *in, int mute, int *vol)
537 {
538         uint q, r, i, j;
539         uint zerodb;
540
541         if((w->cap & Winampcap) == 0)
542                 return;
543
544         r = cmd(w->id, Getparm, Inampcap);
545         zerodb = r & 0x7f;
546         
547         for(i=0; i<2; i++){
548                 q = Asetin | (i == 0 ? Asetleft : Asetright);
549                 if(mute)
550                         q |= Asetmute;
551                 else if(vol == nil)
552                         q |= zerodb << Again;
553                 else
554                         q |= vol[i] << Again;
555                 for(j=0; j<w->nlist; j++){
556                         if(in == nil || w->list[j] == in)
557                                 cmd(w->id, Setamp, q | (j << Asetidx));
558                 }
559         }
560 }
561
562 static Widget *
563 findpath(Widget *src)
564 {
565         Widget *q[Maxwidgets];
566         uint l, r, i;
567         Widget *w, *v;
568         
569         l = r = 0;
570         q[r++] = src;
571         for(w=src->fg->first; w; w=w->next)
572                 w->from = nil;
573         src->from = src;
574
575         while(l < r){
576                 w = q[l++];
577                 if(w->type == Waout)
578                         break;
579                 for(i=0; i<w->nlist; i++){
580                         v = w->list[i];
581                         if(v == nil || v->from)
582                                 continue;
583                         v->from = w;
584                         q[r++] = v;
585                 }
586         }
587         if(w->type != Waout)
588                 return nil;
589         return w;
590 }
591
592 static void
593 connectpath(Widget *src, Widget *dst, uint stream)
594 {
595         Widget *w, *v;
596         uint i;
597
598         for(w=src->fg->first; w != nil; w=w->next){
599                 setoutamp(w, 1, nil);
600                 setinamp(w, nil, 1, nil);
601                 cmd(w->id, Setstream, 0);
602         }
603         for(w=dst; w != src; w=v){
604                 v = w->from;
605                 setoutamp(w, 0, nil);
606                 setinamp(v, w, 0, nil);
607                 if(v->type == Waout || v->type == Wamix)
608                         continue;
609                 if(v->nlist == 1)
610                         continue;
611                 for(i=0; i < v->nlist && v->list[i] != w; i++)
612                         ;
613                 cmd(v->id, Setconn, i);
614         }
615         setoutamp(src, 0, nil);
616         cmd(src->id, Setpinctl, Pinctlout);
617         cmd(dst->id, Setstream, (stream << 4) | 0);
618         cmd(dst->id, Setconvfmt, (1 << 14) | (1 << 4) | 1);
619         cmd(dst->id, Setchancnt, 1);
620 }
621
622 static void
623 addconn(Widget *w, uint nid)
624 {
625         Widget *src;
626
627         if(nid >= Maxwidgets)
628                 return;
629         if((src = w->fg->codec->widgets[nid]) == nil)
630                 return;
631         for(nid=0; nid<w->nlist; nid++)
632                 if(w->list[nid] == src)
633                         return;
634         if((w->nlist % 16) == 0){
635                 void *p;
636
637                 if((p = realloc(w->list, sizeof(Widget*) * (w->nlist+16))) == nil){
638                         print("hda: no memory for Widgetlist\n");
639                         return;
640                 }
641                 w->list = p;
642         }
643         w->list[w->nlist++] = src;
644 }
645
646 static void
647 enumconns(Widget *w)
648 {
649         uint r, f, b, m, i, n, x, y;
650
651         r = cmd(w->id, Getparm, Connlistlen);
652         n = r & 0x7f;
653         b = (r & 0x80) ? 16 : 8;
654         m = (1<<b)-1;
655         f = (32/b)-1;
656         x = 0;
657         for(i=0; i<n; i++){
658                 if(i & f)
659                         r >>= b;
660                 else
661                         r = cmd(w->id, Getconnlist, i);
662                 y = r & (m>>1);
663                 if(i && ((r & m) != y))
664                         while(++x < y)
665                                 addconn(w, x);
666                 addconn(w, y);
667                 x = y;
668         }
669 }
670
671 static void
672 enumwidget(Widget *w)
673 {
674         w->cap = cmd(w->id, Getparm, Widgetcap);
675         w->type = (w->cap >> 20) & 0x7;
676
677         enumconns(w);
678         
679         switch(w->type){
680         case Wpin:
681                 w->pin = cmd(w->id, Getdefault, 0);
682                 w->pincap = cmd(w->id, Getparm, Pincap);
683                 break;
684         }
685 }
686
687 static Fungroup *
688 enumfungroup(Codec *codec, Id id)
689 {
690         Fungroup *fg;
691         Widget *w, **tail;
692         uint i, r, n, base;
693
694         r = cmd(id, Getparm, Fungrtype) & 0x7f;
695         if(r != Graudio)
696                 return nil;
697
698         fg = mallocz(sizeof *fg, 1);
699         if(fg == nil){
700 Nomem:
701                 print("hda: enumfungroup: out of memory\n");
702                 return nil;
703         }
704         fg->codec = codec;
705         fg->id = id;
706         fg->type = r;
707
708         r = cmd(id, Getparm, Subnodecnt);
709         n = r & 0xff;
710         base = (r >> 16) & 0xff;
711         
712         if(base + n > Maxwidgets){
713                 free(fg);
714                 return nil;
715         }
716
717         tail = &fg->first;
718         for(i=0; i<n; i++){
719                 w = mallocz(sizeof(Widget), 1);
720                 if(w == nil){
721                         while(w = fg->first){
722                                 fg->first = w->next;
723                                 codec->widgets[w->id.nid] = nil;
724                                 free(w);
725                         }
726                         free(fg);
727                         goto Nomem;
728                 }
729                 w->id = newnid(id, base + i);
730                 w->fg = fg;
731                 codec->widgets[base + i] = w;
732                 *tail = w;
733                 tail = &w->next;
734         }
735
736         for(i=0; i<n; i++)
737                 enumwidget(codec->widgets[base + i]);
738
739         return fg;
740 }
741
742
743 static int
744 enumcodec(Codec *codec, Id id)
745 {
746         Fungroup *fg;
747         uint i, r, n, base;
748         uint vid, rid;
749         
750         if(cmderr(id, Getparm, Vendorid, &vid) < 0)
751                 return -1;
752         if(cmderr(id, Getparm, Revid, &rid) < 0)
753                 return -1;
754         
755         codec->id = id;
756         codec->vid = vid;
757         codec->rid = rid;
758
759         print("#A%d: codec #%d, vendor %08x, rev %08x\n",
760                 id.ctlr->no, codec->id.codec, codec->vid, codec->rid);
761
762         r = cmd(id, Getparm, Subnodecnt);
763         n = r & 0xff;
764         base = (r >> 16) & 0xff;
765
766         for(i=0; i<n; i++){
767                 fg = enumfungroup(codec, newnid(id, base + i));
768                 if(fg == nil)
769                         continue;
770                 fg->next = codec->fgroup;
771                 codec->fgroup = fg;
772         }
773         if(codec->fgroup == nil)
774                 return -1;
775         return 0;
776 }
777
778 static int
779 enumdev(Ctlr *ctlr)
780 {
781         Codec *codec;
782         int ret;
783         Id id;
784         int i;
785
786         ret = -1;
787         id.ctlr = ctlr;
788         id.nid = 0;
789         for(i=0; i<Maxcodecs; i++){
790                 if(((1<<i) & ctlr->codecmask) == 0)
791                         continue;
792                 codec = mallocz(sizeof(Codec), 1);
793                 if(codec == nil){
794                         print("hda: no memory for Codec\n");
795                         break;
796                 }
797                 id.codec = i;
798                 ctlr->codec[i] = codec;
799                 if(enumcodec(codec, id) < 0){
800                         ctlr->codec[i] = nil;
801                         free(codec);
802                         continue;
803                 }
804                 ret++;
805         }
806         return ret;
807 }
808
809 static int
810 connectpin(Ctlr *ctlr, uint pin, uint cad)
811 {
812         Widget *src, *dst;
813
814         if(cad >= Maxcodecs || pin >= Maxwidgets || ctlr->codec[cad] == nil)
815                 return -1;
816         src = ctlr->codec[cad]->widgets[pin];
817         if(src == nil)
818                 return -1;
819         if(src->type != Wpin)
820                 return -1;
821         if((src->pincap & Pout) == 0)
822                 return -1;
823         dst = findpath(src);
824         if(!dst)
825                 return -1;
826         connectpath(src, dst, ctlr->atag);
827         ctlr->amp = dst;
828         ctlr->src = src;
829         ctlr->pin = pin;
830         ctlr->cad = cad;
831         return 0;
832 }
833
834 static int
835 bestpin(Ctlr *ctlr, int *pcad)
836 {
837         Fungroup *fg;
838         Widget *w;
839         int best, pin, score;
840         uint r;
841         int i;
842
843         pin = -1;
844         best = -1;
845         for(i=0; i<Maxcodecs; i++){
846                 if(ctlr->codec[i] == nil)
847                         continue;
848                 for(fg=ctlr->codec[i]->fgroup; fg; fg=fg->next){
849                         for(w=fg->first; w; w=w->next){
850                                 if(w->type != Wpin)
851                                         continue;
852                                 if((w->pincap & Pout) == 0)
853                                         continue;
854                                 score = 0;
855                                 r = w->pin;
856                                 if(((r >> 12) & 0xf) == 4) /* green */
857                                         score |= 32;
858                                 if(((r >> 24) & 0xf) == 1) /* rear */
859                                         score |= 16;
860                                 if(((r >> 28) & 0x3) == 0) /* ext */
861                                         score |= 8;
862                                 if(((r >> 20) & 0xf) == 2) /* hpout */
863                                         score |= 4;
864                                 if(((r >> 20) & 0xf) == 0) /* lineout */
865                                         score |= 4;
866                                 if(score >= best){
867                                         best = score;
868                                         pin = w->id.nid;
869                                         *pcad = i;
870                                 }
871                         }
872                 }
873         }
874         return pin;
875 }
876
877 static long
878 buffered(Ring *r)
879 {
880         ulong ri, wi;
881
882         ri = r->ri;
883         wi = r->wi;
884         if(wi >= ri)
885                 return wi - ri;
886         else
887                 return r->nbuf - (ri - wi);
888 }
889
890 static long
891 available(Ring *r)
892 {
893         long m;
894
895         m = (r->nbuf - BytesPerSample) - buffered(r);
896         if(m < 0)
897                 m = 0;
898         return m;
899 }
900
901 static long
902 writering(Ring *r, uchar *p, long n)
903 {
904         long n0, m;
905
906         n0 = n;
907         while(n > 0){
908                 if((m = available(r)) <= 0)
909                         break;
910                 if(m > n)
911                         m = n;
912                 if(p){
913                         if(r->wi + m > r->nbuf)
914                                 m = r->nbuf - r->wi;
915                         memmove(r->buf + r->wi, p, m);
916                         p += m;
917                 }
918                 r->wi = (r->wi + m) % r->nbuf;
919                 n -= m;
920         }
921         return n0 - n;
922 }
923
924 static int
925 streamalloc(Ctlr *ctlr)
926 {
927         Ring *r;
928         int i;
929
930         r = &ctlr->ring;
931         memset(r, 0, sizeof(*r));
932         r->buf = xspanalloc(r->nbuf = Bufsize, 128, 0);
933         ctlr->blds = xspanalloc(Nblocks * sizeof(Bld), 128, 0);
934         if(r->buf == nil || ctlr->blds == nil){
935                 print("hda: no memory for stream\n");
936                 return -1;
937         }
938         for(i=0; i<Nblocks; i++){
939                 ctlr->blds[i].addrlo = PADDR(r->buf) + i*Blocksize;
940                 ctlr->blds[i].addrhi = 0;
941                 ctlr->blds[i].len = Blocksize;
942                 ctlr->blds[i].flags = 0x01;     /* interrupt on completion */
943         }
944
945         /* output dma engine starts after inputs */
946         ctlr->sdnum = ctlr->iss;
947         ctlr->sdctl = Sdctl0 + ctlr->sdnum*0x20;
948         ctlr->sdintr = 1<<ctlr->sdnum;
949         ctlr->atag = ctlr->sdnum+1;
950         ctlr->afmt = Fmtstereo | Fmtsampw | Fmtdiv1 | Fmtmul1 | Fmtbase441;
951         ctlr->active = 0;
952
953         /* perform reset */
954         csr8(ctlr, ctlr->sdctl) &= ~(Srst | Srun | Scie | Seie | Sdie);
955         csr8(ctlr, ctlr->sdctl) |= Srst;
956         microdelay(Codecdelay);
957         waitup8(ctlr, ctlr->sdctl, Srst, Srst);
958         csr8(ctlr, ctlr->sdctl) &= ~Srst;
959         microdelay(Codecdelay);
960         waitup8(ctlr, ctlr->sdctl, Srst, 0);
961
962         /* set stream number */
963         csr32(ctlr, ctlr->sdctl) = (ctlr->atag << Stagbit) |
964                 (csr32(ctlr, ctlr->sdctl) & ~(0xF << Stagbit));
965
966         /* set stream format */
967         csr16(ctlr, Sdfmt+ctlr->sdctl) = ctlr->afmt;
968
969         /* program stream DMA & parms */
970         csr32(ctlr, Sdbdplo+ctlr->sdctl) = PADDR(ctlr->blds);
971         csr32(ctlr, Sdbdphi+ctlr->sdctl) = 0;
972         csr32(ctlr, Sdcbl+ctlr->sdctl) = r->nbuf;
973         csr16(ctlr, Sdlvi+ctlr->sdctl) = (Nblocks - 1) & 0xff;
974
975         /* mask out ints */
976         csr8(ctlr, Sdsts+ctlr->sdctl) = Scompl | Sfifoerr | Sdescerr;
977
978         /* enable global intrs for this stream */
979         csr32(ctlr, Intctl) |= ctlr->sdintr;
980         csr8(ctlr, ctlr->sdctl) |= Scie | Seie | Sdie;
981
982         return 0;
983 }
984
985 static void
986 streamstart(Ctlr *ctlr)
987 {
988         ctlr->active = 1;
989         
990         csr8(ctlr, ctlr->sdctl) |= Srun;
991         waitup8(ctlr, ctlr->sdctl, Srun, Srun);
992 }
993
994 static void
995 streamstop(Ctlr *ctlr)
996 {
997         csr8(ctlr, ctlr->sdctl) &= ~Srun;
998         waitup8(ctlr, ctlr->sdctl, Srun, 0);
999
1000         ctlr->active = 0;
1001 }
1002
1003 static uint
1004 streampos(Ctlr *ctlr)
1005 {
1006         Ring *r;
1007         uint p;
1008
1009         r = &ctlr->ring;
1010         p = csr32(ctlr, Sdlpib+ctlr->sdctl);
1011         if(p >= r->nbuf)
1012                 p = 0;
1013         return p;
1014 }
1015
1016 static long
1017 hdactl(Audio *adev, void *va, long n, vlong)
1018 {
1019         char *p, *e, *x, *tok[4];
1020         int ntok;
1021         Ctlr *ctlr;
1022         uint pin, cad;
1023         
1024         ctlr = adev->ctlr;
1025         p = va;
1026         e = p + n;
1027         
1028         for(; p < e; p = x){
1029                 if(x = strchr(p, '\n'))
1030                         *x++ = 0;
1031                 else
1032                         x = e;
1033                 ntok = tokenize(p, tok, 4);
1034                 if(ntok <= 0)
1035                         continue;
1036                 if(cistrcmp(tok[0], "pin") == 0 && ntok >= 2){
1037                         cad = ctlr->cad;
1038                         pin = strtoul(tok[1], 0, 0);
1039                         if(ntok > 2)
1040                                 cad = strtoul(tok[2], 0, 0);
1041                         connectpin(ctlr, pin, cad);
1042                 }else
1043                         error(Ebadctl);
1044         }
1045         return n;
1046 }
1047
1048 static int
1049 outavail(void *arg)
1050 {
1051         Ctlr *ctlr = arg;
1052         return available(&ctlr->ring) > 0;
1053 }
1054
1055 static int
1056 outrate(void *arg)
1057 {
1058         Ctlr *ctlr = arg;
1059         int delay = ctlr->adev->delay*BytesPerSample;
1060         return (delay <= 0) || (buffered(&ctlr->ring) <= delay) || (ctlr->active == 0);
1061 }
1062
1063 static long
1064 hdabuffered(Audio *adev)
1065 {
1066         Ctlr *ctlr;
1067         ctlr = adev->ctlr;
1068         return buffered(&ctlr->ring);
1069 }
1070
1071 static void
1072 hdakick(Ctlr *ctlr)
1073 {
1074         if(ctlr->active)
1075                 return;
1076         if(buffered(&ctlr->ring) > Blocksize)
1077                 streamstart(ctlr);
1078 }
1079
1080 static long
1081 hdawrite(Audio *adev, void *vp, long n, vlong)
1082 {
1083         uchar *p, *e;
1084         Ctlr *ctlr;
1085         Ring *ring;
1086
1087         p = vp;
1088         e = p + n;
1089         ctlr = adev->ctlr;
1090         ring = &ctlr->ring;
1091         while(p < e) {
1092                 if((n = writering(ring, p, e - p)) <= 0){
1093                         hdakick(ctlr);
1094                         sleep(&ring->r, outavail, ctlr);
1095                         continue;
1096                 }
1097                 p += n;
1098         }
1099         hdakick(ctlr);
1100         sleep(&ring->r, outrate, ctlr);
1101         return p - (uchar*)vp;
1102 }
1103
1104 static void
1105 hdaclose(Audio *adev)
1106 {
1107         Ctlr *ctlr;
1108         uchar z[1];
1109         Ring *r;
1110
1111         ctlr = adev->ctlr;
1112         if(!ctlr->active)
1113                 return;
1114         z[0] = 0;
1115         r = &ctlr->ring;
1116         while(r->wi % Blocksize)
1117                 hdawrite(adev, z, sizeof(z), 0);
1118 }
1119
1120 enum {
1121         Vmaster,
1122         Vspeed,
1123         Vdelay,
1124         Nvol,
1125 };
1126
1127 static Volume voltab[] = {
1128         [Vmaster] "master", 0, 0x7f, Stereo, 0,
1129         [Vspeed] "speed", 0, 0, Absolute, 0,
1130         [Vdelay] "delay", 0, 0, Absolute, 0,
1131         0
1132 };
1133
1134 static int
1135 hdagetvol(Audio *adev, int x, int a[2])
1136 {
1137         Ctlr *ctlr = adev->ctlr;
1138
1139         switch(x){
1140         case Vmaster:
1141                 if(ctlr->amp != nil)
1142                         getoutamp(ctlr->amp, a);
1143                 break;
1144         case Vspeed:
1145                 a[0] = adev->speed;
1146                 break;
1147         case Vdelay:
1148                 a[0] = adev->delay;
1149                 break;
1150         }
1151         return 0;
1152 }
1153
1154 static int
1155 hdasetvol(Audio *adev, int x, int a[2])
1156 {
1157         Ctlr *ctlr = adev->ctlr;
1158
1159         switch(x){
1160         case Vmaster:
1161                 if(ctlr->amp != nil)
1162                         setoutamp(ctlr->amp, 0, a);
1163                 break;
1164         case Vspeed:
1165                 adev->speed = a[0];
1166                 break;
1167         case Vdelay:
1168                 adev->delay = a[0];
1169                 break;
1170         }
1171         return 0;
1172 }
1173
1174 static void
1175 fillvoltab(Ctlr *ctlr, Volume *vt)
1176 {
1177         memmove(vt, voltab, sizeof(voltab));
1178         if(ctlr->amp != nil)
1179                 vt[Vmaster].range = getoutamprange(ctlr->amp);
1180 }
1181
1182 static long
1183 hdavolread(Audio *adev, void *a, long n, vlong)
1184 {
1185         Volume voltab[Nvol+1];
1186         fillvoltab(adev->ctlr, voltab);
1187         return genaudiovolread(adev, a, n, 0, voltab, hdagetvol, 0);
1188 }
1189
1190 static long
1191 hdavolwrite(Audio *adev, void *a, long n, vlong)
1192 {
1193         Volume voltab[Nvol+1];
1194         fillvoltab(adev->ctlr, voltab);
1195         return genaudiovolwrite(adev, a, n, 0, voltab, hdasetvol, 0);
1196 }
1197
1198 static void
1199 hdainterrupt(Ureg *, void *arg)
1200 {
1201         Ctlr *ctlr;
1202         Audio *adev;
1203         uint sts;
1204         Ring *r;
1205
1206         adev = arg;
1207         ctlr = adev->ctlr;
1208         
1209         ilock(ctlr);
1210         sts = csr32(ctlr, Intsts);
1211         if(sts & ctlr->sdintr){
1212                 /* ack interrupt */
1213                 csr8(ctlr, Sdsts+ctlr->sdctl) |= Scompl;
1214                 r = &ctlr->ring;
1215                 r->ri = streampos(ctlr);
1216                 if(ctlr->active && buffered(r) < Blocksize){
1217                         streamstop(ctlr);
1218                         r->ri = r->wi = streampos(ctlr);
1219                 }
1220                 wakeup(&r->r);
1221         }
1222         iunlock(ctlr);
1223 }
1224
1225 static long
1226 hdastatus(Audio *adev, void *a, long n, vlong)
1227 {
1228         Ctlr *ctlr = adev->ctlr;
1229         Codec *codec;
1230         Fungroup *fg;
1231         Widget *w;
1232         uint r;
1233         int k, i;
1234         char *s;
1235         
1236         s = a;
1237         k = snprint(s, n, "bufsize %6d buffered %6ld\n", Blocksize, buffered(&ctlr->ring));
1238         for(i=0; i<Maxcodecs; i++){
1239                 if((codec = ctlr->codec[i]) == nil)
1240                         continue;
1241                 k += snprint(s+k, n-k, "codec %2d pin %3d\n",
1242                         codec->id.codec, ctlr->pin);
1243                 for(fg=codec->fgroup; fg; fg=fg->next){
1244                         for(w=fg->first; w; w=w->next){
1245                                 if(w->type != Wpin)
1246                                         continue;
1247                                 r = w->pin;
1248                                 k += snprint(s+k, n-k, "pin %3d %s %s %s %s %s %s\n",
1249                                         w->id.nid,
1250                                         (w->pincap & Pout) != 0 ? "out" : "in",
1251                                         pinport[(r >> 30) & 0x3],
1252                                         pinloc2[(r >> 28) & 0x3],
1253                                         pinloc[(r >> 24) & 0xf],
1254                                         pinfunc[(r >> 20) & 0xf],
1255                                         pincol[(r >> 12) & 0xf]
1256                                 );
1257                         }
1258                 }
1259         }
1260         return k;
1261 }
1262
1263
1264 static int
1265 hdastart(Ctlr *ctlr)
1266 {
1267         static int cmdbufsize[] = { 2, 16, 256, 2048 };
1268         int n, size;
1269         uint cap;
1270         
1271         /* reset controller */
1272         csr32(ctlr, Gctl) &= ~Rst;
1273         waitup32(ctlr, Gctl, Rst, 0);
1274         microdelay(Codecdelay);
1275         csr32(ctlr, Gctl) |= Rst;
1276         if(waitup32(ctlr, Gctl, Rst, Rst) && 
1277             waitup32(ctlr, Gctl, Rst, Rst)){
1278                 print("#A%d: hda failed to reset\n", ctlr->no);
1279                 return -1;
1280         }
1281         microdelay(Codecdelay);
1282
1283         ctlr->codecmask = csr16(ctlr, Statests);
1284         if(ctlr->codecmask == 0){
1285                 print("#A%d: hda no codecs\n", ctlr->no);
1286                 return -1;
1287         }
1288
1289         cap = csr16(ctlr, Gcap);
1290         ctlr->bss = (cap>>3) & 0x1F;
1291         ctlr->iss = (cap>>8) & 0xF;
1292         ctlr->oss = (cap>>12) & 0xF;
1293
1294         csr8(ctlr, Corbctl) = 0;
1295         waitup8(ctlr, Corbctl, Corbdma, 0);
1296
1297         csr8(ctlr, Rirbctl) = 0;
1298         waitup8(ctlr, Rirbctl, Rirbdma, 0);
1299
1300         /* alloc command buffers */
1301         size = csr8(ctlr, Corbsz);
1302         n = cmdbufsize[size & 3];
1303         ctlr->corb = xspanalloc(n * 4, 128, 0);
1304         memset(ctlr->corb, 0, n * 4);
1305         ctlr->corbsize = n;
1306
1307         size = csr8(ctlr, Rirbsz);
1308         n = cmdbufsize[size & 3];
1309         ctlr->rirb = xspanalloc(n * 8, 128, 0);
1310         memset(ctlr->rirb, 0, n * 8);
1311         ctlr->rirbsize = n;
1312
1313         /* setup controller  */
1314         csr32(ctlr, Dplbase) = 0;
1315         csr32(ctlr, Dpubase) = 0;
1316         csr16(ctlr, Statests) = csr16(ctlr, Statests);
1317         csr8(ctlr, Rirbsts) = csr8(ctlr, Rirbsts);
1318         
1319         /* setup CORB */
1320         csr32(ctlr, Corblbase) = PADDR(ctlr->corb);
1321         csr32(ctlr, Corbubase) = 0;
1322         csr16(ctlr, Corbwp) = 0;
1323         csr16(ctlr, Corbrp) = Corbptrrst;
1324         waitup16(ctlr, Corbrp, Corbptrrst, Corbptrrst);
1325         csr16(ctlr, Corbrp) = 0;
1326         waitup16(ctlr, Corbrp, Corbptrrst, 0);
1327         csr8(ctlr, Corbctl) = Corbdma;
1328         waitup8(ctlr, Corbctl, Corbdma, Corbdma);
1329         
1330         /* setup RIRB */
1331         csr32(ctlr, Rirblbase) = PADDR(ctlr->rirb);
1332         csr32(ctlr, Rirbubase) = 0;
1333         csr16(ctlr, Rirbwp) = Rirbptrrst;
1334         csr8(ctlr, Rirbctl) = Rirbdma;
1335         waitup8(ctlr, Rirbctl, Rirbdma, Rirbdma);
1336         
1337         /* enable interrupts */
1338         csr32(ctlr, Intctl) |= Gie | Cie;
1339         
1340         return 0;
1341 }
1342
1343 static Pcidev*
1344 hdamatch(Pcidev *p)
1345 {
1346         while(p = pcimatch(p, 0, 0))
1347                 switch((p->vid << 16) | p->did){
1348                 case (0x8086 << 16) | 0x27d8:
1349                 case (0x1002 << 16) | 0x4383:   /* ATI */
1350                 case (0x1002 << 16) | 0x7919:   /* ATI HDMI */
1351                         return p;
1352                 }
1353         return nil;
1354 }
1355
1356 static long
1357 hdacmdread(Chan *, void *a, long n, vlong)
1358 {
1359         Ctlr *ctlr;
1360         
1361         ctlr = lastcard;
1362         if(ctlr == nil)
1363                 error(Enodev);
1364         if(n & 7)
1365                 error(Ebadarg);
1366         return qread(ctlr->q, a, n);
1367 }
1368
1369 static long
1370 hdacmdwrite(Chan *, void *a, long n, vlong)
1371 {
1372         Ctlr *ctlr;
1373         ulong *lp;
1374         int i;
1375         uint w[2];
1376         
1377         ctlr = lastcard;
1378         if(ctlr == nil)
1379                 error(Enodev);
1380         if(n & 3)
1381                 error(Ebadarg);
1382         lp = a;
1383         qlock(ctlr);
1384         for(i=0; i<n/4; i++){
1385                 if(hdacmd(ctlr, lp[i], w) <= 0){
1386                         w[0] = 0;
1387                         w[1] = ~0;
1388                 }
1389                 qproduce(ctlr->q, w, sizeof(w));
1390         }
1391         qunlock(ctlr);
1392         return n;
1393 }
1394
1395 static int
1396 hdareset(Audio *adev)
1397 {
1398         static Ctlr *cards = nil;
1399         int irq, tbdf, best, cad;
1400         Ctlr *ctlr;
1401         Pcidev *p;
1402
1403         /* make a list of all cards if not already done */
1404         if(cards == nil){
1405                 p = nil;
1406                 while(p = hdamatch(p)){
1407                         ctlr = xspanalloc(sizeof(Ctlr), 8, 0);
1408                         memset(ctlr, 0, sizeof(Ctlr));
1409                         ctlr->pcidev = p;
1410                         ctlr->next = cards;
1411                         cards = ctlr;
1412                 }
1413         }
1414
1415         /* pick a card from the list */
1416         for(ctlr = cards; ctlr; ctlr = ctlr->next){
1417                 if(p = ctlr->pcidev){
1418                         ctlr->pcidev = nil;
1419                         goto Found;
1420                 }
1421         }
1422         return -1;
1423
1424 Found:
1425         adev->ctlr = ctlr;
1426         ctlr->adev = adev;
1427
1428         irq = p->intl;
1429         tbdf = p->tbdf;
1430
1431         /* magic for ATI */
1432         if(p->vid == 0x1002)
1433                 pcicfgw8(p, 0x42, pcicfgr8(p, 0x42) | 2);
1434
1435         pcisetbme(p);
1436         pcisetpms(p, 0);
1437
1438         ctlr->no = adev->ctlrno;
1439         ctlr->size = p->mem[0].size;
1440         ctlr->q = qopen(256, 0, 0, 0);
1441         ctlr->mem = vmap(p->mem[0].bar & ~0x0F, ctlr->size);
1442         if(ctlr->mem == nil){
1443                 print("#A%d: can't map %.8lux\n", ctlr->no, p->mem[0].bar);
1444                 return -1;
1445         }
1446         print("#A%d: hda mem %p irq %d\n", ctlr->no, ctlr->mem, irq);
1447
1448         if(hdastart(ctlr) < 0){
1449                 print("#A%d: unable to start hda\n", ctlr->no);
1450                 return -1;
1451         }
1452         if(streamalloc(ctlr) < 0){
1453                 print("#A%d: streamalloc failed\n", ctlr->no);
1454                 return -1;
1455         }
1456         if(enumdev(ctlr) < 0){
1457                 print("#A%d: no audio codecs found\n", ctlr->no);
1458                 return -1;
1459         }
1460         best = bestpin(ctlr, &cad);
1461         if(best < 0){
1462                 print("#A%d: no output pins found!\n", ctlr->no);
1463                 return -1;
1464         }
1465         if(connectpin(ctlr, best, cad) < 0){
1466                 print("#A%d: error connecting pin\n", ctlr->no);
1467                 return -1;
1468         }
1469
1470         adev->write = hdawrite;
1471         adev->close = hdaclose;
1472         adev->buffered = hdabuffered;
1473         adev->volread = hdavolread;
1474         adev->volwrite = hdavolwrite;
1475         adev->status = hdastatus;
1476         adev->ctl = hdactl;
1477         
1478         intrenable(irq, hdainterrupt, adev, tbdf, "hda");
1479         lastcard = ctlr;
1480         addarchfile("hdacmd", 0664, hdacmdread, hdacmdwrite);
1481         
1482         return 0;
1483 }
1484
1485 void
1486 audiohdalink(void)
1487 {
1488         addaudiocard("hda", hdareset);
1489 }
1490