]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/audiohda.c
7b978f117f6c3b30e2f16a6a75bcf715cc980ded
[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                 Aidx = 8,
206                 Again = 0,
207         Getconvfmt = 0xa,
208         Setconvfmt = 0x2,
209 };
210
211 enum {
212         Maxcodecs = 16,
213         Maxwidgets = 256,
214 };
215
216 struct Ring {
217         uint rp, wp, cp;
218         uint size, blocksize;
219         uchar *buf;
220 };
221
222 struct Bld {
223         uint addrlo, addrhi;
224         uint len, flags;
225 };
226
227 struct Id {
228         Ctlr *ctlr;
229         uint codec, nid;
230 };
231
232 struct Widget {
233         Id id;
234         Fungroup *fg;
235         uint cap, type;
236         uint nlist;
237         Widget **list;
238         union {
239                 struct {
240                         uint pin, pincap;
241                 };
242                 struct {
243                         uint convrate, convfmt;
244                 };
245         };
246         Widget *next;
247         Widget *from;
248 };
249
250 struct Fungroup {
251         Id id;
252         Codec *codec;
253         uint type;
254         Widget *first;
255         Widget *mixer;
256         Widget *src, *dst;
257         Fungroup *next;
258 };
259
260 struct Codec {
261         Id id;
262         uint vid, rid;
263         Widget *widgets[Maxwidgets];
264         Fungroup *fgroup;
265 };
266
267 struct Ctlr {
268         Ctlr *next;
269         uint no;
270
271         Lock;                   /* interrupt lock */
272         QLock;                  /* command lock */
273         Rendez outr;
274         
275         Pcidev *pcidev;
276         
277         uchar *mem;
278         ulong size;
279         
280         ulong *corb;
281         ulong corbsize;
282         
283         ulong *rirb;
284         ulong rirbsize;
285         
286         Bld *blds;
287         Ring ring;
288         
289         Codec codec;
290         Widget *amp, *src;
291         uint pin;
292
293         int active;
294         uint afmt, atag;
295 };
296
297 #define csr32(c, r)     (*(ulong *)&(c)->mem[r])
298 #define csr16(c, r)     (*(ushort *)&(c)->mem[r])
299 #define csr8(c, r)      (*(uchar *)&(c)->mem[r])
300
301 static char *pinport[] = {
302         "jack",
303         "nothing",
304         "fix",
305         "jack+fix",
306 };
307
308 static char *pinfunc[] = {
309         "lineout",
310         "speaker",
311         "hpout",
312         "cd",
313         "spdifout",
314         "digiout",
315         "modemline",
316         "modemhandset",
317         "linein",
318         "aux",
319         "micin",
320         "telephony",
321         "spdifin",
322         "digiin",
323         "resvd",
324         "other",
325 };
326
327
328 static char *pincol[] = {
329         "?",
330         "black",
331         "grey",
332         "blue",
333         "green",
334         "red",
335         "orange",
336         "yellow",
337         "purple",
338         "pink",
339         "resvd",
340         "resvd",
341         "resvd",
342         "resvd",
343         "white",
344         "other",
345 };
346
347 static char *pinloc[] = {
348         "N/A",
349         "rear",
350         "front",
351         "left",
352         "right",
353         "top",
354         "bottom",
355         "special",
356         "special",
357         "special",
358         "resvd",
359         "resvd",
360         "resvd",
361         "resvd",
362         "resvd",
363         "resvd",
364 };
365
366 static char *pinloc2[] = {
367         "ext",
368         "int",
369         "sep",
370         "other",
371 };
372
373 static int
374 waitup8(Ctlr *ctlr, int reg, uchar mask, uchar set)
375 {
376         int i;
377         for(i=0; i<Maxwaitup; i++){
378                 if((csr8(ctlr, reg) & mask) == set)
379                         return 0;
380                 microdelay(1);
381         }
382         print("#A%d: waitup timeout for reg=%x, mask=%x, set=%x\n",
383                 ctlr->no, reg, mask, set);
384         return -1;
385 }
386
387 static int
388 waitup16(Ctlr *ctlr, int reg, ushort mask, ushort set)
389 {
390         int i;
391         for(i=0; i<Maxwaitup; i++){
392                 if((csr16(ctlr, reg) & mask) == set)
393                         return 0;
394                 microdelay(1);
395         }
396         print("#A%d: waitup timeout for reg=%x, mask=%x, set=%x\n",
397                 ctlr->no, reg, mask, set);
398         return -1;
399 }
400
401 static int
402 waitup32(Ctlr *ctlr, int reg, uint mask, uint set)
403 {
404         int i;
405         for(i=0; i<Maxwaitup; i++){
406                 if((csr32(ctlr, reg) & mask) == set)
407                         return 0;
408                 microdelay(1);
409         }
410         print("#A%d: waitup timeout for reg=%x, mask=%x, set=%x\n",
411                 ctlr->no, reg, mask, set);
412         return -1;
413 }
414
415 static int
416 hdacmd(Ctlr *ctlr, uint request, uint reply[2])
417 {
418         uint rp, wp;
419         uint re;
420         int wait;
421         
422         re = csr16(ctlr, Rirbwp);
423         rp = csr16(ctlr, Corbrp);
424         wp = (csr16(ctlr, Corbwp) + 1) % ctlr->corbsize;
425         if(rp == wp){
426                 print("#A%d: corb full\n", ctlr->no);
427                 return -1;
428         }
429         ctlr->corb[wp] = request;
430         coherence();
431         csr16(ctlr, Corbwp) = wp;
432         for(wait=0; wait < Maxrirbwait; wait++){
433                 if(csr16(ctlr, Rirbwp) != re){
434                         re = (re + 1) % ctlr->rirbsize;
435                         memmove(reply, &ctlr->rirb[re*2], 8);
436                         return 8;
437                 }
438                 microdelay(1);
439         }
440         return 0;
441 }
442
443 static int
444 cmderr(Id id, uint verb, uint par, uint *ret)
445 {
446         uint q, w[2];
447         q = (id.codec << 28) | (id.nid << 20);
448         if((verb & 0x700) == 0x700)
449                 q |= (verb << 8) | par;
450         else
451                 q |= (verb << 16) | par;
452         if(hdacmd(id.ctlr, q, w) != 8)
453                 return -1;
454         *ret = w[0];
455         return 0;
456 }
457
458 static uint
459 cmd(Id id, uint verb, uint par)
460 {
461         uint w[2];
462         if(cmderr(id, verb, par, w) == -1)
463                 return ~0;
464         return w[0];
465 }
466
467 static Id
468 newnid(Id id, uint nid)
469 {
470         id.nid = nid;
471         return id;
472 }
473
474 /* vol is 0...127 or ~0 for 0dB; mute is 0/1 */
475 static void
476 setoutamp(Widget *w, int mute, uint vol)
477 {
478         uint q, r;
479         uint zerodb, range;
480
481         if((w->cap & Woutampcap) == 0)
482                 return;
483
484         r = cmd(w->id, Getparm, Outampcap);
485         range = (r >> 8) & 0x7f;
486         zerodb = r & 127;
487
488         q = Asetout | Asetleft | Asetright;
489         if(mute)
490                 q |= Asetmute;
491         else if(vol == ~0)
492                 q |= zerodb << Again;
493         else if(range > 0)
494                 q |= (range * vol / 128) << Again;
495
496         cmd(w->id, Setamp, q);
497 }
498
499 static void
500 getoutamp(Widget *w, int v[2])
501 {
502         uint q, r, range;
503         
504         v[0] = 0;
505         v[1] = 0;
506         
507         if((w->cap & Woutampcap) == 0)
508                 return;
509                 
510         r = cmd(w->id, Getparm, Outampcap);
511         range = (r >> 8) & 0x7f;
512         
513         q = (1 << 15) | (1 << 13);
514         r = cmd(w->id, Getamp, q);
515         v[0] = (r & 0x7f) * 128 / range;
516         
517         q = (1 << 15) | (0 << 13);
518         r = cmd(w->id, Getamp, q);
519         v[1] = (r & 0x7f) * 128 / range;
520 }
521         
522 /* vol is 0...127 or ~0 for 0dB; mute is 0/1; in is widget or nil for all */
523 static void
524 setinamp(Widget *w, Widget *in, int mute, uint vol)
525 {
526         uint q, r, i;
527         uint zerodb, range;
528
529         if((w->cap & Winampcap) == 0)
530                 return;
531
532         r = cmd(w->id, Getparm, Inampcap);
533         range = (r >> 8) & 0x7f;
534         zerodb = r & 127;
535
536         q = Asetin | Asetleft | Asetright;
537         if(mute)
538                 q |= Asetmute;
539         else if(vol == ~0)
540                 q |= zerodb << Again;
541         else if(range > 0)
542                 q |= (range * vol / 128) << Again;
543         for(i=0; i<w->nlist; i++){
544                 if(in == nil || w->list[i] == in)
545                         cmd(w->id, Setamp, q | (i << Aidx));
546         }
547 }
548
549 static Widget *
550 findpath(Widget *src)
551 {
552         Widget *q[Maxwidgets];
553         uint l, r, i;
554         Widget *w, *v;
555         
556         l = r = 0;
557         q[r++] = src;
558         for(w=src->fg->first; w; w=w->next)
559                 w->from = nil;
560         src->from = src;
561
562         while(l < r){
563                 w = q[l++];
564                 if(w->type == Waout)
565                         break;
566                 for(i=0; i<w->nlist; i++){
567                         v = w->list[i];
568                         if(v->from)
569                                 continue;
570                         v->from = w;
571                         q[r++] = v;
572                 }
573         }
574         if(w->type != Waout)
575                 return nil;
576         return w;
577 }
578
579 static void
580 connectpath(Widget *src, Widget *dst, uint stream)
581 {
582         Widget *w, *v;
583         uint i;
584
585         for(w=src->fg->first; w != nil; w=w->next){
586                 setoutamp(w, 1, 0);
587                 setinamp(w, nil, 1, 0);
588                 cmd(w->id, Setstream, 0);
589         }
590         for(w=dst; w != src; w=v){
591                 v = w->from;
592                 setoutamp(w, 0, ~0);
593                 setinamp(v, w, 0, ~0);
594                 if(v->type == Waout || v->type == Wamix)
595                         continue;
596                 if(v->nlist == 1)
597                         continue;
598                 for(i=0; i < v->nlist && v->list[i] != w; i++)
599                         ;
600                 cmd(v->id, Setconn, i);
601         }
602         setoutamp(src, 0, ~0);
603         cmd(src->id, Setpinctl, Pinctlout);
604         cmd(dst->id, Setstream, (stream << 4) | 0);
605         cmd(dst->id, Setconvfmt, (1 << 14) | (1 << 4) | 1);
606         cmd(dst->id, Setchancnt, 1);
607 }
608
609 static void
610 enumconns(Widget *w)
611 {
612         uint r, i, mask, bits, nlist;
613         Widget **ws, **list;
614         
615         ws = w->fg->codec->widgets;
616         r = cmd(w->id, Getparm, Connlistlen);
617         bits = (r & 0x80) == 0 ? 8 : 16;
618         nlist = r & 0x7f;
619         mask = (1 << bits) - 1;
620         list = malloc(sizeof *list * nlist);
621         for(i=0; i<nlist; i++){
622                 if(i * bits % 32 == 0)
623                         r = cmd(w->id, Getconnlist, i);         
624                 list[i] = ws[(r >> (i * bits % 32)) & mask];
625         }
626         w->nlist = nlist;
627         w->list = list;
628 }
629
630 static void
631 enumwidget(Widget *w)
632 {
633         w->cap = cmd(w->id, Getparm, Widgetcap);
634         w->type = (w->cap >> 20) & 0x7;
635         
636         enumconns(w);
637         
638         switch(w->type){
639                 case Wpin:
640                         w->pin = cmd(w->id, Getdefault, 0);
641                         w->pincap = cmd(w->id, Getparm, Pincap);
642                         break;
643         }
644 }
645
646 static Fungroup *
647 enumfungroup(Codec *codec, Id id)
648 {
649         Fungroup *fg;
650         Widget *w, *next;
651         uint i, r, n, base;
652
653         r = cmd(id, Getparm, Fungrtype) & 0x7f;
654         if(r != Graudio)
655                 return nil;
656
657         fg = mallocz(sizeof *fg, 1);
658         fg->codec = codec;
659         fg->id = id;
660         fg->type = r;
661
662         r = cmd(id, Getparm, Subnodecnt);
663         n = r & 0xff;
664         base = (r >> 8) & 0xff;
665         
666         if(base + n > Maxwidgets)
667                 return nil;
668         
669         for(i=n, next=nil; i--; next=w){
670                 w = mallocz(sizeof(Widget), 1);
671                 w->id = newnid(id, base + i);
672                 w->fg = fg;
673                 w->next = next;
674                 codec->widgets[base + i] = w;
675         }
676         fg->first = next;
677
678         for(i=0; i<n; i++)
679                 enumwidget(codec->widgets[base + i]);
680
681         return fg;
682 }
683
684
685 static int
686 enumcodec(Codec *codec, Id id)
687 {
688         Fungroup *fg;
689         uint i, r, n, base;
690         uint vid, rid;
691         
692         
693         if(cmderr(id, Getparm, Vendorid, &vid) < 0)
694                 return -1;
695         if(cmderr(id, Getparm, Revid, &rid) < 0)
696                 return -1;
697         
698         codec->id = id;
699         codec->vid = vid;
700         codec->rid = rid;
701
702         r = cmd(id, Getparm, Subnodecnt);
703         n = r & 0xff;
704         base = (r >> 16) & 0xff;
705         
706         for(i=0; i<n; i++){
707                 fg = enumfungroup(codec, newnid(id, base + i));
708                 if(fg == nil)
709                         continue;
710                 fg->next = codec->fgroup;
711                 codec->fgroup = fg;
712         }
713         if(codec->fgroup == nil)
714                 return -1;
715         return 0;
716 }
717
718 static int
719 enumdev(Ctlr *ctlr)
720 {
721         Id id;
722         int i;
723         
724         id.ctlr = ctlr;
725         id.nid = 0;
726         for(i=0; i<Maxcodecs; i++){
727                 id.codec = i;
728                 if(enumcodec(&ctlr->codec, id) == 0)
729                         return 0;
730         }
731         return -1;
732 }
733
734 static int
735 connectpin(Ctlr *ctlr, uint pin)
736 {
737         Widget *src, *dst;
738         
739         src = ctlr->codec.widgets[pin];
740         if(src == nil)
741                 return -1;
742         if(src->type != Wpin)
743                 return -1;
744         if((src->pincap & Pout) == 0)
745                 return -1;
746         dst = findpath(src);
747         if(!dst)
748                 return -1;
749                 
750         connectpath(src, dst, Streamtag);
751         ctlr->amp = dst;
752         ctlr->src = src;
753         ctlr->pin = pin;
754         return 0;
755 }
756
757 static int
758 bestpin(Ctlr *ctlr)
759 {
760         Fungroup *fg;
761         Widget *w;
762         int best, pin, score;
763         uint r;
764         
765         pin = -1;
766         best = -1;
767         for(fg=ctlr->codec.fgroup; fg; fg=fg->next){
768                 for(w=fg->first; w; w=w->next){
769                         if(w->type != Wpin)
770                                 continue;
771                         if((w->pincap & Pout) == 0)
772                                 continue;
773                         score = 0;
774                         r = w->pin;
775                         if(((r >> 12) & 0xf) == 4)      /* green */
776                                 score |= 32;
777                         if(((r >> 24) & 0xf) == 1)      /* rear */
778                                 score |= 16;
779                         if(((r >> 28) & 0x3) == 0) /* ext */
780                                 score |= 8;
781                         if(((r >> 20) & 0xf) == 2) /* hpout */
782                                 score |= 4;
783                         if(((r >> 20) & 0xf) == 0) /* lineout */
784                                 score |= 4;
785                         if(score >= best){
786                                 best = score;
787                                 pin = w->id.nid;
788                         }
789                 }
790         }
791         return pin;
792 }
793
794 static void
795 ringreset(Ring *r)
796 {
797         memset(r->buf, 0, r->size);
798         r->rp = 0;
799         r->wp = 0;
800         r->cp = 0;
801 }
802
803 static uint
804 ringused(Ring *r)
805 {
806         return (r->wp - r->rp) % r->size;
807 }
808
809 static uint
810 ringavail(Ring *r)
811 {
812         return r->size - r->blocksize - ringused(r);
813 }
814
815 static uint
816 ringdirty(Ring *r)
817 {
818         return (r->rp - r->cp) % r->size;
819 }
820
821 static void
822 ringalign(Ring *r)
823 {
824         r->wp += r->blocksize - 1;
825         r->wp -= r->wp % r->blocksize;
826         r->wp %= r->size;
827 }
828
829 static uint
830 ringwrite(Ring *r, uchar *ap, uint n)
831 {
832         uchar *p;
833         uint a, c;
834
835         p = ap;
836         a = ringavail(r);
837         if(n > a)
838                 n = a;
839                 
840         c = ringdirty(r);
841         while(c > 0){
842                 a = r->size - r->cp;
843                 if(a > c)
844                         a = c;
845                 memset(r->buf + r->cp, 0, a);
846                 r->cp = (r->cp + a) % r->size;
847                 c -= a;
848         }
849         
850         while(n > 0){
851                 a = r->size - r->wp;
852                 if(a > n)
853                         a = n;
854                 memmove(r->buf + r->wp, p, a);
855                 r->wp = (r->wp + a) % r->size;
856                 p += a;
857                 n -= a;
858         }
859         return p - ap;
860 }
861
862
863 static int
864 ringupdate(Ring *r, uint np)
865 {
866         uint rp, wp, bs, s;
867         
868         rp = r->rp;
869         bs = r->blocksize;
870         s = r->size;
871         
872         np += bs / 2;
873         np %= s;
874         np -= np % bs;
875         wp = r->wp;
876         wp -= wp % bs;
877         r->rp = np;
878         if((np - rp) % s >= (wp - rp) % s)
879                 return 1;
880         return 0;
881 }
882
883 static int
884 streamalloc(Ctlr *ctlr)
885 {
886         uchar *p;
887         Bld *b;
888         uint i;
889         Ring *r;
890         
891         r = &ctlr->ring;
892         r->size = Bufsize;
893         r->blocksize = Blocksize;
894         r->buf = xspanalloc(r->size, 128, 0);
895         if(r->buf == nil)
896                 return -1;
897         ringreset(r);
898         
899         ctlr->active = 0;
900         ctlr->atag = Streamtag;
901         ctlr->afmt = Fmtstereo | Fmtsampw | Fmtdiv1 |
902                 Fmtmul1 | Fmtbase441;
903         
904         ctlr->blds = xspanalloc(Nblocks * sizeof(Bld), 128, 0);
905         if(ctlr->blds == nil)
906                 return -1;
907         b = ctlr->blds;
908         p = r->buf;
909         for(i=0; i<Nblocks; i++){
910                 b->addrlo = PADDR(p);
911                 b->addrhi = 0;
912                 b->flags = ~0;
913                 b->len = Blocksize;
914                 p += Blocksize;
915                 b++;
916         }
917         return 0;
918 }
919
920 static void
921 streamstart(Ctlr *ctlr)
922 {
923         Ring *r = &ctlr->ring;
924                 
925         /* perform reset */
926         csr8(ctlr, Sdctl) = Srst;
927         waitup8(ctlr, Sdctl, Srst, Srst);
928         csr8(ctlr, Sdctl) = 0;
929         waitup8(ctlr, Sdctl, Srst, 0);
930         
931         /* program stream DMA & parms */
932         csr32(ctlr, Sdcbl) = r->size;
933         csr16(ctlr, Sdlvi) = (r->size / r->blocksize - 1) & 0xff;
934         csr32(ctlr, Sdfmt) = ctlr->afmt;
935         csr32(ctlr, Sdbdplo) = PADDR(ctlr->blds);
936         csr32(ctlr, Sdbdphi) = 0;
937         
938         /* enable global intrs for this stream */
939         csr32(ctlr, Intctl) |= (1 << Streamno);
940         
941         /* enable stream intrs */
942         csr32(ctlr, Sdctl) = (ctlr->atag << Stagbit) | Srun | Scie | Seie | Sdie;
943         waitup32(ctlr, Sdctl, Srun, Srun);
944         
945         /* mark as running */
946         ctlr->active = 1;
947 }
948
949 static void
950 streamstop(Ctlr *ctlr)
951 {
952         /* disble stream intrs */
953         csr32(ctlr, Sdctl) = 0;
954         
955         /* disable global intrs for this stream */
956         csr32(ctlr, Intctl) &= ~(1 << Streamno);
957         
958         /* mark as stopped */
959         ctlr->active = 0;
960 }
961
962
963 static void
964 streamupdate(Ctlr *ctlr)
965 {
966         uint pos;
967         Ring *r;
968         
969         r = &ctlr->ring;
970         
971         /* ack interrupt and wake writer */
972         csr8(ctlr, Sdsts) |= 0x4;
973         wakeup(&ctlr->outr);
974         pos = csr32(ctlr, Sdlpib);
975         
976         /* underrun? */
977         if(ringupdate(r, pos) == 1)
978                 streamstop(ctlr);
979 }
980
981 static int
982 outavail(void *arg)
983 {
984         return ringavail(arg) > 0;
985 }
986
987 static int
988 checkptr(Ctlr *ctlr)
989 {
990         Ring *r;
991         
992         r = &ctlr->ring;
993         if(ctlr->active == 1)
994                 return 1;
995         if(r->rp == 0)
996                 return 1;
997         ringreset(r);
998         return 0;
999 }
1000
1001 static void
1002 hdakick(Ctlr *ctlr)
1003 {
1004         Ring *r = &ctlr->ring;
1005         
1006         ilock(ctlr);
1007         if(ctlr->active == 0){
1008                 if(ringused(r) >= r->blocksize){
1009                         iunlock(ctlr);
1010                         streamstart(ctlr);
1011                         return;
1012                 }
1013         }
1014         iunlock(ctlr);
1015 }
1016
1017 static long
1018 hdabuffered(Audio *adev)
1019 {
1020         Ctlr *ctlr;
1021         ctlr = adev->ctlr;
1022         return ringused(&ctlr->ring);
1023 }
1024
1025 static long
1026 hdactl(Audio *adev, void *va, long n, vlong)
1027 {
1028         char *p, *e, *x, *tok[4];
1029         int ntok;
1030         Ctlr *ctlr;
1031         
1032         ctlr = adev->ctlr;
1033         p = va;
1034         e = p + n;
1035         
1036         for(; p < e; p = x){
1037                 if(x = strchr(p, '\n'))
1038                         *x++ = 0;
1039                 else
1040                         x = e;
1041                 ntok = tokenize(p, tok, 4);
1042                 if(ntok <= 0)
1043                         continue;
1044                 if(cistrcmp(tok[0], "pin") == 0 && ntok == 2){
1045                         qlock(ctlr);
1046                         connectpin(ctlr, strtoul(tok[1], 0, 0));
1047                         qunlock(ctlr);
1048                 }else
1049                         error(Ebadctl);
1050         }
1051         return n;
1052 }
1053
1054 static long
1055 hdawrite(Audio *adev, void *vp, long vn, vlong)
1056 {
1057         uchar *p;
1058         uint n, k;
1059         Ring *r;
1060         Ctlr *ctlr;
1061         
1062         p = vp;
1063         n = vn;
1064         ctlr = adev->ctlr;
1065         r = &ctlr->ring;
1066         
1067         checkptr(ctlr);
1068         while(n > 0){
1069                 k = ringwrite(r, p, n);
1070                 if(checkptr(ctlr) == 0)
1071                         continue;
1072                 if(k == 0){
1073                         hdakick(ctlr);
1074                         sleep(&ctlr->outr, outavail, r);
1075                 }else{
1076                         p += k;
1077                         n -= k;
1078                 }
1079         }
1080         hdakick(ctlr);
1081         return vn;
1082 }
1083
1084 static void
1085 hdaclose(Audio *adev)
1086 {
1087         Ctlr *ctlr;
1088         ctlr = adev->ctlr;
1089         ringalign(&ctlr->ring);
1090         hdakick(ctlr);
1091 }
1092
1093 static Volume voltab[] = {
1094         [0] "master", 0, 0x7f, Stereo, 0,
1095         0
1096 };
1097
1098 static int
1099 hdagetvol(Audio *adev, int, int a[2])
1100 {
1101         Ctlr *ctlr = adev->ctlr;
1102         
1103         if(ctlr->amp == nil)
1104                 return -1;
1105         qlock(ctlr);
1106         getoutamp(ctlr->amp, a);
1107         qunlock(ctlr);
1108         return 0;
1109 }
1110
1111 static int
1112 hdasetvol(Audio *adev, int, int a[2])
1113 {
1114         Ctlr *ctlr = adev->ctlr;
1115         
1116         if(ctlr->amp == nil)
1117                 return -1;
1118         qlock(ctlr);
1119         setoutamp(ctlr->amp, 0, a[0]);
1120         qunlock(ctlr);
1121         return 0;
1122 }
1123
1124 static long
1125 hdavolread(Audio *adev, void *a, long n, vlong)
1126 {
1127         return genaudiovolread(adev, a, n, 0, voltab, hdagetvol, 0);
1128 }
1129
1130 static long
1131 hdavolwrite(Audio *adev, void *a, long n, vlong)
1132 {
1133         return genaudiovolwrite(adev, a, n, 0, voltab, hdasetvol, 0);
1134 }
1135
1136 static void
1137 hdainterrupt(Ureg *, void *arg)
1138 {
1139         Ctlr *ctlr;
1140         Audio *adev;
1141         uint sts;
1142         
1143         adev = arg;
1144         ctlr = adev->ctlr;
1145         
1146         ilock(ctlr);
1147         sts = csr32(ctlr, Intsts);
1148         if(sts & Sismask){
1149                 streamupdate(ctlr);
1150         }else{
1151                 iprint("#A%d: hda unhandled interrupt\n", ctlr->no);
1152         }
1153         iunlock(ctlr);
1154 }
1155
1156 static long
1157 hdastatus(Audio *adev, void *a, long n, vlong)
1158 {
1159         Ctlr *ctlr = adev->ctlr;
1160         Fungroup *fg;
1161         Widget *w;
1162         uint r;
1163         int k;
1164         char *s;
1165         
1166         s = a;
1167         k = snprint(s, n, 
1168                 "bufsize %6d buffered %6ud codec %2d pin %3d\n",
1169                 Bufsize, ringused(&ctlr->ring), ctlr->codec.id.codec, ctlr->pin);
1170         
1171         for(fg=ctlr->codec.fgroup; fg; fg=fg->next){
1172                 for(w=fg->first; w; w=w->next){
1173                         if(w->type != Wpin)
1174                                 continue;
1175                         r = w->pin;
1176                         k += snprint(s+k, n-k, "pin %3d %s %s %s %s %s %s\n",
1177                                 w->id.nid,
1178                                 (w->pincap & Pout) != 0 ? "out" : "in",
1179                                 pinport[(r >> 30) & 0x3],
1180                                 pinloc2[(r >> 28) & 0x3],
1181                                 pinloc[(r >> 24) & 0xf],
1182                                 pinfunc[(r >> 20) & 0xf],
1183                                 pincol[(r >> 12) & 0xf]
1184                         );
1185                         
1186                 }
1187         }
1188         return k;
1189 }
1190
1191
1192 static int
1193 hdastart(Ctlr *ctlr)
1194 {
1195         static int cmdbufsize[] = { 2, 16, 256, 2048 };
1196         int n, size;
1197         
1198         /* alloc command buffers */
1199         size = csr8(ctlr, Corbsz);
1200         n = cmdbufsize[size & 3];
1201         ctlr->corb = xspanalloc(n * 4, 128, 0);
1202         memset(ctlr->corb, 0, n * 4);
1203         ctlr->corbsize = n;
1204
1205         size = csr8(ctlr, Rirbsz);
1206         n = cmdbufsize[size & 3];
1207         ctlr->rirb = xspanalloc(n * 8, 128, 0);
1208         memset(ctlr->rirb, 0, n * 8);
1209         ctlr->rirbsize = n;
1210         
1211         /* stop command buffers */
1212         csr16(ctlr, Wakeen) = 0;
1213         csr32(ctlr, Intctl) = 0;
1214         csr8(ctlr, Corbctl) = 0;
1215         csr8(ctlr, Rirbctl) = 0;
1216         waitup8(ctlr, Corbctl, Corbdma, 0);
1217         waitup8(ctlr, Rirbctl, Rirbdma, 0);
1218         
1219         /* reset controller */
1220         csr32(ctlr, Gctl) = 0;
1221         waitup32(ctlr, Gctl, Rst, 0);
1222         microdelay(Codecdelay);
1223         csr32(ctlr, Gctl) = Rst;
1224         waitup32(ctlr, Gctl, Rst, Rst);
1225         
1226         /* setup controller  */
1227         csr32(ctlr, Dplbase) = 0;
1228         csr32(ctlr, Dpubase) = 0;
1229         csr16(ctlr, Statests) = csr16(ctlr, Statests);
1230         csr8(ctlr, Rirbsts) = csr8(ctlr, Rirbsts);
1231         
1232         /* setup CORB */
1233         csr32(ctlr, Corblbase) = PADDR(ctlr->corb);
1234         csr32(ctlr, Corbubase) = 0;
1235         csr16(ctlr, Corbwp) = 0;
1236         csr16(ctlr, Corbrp) = Corbptrrst;
1237         waitup16(ctlr, Corbrp, Corbptrrst, Corbptrrst);
1238         csr16(ctlr, Corbrp) = 0;
1239         waitup16(ctlr, Corbrp, Corbptrrst, 0);
1240         csr8(ctlr, Corbctl) = Corbdma;
1241         waitup8(ctlr, Corbctl, Corbdma, Corbdma);
1242         
1243         /* setup RIRB */
1244         csr32(ctlr, Rirblbase) = PADDR(ctlr->rirb);
1245         csr32(ctlr, Rirbubase) = 0;
1246         csr16(ctlr, Rirbwp) = Rirbptrrst;
1247         csr8(ctlr, Rirbctl) = Rirbdma;
1248         waitup8(ctlr, Rirbctl, Rirbdma, Rirbdma);
1249         
1250         /* enable interrupts */
1251         csr32(ctlr, Intctl) = Gie | Cie;
1252         
1253         return 0;
1254 }
1255
1256 static Pcidev*
1257 hdamatch(Pcidev *p)
1258 {
1259         while(p = pcimatch(p, 0, 0))
1260                 switch((p->vid << 16) | p->did){
1261                 case (0x8086 << 16) | 0x27d8:
1262                         return p;
1263                 }
1264         return nil;
1265 }
1266
1267 static int
1268 hdareset(Audio *adev)
1269 {
1270         static Ctlr *cards = nil;
1271         Pcidev *p;
1272         int irq, tbdf, best;
1273         Ctlr *ctlr;
1274
1275         /* make a list of all ac97 cards if not already done */
1276         if(cards == nil){
1277                 p = nil;
1278                 while(p = hdamatch(p)){
1279                         ctlr = xspanalloc(sizeof(Ctlr), 8, 0);
1280                         memset(ctlr, 0, sizeof(Ctlr));
1281                         ctlr->pcidev = p;
1282                         ctlr->next = cards;
1283                         cards = ctlr;
1284                 }
1285         }
1286
1287         /* pick a card from the list */
1288         for(ctlr = cards; ctlr; ctlr = ctlr->next){
1289                 if(p = ctlr->pcidev){
1290                         ctlr->pcidev = nil;
1291                         goto Found;
1292                 }
1293         }
1294         return -1;
1295
1296 Found:
1297         adev->ctlr = ctlr;
1298
1299         irq = p->intl;
1300         tbdf = p->tbdf;
1301
1302         pcisetbme(p);
1303         pcisetpms(p, 0);
1304         
1305         ctlr->no = adev->ctlrno;
1306         ctlr->size = p->mem[0].size;
1307         ctlr->mem = vmap(p->mem[0].bar & ~0x0F, ctlr->size);
1308         if(ctlr->mem == nil){
1309                 print("#A%d: can't map %.8lux\n", ctlr->no, p->mem[0].bar);
1310                 return -1;
1311         }
1312         print("#A%d: hda mem %p irq %d\n", ctlr->no, ctlr->mem, irq);
1313
1314         if(hdastart(ctlr) < 0){
1315                 print("#A%d: unable to start hda\n", ctlr->no);
1316                 return -1;
1317         }
1318         if(streamalloc(ctlr) < 0){
1319                 print("#A%d: unable to allocate stream buffer\n", ctlr->no);
1320                 return -1;
1321         }
1322         if(enumdev(ctlr) < 0){
1323                 print("#A%d: no audio codecs found\n", ctlr->no);
1324                 return -1;
1325         }
1326         print("#A%d: using codec #%d, vendor %08x\n",
1327                 ctlr->no, ctlr->codec.id.codec, ctlr->codec.vid);
1328         
1329         best = bestpin(ctlr);
1330         if(best < 0){
1331                 print("#A%d: no output pins found!\n", ctlr->no);
1332                 return -1;
1333         }
1334         if(connectpin(ctlr, best) < 0){
1335                 print("#A%d: error connecting pin\n", ctlr->no);
1336                 return -1;
1337         }
1338
1339         adev->write = hdawrite;
1340         adev->close = hdaclose;
1341         adev->buffered = hdabuffered;
1342         adev->volread = hdavolread;
1343         adev->volwrite = hdavolwrite;
1344         adev->status = hdastatus;
1345         adev->ctl = hdactl;
1346         
1347         intrenable(irq, hdainterrupt, adev, tbdf, "hda");
1348         
1349         return 0;
1350 }
1351
1352 void
1353 audiohdalink(void)
1354 {
1355         addaudiocard("hda", hdareset);
1356 }