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