2 #include "../port/lib.h"
7 #include "../port/error.h"
8 #include "../port/audioif.h"
10 typedef struct Codec Codec;
11 typedef struct Ctlr Ctlr;
12 typedef struct Bld Bld;
13 typedef struct Ring Ring;
15 typedef struct Widget Widget;
16 typedef struct Codec Codec;
17 typedef struct Fungroup Fungroup;
18 typedef struct Pinprop Pinprop;
66 /* stream register base */
75 /* Warning: Sdctl is 24bit register */
76 Sdctl = Sdoutput0 + 0x00,
84 Sdsts = Sdoutput0 + 0x03,
89 Sdlpib = Sdoutput0 + 0x04,
90 Sdcbl = Sdoutput0 + 0x08,
91 Sdlvi = Sdoutput0 + 0x0c,
92 Sdfifow = Sdoutput0 + 0x0e,
93 Sdfifos = Sdoutput0 + 0x10,
94 Sdfmt = Sdoutput0 + 0x12,
103 Sdbdplo = Sdoutput0 + 0x18,
104 Sdbdphi = Sdoutput0 + 0x1c,
108 Bufsize = 64 * 1024 * 4,
110 Blocksize = Bufsize / Nblocks,
113 Maxrirbwait = 1000, /* microseconds */
114 Maxwaitup = 500, /* microseconds */
115 Codecdelay = 1000, /* microseconds */
119 /* 12-bit cmd + 8-bit payload */
178 Getunsolresp = 0xf08,
179 Setunsolresp = 0x708,
193 /* 4-bit cmd + 16-bit payload */
224 uint size, blocksize;
249 uint convrate, convfmt;
269 Widget *widgets[Maxwidgets];
277 Lock; /* interrupt lock */
278 QLock; /* command lock */
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])
308 static char *pinport[] = {
315 static char *pinfunc[] = {
335 static char *pincol[] = {
354 static char *pinloc[] = {
373 static char *pinloc2[] = {
383 waitup8(Ctlr *ctlr, int reg, uchar mask, uchar set)
386 for(i=0; i<Maxwaitup; i++){
387 if((csr8(ctlr, reg) & mask) == set)
391 print("#A%d: waitup timeout for reg=%x, mask=%x, set=%x\n",
392 ctlr->no, reg, mask, set);
397 waitup16(Ctlr *ctlr, int reg, ushort mask, ushort set)
400 for(i=0; i<Maxwaitup; i++){
401 if((csr16(ctlr, reg) & mask) == set)
405 print("#A%d: waitup timeout for reg=%x, mask=%x, set=%x\n",
406 ctlr->no, reg, mask, set);
411 waitup32(Ctlr *ctlr, int reg, uint mask, uint set)
414 for(i=0; i<Maxwaitup; i++){
415 if((csr32(ctlr, reg) & mask) == set)
419 print("#A%d: waitup timeout for reg=%x, mask=%x, set=%x\n",
420 ctlr->no, reg, mask, set);
425 hdacmd(Ctlr *ctlr, uint request, uint reply[2])
431 re = csr16(ctlr, Rirbwp);
432 rp = csr16(ctlr, Corbrp);
433 wp = (csr16(ctlr, Corbwp) + 1) % ctlr->corbsize;
435 print("#A%d: corb full\n", ctlr->no);
438 ctlr->corb[wp] = request;
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);
453 cmderr(Id id, uint verb, uint par, uint *ret)
456 q = (id.codec << 28) | (id.nid << 20);
457 if((verb & 0x700) == 0x700)
458 q |= (verb << 8) | par;
460 q |= (verb << 16) | par;
461 if(hdacmd(id.ctlr, q, w) != 1)
470 cmd(Id id, uint verb, uint par)
473 if(cmderr(id, verb, par, w) == -1)
479 newnid(Id id, uint nid)
486 getoutamprange(Widget *w)
489 r = cmd(w->id, Getparm, Outampcap);
490 return (r >> 8) & 0x7f;
494 getoutamp(Widget *w, int vol[2])
497 if((w->cap & Woutampcap) == 0)
499 vol[0] = cmd(w->id, Getamp, Agetout | Agetleft) & Againmask;
500 vol[1] = cmd(w->id, Getamp, Agetout | Agetright) & Againmask;
503 /* vol is 0...range or nil for 0dB; mute is 0/1 */
505 setoutamp(Widget *w, int mute, int *vol)
510 if((w->cap & Woutampcap) == 0)
513 r = cmd(w->id, Getparm, Outampcap);
517 q = Asetout | (i == 0 ? Asetleft : Asetright);
521 q |= zerodb << Again;
523 q |= vol[i] << Again;
524 cmd(w->id, Setamp, q);
528 /* vol is 0...range or nil for 0dB; mute is 0/1; in is widget or nil for all */
530 setinamp(Widget *w, Widget *in, int mute, int *vol)
535 if((w->cap & Winampcap) == 0)
538 r = cmd(w->id, Getparm, Inampcap);
542 q = Asetin | (i == 0 ? Asetleft : Asetright);
546 q |= zerodb << Again;
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));
557 findpath(Widget *src)
559 Widget *q[Maxwidgets];
565 for(w=src->fg->first; w; w=w->next)
573 for(i=0; i<w->nlist; i++){
587 connectpath(Widget *src, Widget *dst, uint stream)
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);
597 for(w=dst; w != src; w=v){
599 setoutamp(w, 0, nil);
600 setinamp(v, w, 0, nil);
601 if(v->type == Waout || v->type == Wamix)
605 for(i=0; i < v->nlist && v->list[i] != w; i++)
607 cmd(v->id, Setconn, i);
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);
619 uint r, i, mask, bits, nlist;
622 ws = w->fg->codec->widgets;
623 r = cmd(w->id, Getparm, Connlistlen);
624 bits = (r & 0x80) == 0 ? 8 : 16;
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];
638 enumwidget(Widget *w)
640 w->cap = cmd(w->id, Getparm, Widgetcap);
641 w->type = (w->cap >> 20) & 0x7;
647 w->pin = cmd(w->id, Getdefault, 0);
648 w->pincap = cmd(w->id, Getparm, Pincap);
654 enumfungroup(Codec *codec, Id id)
660 r = cmd(id, Getparm, Fungrtype) & 0x7f;
664 fg = mallocz(sizeof *fg, 1);
669 r = cmd(id, Getparm, Subnodecnt);
671 base = (r >> 8) & 0xff;
673 if(base + n > Maxwidgets)
676 for(i=n, next=nil; i--; next=w){
677 w = mallocz(sizeof(Widget), 1);
678 w->id = newnid(id, base + i);
681 codec->widgets[base + i] = w;
686 enumwidget(codec->widgets[base + i]);
693 enumcodec(Codec *codec, Id id)
700 if(cmderr(id, Getparm, Vendorid, &vid) < 0)
702 if(cmderr(id, Getparm, Revid, &rid) < 0)
709 r = cmd(id, Getparm, Subnodecnt);
711 base = (r >> 16) & 0xff;
714 fg = enumfungroup(codec, newnid(id, base + i));
717 fg->next = codec->fgroup;
720 if(codec->fgroup == nil)
733 for(i=0; i<Maxcodecs; i++){
735 if(enumcodec(&ctlr->codec, id) == 0)
742 connectpin(Ctlr *ctlr, uint pin)
746 src = ctlr->codec.widgets[pin];
749 if(src->type != Wpin)
751 if((src->pincap & Pout) == 0)
757 connectpath(src, dst, Streamtag);
769 int best, pin, score;
774 for(fg=ctlr->codec.fgroup; fg; fg=fg->next){
775 for(w=fg->first; w; w=w->next){
778 if((w->pincap & Pout) == 0)
782 if(((r >> 12) & 0xf) == 4) /* green */
784 if(((r >> 24) & 0xf) == 1) /* rear */
786 if(((r >> 28) & 0x3) == 0) /* ext */
788 if(((r >> 20) & 0xf) == 2) /* hpout */
790 if(((r >> 20) & 0xf) == 0) /* lineout */
804 memset(r->buf, 0, r->size);
813 return (r->wp - r->rp) % r->size;
819 return r->size - r->blocksize - ringused(r);
825 return (r->rp - r->cp) % r->size;
831 r->wp += r->blocksize - 1;
832 r->wp -= r->wp % r->blocksize;
837 ringwrite(Ring *r, uchar *ap, uint n)
852 memset(r->buf + r->cp, 0, a);
853 r->cp = (r->cp + a) % r->size;
861 memmove(r->buf + r->wp, p, a);
862 r->wp = (r->wp + a) % r->size;
871 ringupdate(Ring *r, uint np)
885 if((np - rp) % s >= (wp - rp) % s)
891 streamalloc(Ctlr *ctlr)
900 r->blocksize = Blocksize;
901 r->buf = xspanalloc(r->size, 128, 0);
907 ctlr->atag = Streamtag;
908 ctlr->afmt = Fmtstereo | Fmtsampw | Fmtdiv1 |
909 Fmtmul1 | Fmtbase441;
911 ctlr->blds = xspanalloc(Nblocks * sizeof(Bld), 128, 0);
912 if(ctlr->blds == nil)
916 for(i=0; i<Nblocks; i++){
917 b->addrlo = PADDR(p);
928 streamstart(Ctlr *ctlr)
930 Ring *r = &ctlr->ring;
933 csr8(ctlr, Sdctl) = Srst;
934 waitup8(ctlr, Sdctl, Srst, Srst);
935 csr8(ctlr, Sdctl) = 0;
936 waitup8(ctlr, Sdctl, Srst, 0);
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;
945 /* enable global intrs for this stream */
946 csr32(ctlr, Intctl) |= (1 << Streamno);
948 /* enable stream intrs */
949 csr32(ctlr, Sdctl) = (ctlr->atag << Stagbit) | Srun | Scie | Seie | Sdie;
950 waitup32(ctlr, Sdctl, Srun, Srun);
952 /* mark as running */
957 streamstop(Ctlr *ctlr)
959 /* disble stream intrs */
960 csr32(ctlr, Sdctl) = 0;
962 /* disable global intrs for this stream */
963 csr32(ctlr, Intctl) &= ~(1 << Streamno);
965 /* mark as stopped */
971 streamupdate(Ctlr *ctlr)
978 /* ack interrupt and wake writer */
979 csr8(ctlr, Sdsts) |= 0x4;
981 pos = csr32(ctlr, Sdlpib);
984 if(ringupdate(r, pos) == 1)
991 return ringavail(arg) > 0;
998 int delay = ctlr->adev->delay*4;
999 return (delay <= 0) || (ringused(&ctlr->ring) <= delay) || (ctlr->active == 0);
1003 checkptr(Ctlr *ctlr)
1008 if(ctlr->active == 1)
1019 Ring *r = &ctlr->ring;
1022 if(ctlr->active == 0){
1023 if(ringused(r) >= r->blocksize){
1033 hdabuffered(Audio *adev)
1037 return ringused(&ctlr->ring);
1041 hdactl(Audio *adev, void *va, long n, vlong)
1043 char *p, *e, *x, *tok[4];
1051 for(; p < e; p = x){
1052 if(x = strchr(p, '\n'))
1056 ntok = tokenize(p, tok, 4);
1059 if(cistrcmp(tok[0], "pin") == 0 && ntok == 2){
1060 connectpin(ctlr, strtoul(tok[1], 0, 0));
1068 hdawrite(Audio *adev, void *vp, long vn, vlong)
1082 k = ringwrite(r, p, n);
1083 if(checkptr(ctlr) == 0)
1087 sleep(&ctlr->outr, outavail, r);
1094 sleep(&ctlr->outr, outrate, ctlr);
1099 hdaclose(Audio *adev)
1103 ringalign(&ctlr->ring);
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,
1122 hdagetvol(Audio *adev, int x, int a[2])
1124 Ctlr *ctlr = adev->ctlr;
1128 if(ctlr->amp != nil)
1129 getoutamp(ctlr->amp, a);
1142 hdasetvol(Audio *adev, int x, int a[2])
1144 Ctlr *ctlr = adev->ctlr;
1148 if(ctlr->amp != nil)
1149 setoutamp(ctlr->amp, 0, a);
1162 fillvoltab(Ctlr *ctlr, Volume *vt)
1164 memmove(vt, voltab, sizeof(voltab));
1165 if(ctlr->amp != nil)
1166 vt[Vmaster].range = getoutamprange(ctlr->amp);
1170 hdavolread(Audio *adev, void *a, long n, vlong)
1172 Volume voltab[Nvol+1];
1173 fillvoltab(adev->ctlr, voltab);
1174 return genaudiovolread(adev, a, n, 0, voltab, hdagetvol, 0);
1178 hdavolwrite(Audio *adev, void *a, long n, vlong)
1180 Volume voltab[Nvol+1];
1181 fillvoltab(adev->ctlr, voltab);
1182 return genaudiovolwrite(adev, a, n, 0, voltab, hdasetvol, 0);
1186 hdainterrupt(Ureg *, void *arg)
1196 sts = csr32(ctlr, Intsts);
1203 hdastatus(Audio *adev, void *a, long n, vlong)
1205 Ctlr *ctlr = adev->ctlr;
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);
1217 for(fg=ctlr->codec.fgroup; fg; fg=fg->next){
1218 for(w=fg->first; w; w=w->next){
1222 k += snprint(s+k, n-k, "pin %3d %s %s %s %s %s %s\n",
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]
1239 hdastart(Ctlr *ctlr)
1241 static int cmdbufsize[] = { 2, 16, 256, 2048 };
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);
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);
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);
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);
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);
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);
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);
1296 /* enable interrupts */
1297 csr32(ctlr, Intctl) = Gie | Cie;
1305 while(p = pcimatch(p, 0, 0))
1306 switch((p->vid << 16) | p->did){
1307 case (0x8086 << 16) | 0x27d8:
1314 hdacmdread(Chan *, void *a, long n, vlong)
1323 return qread(ctlr->q, a, n);
1327 hdacmdwrite(Chan *, void *a, long n, vlong)
1341 for(i=0; i<n/4; i++){
1342 if(hdacmd(ctlr, lp[i], w) <= 0){
1346 qproduce(ctlr->q, w, sizeof(w));
1353 hdareset(Audio *adev)
1355 static Ctlr *cards = nil;
1357 int irq, tbdf, best;
1360 /* make a list of all ac97 cards if not already done */
1363 while(p = hdamatch(p)){
1364 ctlr = xspanalloc(sizeof(Ctlr), 8, 0);
1365 memset(ctlr, 0, sizeof(Ctlr));
1372 /* pick a card from the list */
1373 for(ctlr = cards; ctlr; ctlr = ctlr->next){
1374 if(p = ctlr->pcidev){
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);
1399 print("#A%d: hda mem %p irq %d\n", ctlr->no, ctlr->mem, irq);
1401 if(hdastart(ctlr) < 0){
1402 print("#A%d: unable to start hda\n", ctlr->no);
1405 if(streamalloc(ctlr) < 0){
1406 print("#A%d: unable to allocate stream buffer\n", ctlr->no);
1409 if(enumdev(ctlr) < 0){
1410 print("#A%d: no audio codecs found\n", ctlr->no);
1413 print("#A%d: using codec #%d, vendor %08x\n",
1414 ctlr->no, ctlr->codec.id.codec, ctlr->codec.vid);
1416 best = bestpin(ctlr);
1418 print("#A%d: no output pins found!\n", ctlr->no);
1421 if(connectpin(ctlr, best) < 0){
1422 print("#A%d: error connecting pin\n", ctlr->no);
1426 adev->write = hdawrite;
1427 adev->close = hdaclose;
1428 adev->buffered = hdabuffered;
1429 adev->volread = hdavolread;
1430 adev->volwrite = hdavolwrite;
1431 adev->status = hdastatus;
1434 intrenable(irq, hdainterrupt, adev, tbdf, "hda");
1436 addarchfile("hdacmd", 0664, hdacmdread, hdacmdwrite);
1444 addaudiocard("hda", hdareset);