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