]> git.lizzy.rs Git - plan9front.git/blobdiff - sys/src/9/pc/audiohda.c
audiohda: add pci id for nvidia GM204
[plan9front.git] / sys / src / 9 / pc / audiohda.c
index 177c16b08a5cf62d91f766729ba8da9daca43bed..b8d3fc8d9596119c33cf4c7459aa9a0c213c5b4b 100644 (file)
@@ -568,6 +568,30 @@ setoutamp(Widget *w, int mute, int *vol)
        }
 }
 
+static uint
+getinamprange(Widget *w)
+{
+       uint r;
+
+       if((w->cap & Winampcap) == 0)
+               return 0;
+       if((w->cap & Wampovrcap) == 0)
+               r = cmd(w->fg->id, Getparm, Inampcap);
+       else
+               r = cmd(w->id, Getparm, Inampcap);
+       return (r >> 8) & 0x7f;
+}
+
+static void
+getinamp(Widget *w, int vol[2])
+{
+       vol[0] = vol[1] = 0;
+       if((w->cap & Winampcap) == 0)
+               return;
+       vol[0] = cmd(w->id, Getamp, Agetin | Agetleft) & Againmask;
+       vol[1] = cmd(w->id, Getamp, Agetin | Agetright) & Againmask;
+}
+
 /* vol is 0...range or nil for 0dB; mute is 0/1; in is widget or nil for all */
 static void
 setinamp(Widget *w, Widget *in, int mute, int *vol)
@@ -599,21 +623,44 @@ setinamp(Widget *w, Widget *in, int mute, int *vol)
 }
 
 static Widget *
-findpath(Widget *jack, int type)
+findpath(Widget *jack, int type, char *route)
 {
        Widget *q[Maxwidgets];
        uint l, r, i;
        Widget *w, *to;
+       Fungroup *fg;
+
+       fg = jack->fg;
 
        l = r = 0;
-       for(w=jack->fg->first; w != nil; w = w->next)
+       for(w=fg->first; w != nil; w = w->next)
                w->link = nil;
 
+       if(route != nil && *route != 0){
+               w = jack;
+               while(*route++ == ','){
+                       i = strtoul(route, &route, 0);
+                       if(i >= Maxwidgets)
+                               return nil;
+                       to = fg->codec->widgets[i];
+                       if(to == nil || to->fg != fg || to->link != nil)
+                               return nil;
+                       if(type == Waout)
+                               to->link = w;
+                       else
+                               w->link = to;
+                       w = to;
+               }
+               if(w == jack || w->type != type)
+                       w = nil;
+               return w;
+       }
+
        if(type == Waout){
                q[r++] = jack;
                jack->link = jack;
        } else {
-               for(w=jack->fg->first; w != nil; w = w->next)
+               for(w=fg->first; w != nil; w = w->next)
                        if(w->type == type){
                                q[r++] = w;
                                w->link = w;
@@ -652,11 +699,40 @@ disconnectpath(Widget *from, Widget *to)
                next = from->path;
                from->path = nil;
                setoutamp(from, 1, nil);
-               setinamp(next, from, 1, nil);
+               if(next != nil)
+                       setinamp(next, from, 1, nil);
        }
        setoutamp(to, 1, nil);
 }
 
+static void
+muteall(Ctlr *ctlr)
+{
+       Fungroup *fg;
+       Widget *w;
+       int i;
+
+       for(i=0; i<Maxcodecs; i++){
+               if(ctlr->codec[i] == nil)
+                       continue;
+               for(fg=ctlr->codec[i]->fgroup; fg; fg=fg->next){
+                       for(w=fg->first; w != nil; w=w->next){
+                               setinamp(w, nil, 1, nil);
+                               setoutamp(w, 1, nil);
+                               switch(w->type){
+                               case Wain:
+                               case Waout:
+                                       cmd(w->id, Setstream, 0);
+                                       break;
+                               case Wpin:
+                                       cmd(w->id, Setpinctl, 0);
+                                       break;
+                               }
+                       }
+               }
+       }
+}
+
 static void
 connectpath(Widget *from, Widget *to)
 {
@@ -667,14 +743,15 @@ connectpath(Widget *from, Widget *to)
                next = from->link;
                from->path = next;
                setoutamp(from, 0, nil);
-               setinamp(next, from, 0, nil);
-               if(next->nlist == 1)
-                       continue;
-               for(i=0; i < next->nlist; i++)
-                       if(next->list[i] == from){
-                               cmd(next->id, Setconn, i);      
-                               break;
+               if(next != nil){
+                       setinamp(next, from, 0, nil);
+                       for(i=0; i < next->nlist; i++){
+                               if(next->list[i] == from){
+                                       cmd(next->id, Setconn, i);      
+                                       break;
+                               }
                        }
+               }
        }
        setoutamp(to, 0, nil);
 }
@@ -702,7 +779,6 @@ addconn(Widget *w, uint nid)
                w->list = p;
        }
        w->list[w->nlist++] = src;
-       return;
 }
 
 static void
@@ -738,11 +814,10 @@ enumwidget(Widget *w)
 {
        w->cap = cmd(w->id, Getparm, Widgetcap);
        w->type = (w->cap >> 20) & 0x7;
-       if(w->cap & Wpwrcap)
+       if(w->cap & Wpwrcap){
                cmd(w->id, Setpower, 0);
-
-       enumconns(w);
-       
+               delay(10);
+       }       
        switch(w->type){
        case Wpin:
                w->pin = cmd(w->id, Getdefault, 0);
@@ -768,7 +843,19 @@ enumfungroup(Codec *codec, Id id)
 
        /* open eyes */
        cmd(id, Setpower, 0);
-       microdelay(100);
+       delay(10);
+
+       r = cmd(id, Getparm, Subnodecnt);
+       n = r & 0xff;
+       base = (r >> 16) & 0xff;
+       if(base >= Maxwidgets){
+               print("hda: enumfungroup: base %d out of range\n", base);
+               return nil;
+       }
+       if(base+n > Maxwidgets){
+               print("hda: enumfungroup: widgets %d - %d out of range\n", base, base+n);
+               n = Maxwidgets - base;
+       }
 
        fg = mallocz(sizeof *fg, 1);
        if(fg == nil){
@@ -780,17 +867,12 @@ Nomem:
        fg->id = id;
        fg->type = r;
 
-       r = cmd(id, Getparm, Subnodecnt);
-       n = r & 0xff;
-       base = (r >> 16) & 0xff;
-       
-       if(base + n > Maxwidgets){
-               free(fg);
-               return nil;
-       }
-
        tail = &fg->first;
        for(i=0; i<n; i++){
+               if(codec->widgets[base + i] != nil){
+                       print("hda: enumfungroup: duplicate widget %d\n", base + i);
+                       continue;
+               }
                w = mallocz(sizeof(Widget), 1);
                if(w == nil){
                        while(w = fg->first){
@@ -810,11 +892,12 @@ Nomem:
 
        for(i=0; i<n; i++)
                enumwidget(codec->widgets[base + i]);
+       for(i=0; i<n; i++)
+               enumconns(codec->widgets[base + i]);
 
        return fg;
 }
 
-
 static int
 enumcodec(Codec *codec, Id id)
 {
@@ -883,7 +966,7 @@ enumdev(Ctlr *ctlr)
 }
 
 static int
-connectpin(Ctlr *ctlr, Stream *s, int type, uint pin, uint cad)
+connectpin(Ctlr *ctlr, Stream *s, int type, uint pin, uint cad, char *route)
 {
        Widget *jack, *conv;
 
@@ -897,7 +980,7 @@ connectpin(Ctlr *ctlr, Stream *s, int type, uint pin, uint cad)
        if(jack->type != Wpin)
                return -1;
 
-       conv = findpath(jack, type);
+       conv = findpath(jack, type, route);
        if(conv == nil)
                return -1;
 
@@ -989,8 +1072,8 @@ bestpin(Ctlr *ctlr, int *pcad, int (*fscore)(Widget *))
        for(i=0; i<Maxcodecs; i++){
                if(ctlr->codec[i] == nil)
                        continue;
-               for(fg=ctlr->codec[i]->fgroup; fg; fg=fg->next){
-                       for(w=fg->first; w; w=w->next){
+               for(fg=ctlr->codec[i]->fgroup; fg != nil; fg=fg->next){
+                       for(w=fg->first; w != nil; w=w->next){
                                if(w->type != Wpin)
                                        continue;
                                score = (*fscore)(w);
@@ -1164,7 +1247,7 @@ streampos(Ctlr *ctlr, Stream *s)
 static long
 hdactl(Audio *adev, void *va, long n, vlong)
 {
-       char *p, *e, *x, *tok[4];
+       char *p, *e, *x, *route, *tok[4];
        int ntok;
        Ctlr *ctlr;
        uint pin, cad;
@@ -1174,6 +1257,7 @@ hdactl(Audio *adev, void *va, long n, vlong)
        e = p + n;
        
        for(; p < e; p = x){
+               route = nil;
                if(x = strchr(p, '\n'))
                        *x++ = 0;
                else
@@ -1183,18 +1267,18 @@ hdactl(Audio *adev, void *va, long n, vlong)
                        continue;
                if(cistrcmp(tok[0], "pin") == 0 && ntok >= 2){
                        cad = ctlr->sout.cad;
-                       pin = strtoul(tok[1], 0, 0);
+                       pin = strtoul(tok[1], &route, 0);
                        if(ntok > 2)
                                cad = strtoul(tok[2], 0, 0);
-                       if(connectpin(ctlr, &ctlr->sout, Waout, pin, cad) < 0)
+                       if(connectpin(ctlr, &ctlr->sout, Waout, pin, cad, route) < 0)
                                error("connectpin failed");
                }else
                if(cistrcmp(tok[0], "inpin") == 0 && ntok >= 2){
                        cad = ctlr->sin.cad;
-                       pin = strtoul(tok[1], 0, 0);
+                       pin = strtoul(tok[1], &route, 0);
                        if(ntok > 2)
                                cad = strtoul(tok[2], 0, 0);
-                       if(connectpin(ctlr, &ctlr->sin, Wain, pin, cad) < 0)
+                       if(connectpin(ctlr, &ctlr->sin, Wain, pin, cad, route) < 0)
                                error("connectpin failed");
                }else
                        error(Ebadctl);
@@ -1235,9 +1319,12 @@ hdabuffered(Audio *adev)
 static void
 hdakick(Ctlr *ctlr)
 {
+       int delay;
+
        if(ctlr->sout.active)
                return;
-       if(buffered(&ctlr->sout.ring) > Blocksize)
+       delay = ctlr->adev->delay*BytesPerSample;
+       if(buffered(&ctlr->sout.ring) >= delay)
                streamstart(ctlr, &ctlr->sout);
 }
 
@@ -1328,15 +1415,50 @@ static Volume voltab[] = {
        0
 };
 
+static Widget*
+findoutamp(Stream *s)
+{
+       Widget *w;
+
+       for(w = s->conv; w != nil; w = w->path){
+               if(w->cap & Woutampcap)
+                       return w;
+               if(w == s->jack)
+                       break;
+       }
+       return nil;
+}
+
+static Widget*
+findinamp(Stream *s)
+{
+       Widget *w, *p, *a;
+
+       a = nil;
+       for(p = nil, w = s->jack; w != nil; p = w, w = w->path){
+               w->link = p;    /* for setinamp */
+               if(w->cap & Winampcap)
+                       a = w;
+               if(w == s->conv)
+                       break;
+       }
+       return a;
+}
+
 static int
 hdagetvol(Audio *adev, int x, int a[2])
 {
        Ctlr *ctlr = adev->ctlr;
+       Widget *w;
 
        switch(x){
        case Vmaster:
-               if(ctlr->sout.conv != nil)
-                       getoutamp(ctlr->sout.conv, a);
+               if((w = findoutamp(&ctlr->sout)) != nil)
+                       getoutamp(w, a);
+               break;
+       case Vrecord:
+               if((w = findinamp(&ctlr->sin)) != nil)
+                       getinamp(w, a);
                break;
        case Vspeed:
                a[0] = adev->speed;
@@ -1352,21 +1474,28 @@ static int
 hdasetvol(Audio *adev, int x, int a[2])
 {
        Ctlr *ctlr = adev->ctlr;
+       Widget *w;
 
        switch(x){
        case Vmaster:
-               if(ctlr->sout.conv != nil)
-                       setoutamp(ctlr->sout.conv, 0, a);
+               if((w = findoutamp(&ctlr->sout)) != nil)
+                       setoutamp(w, 0, a);
                break;
        case Vrecord:
-               if(ctlr->sin.conv != nil)
-                       setinamp(ctlr->sin.conv, nil, 0, a);
+               if((w = findinamp(&ctlr->sin)) != nil)
+                       setinamp(w, w->link, 0, a);
                break;
        case Vspeed:
                adev->speed = a[0];
                break;
        case Vdelay:
-               adev->delay = a[0];
+               if(a[0] < Blocksize/BytesPerSample) {
+                       adev->delay = Blocksize/BytesPerSample;
+               } else if(a[0] > (ctlr->sout.ring.nbuf/BytesPerSample)-1) {
+                       adev->delay = (ctlr->sout.ring.nbuf/BytesPerSample)-1;
+               } else {
+                       adev->delay = a[0];
+               }
                break;
        }
        return 0;
@@ -1375,9 +1504,13 @@ hdasetvol(Audio *adev, int x, int a[2])
 static void
 fillvoltab(Ctlr *ctlr, Volume *vt)
 {
+       Widget *w;
+
        memmove(vt, voltab, sizeof(voltab));
-       if(ctlr->sout.conv != nil)
-               vt[Vmaster].range = getoutamprange(ctlr->sout.conv);
+       if((w = findoutamp(&ctlr->sout)) != nil)
+               vt[Vmaster].range = getoutamprange(w);
+       if((w = findinamp(&ctlr->sin)) != nil)
+               vt[Vrecord].range = getinamprange(w);
 }
 
 static long
@@ -1438,10 +1571,9 @@ hdastatus(Audio *adev, void *a, long n, vlong)
 {
        Ctlr *ctlr = adev->ctlr;
        Codec *codec;
-       Fungroup *fg;
        Widget *w;
        uint r;
-       int i;
+       int i, j, k;
        char *s, *e;
        
        s = a;
@@ -1450,15 +1582,16 @@ hdastatus(Audio *adev, void *a, long n, vlong)
        for(i=0; i<Maxcodecs; i++){
                if((codec = ctlr->codec[i]) == nil)
                        continue;
-               s = seprint(s, e, "codec %2d pin %3d inpin %3d\n",
+               s = seprint(s, e, "codec %d pin %d inpin %d\n",
                        codec->id.codec, ctlr->sout.pin, ctlr->sin.pin);
-               for(fg=codec->fgroup; fg; fg=fg->next){
-                       for(w=fg->first; w; w=w->next){
-                               if(w->type != Wpin)
-                                       continue;
+               for(j=0; j<Maxwidgets; j++){
+                       if((w = codec->widgets[j]) == nil)
+                               continue;
+                       switch(w->type){
+                       case Wpin:
                                r = w->pin;
-                               s = seprint(s, e, "pin %3d %s%s %s %s %s %s %s%s%s\n",
-                                       w->id.nid,
+                               s = seprint(s, e, "%s %d %s%s %s %s %s %s %s%s%s",
+                                       widtype[w->type&7], w->id.nid,
                                        (w->pincap & Pin) != 0 ? "in" : "",
                                        (w->pincap & Pout) != 0 ? "out" : "",
                                        pinport[(r >> 30) & 0x3],
@@ -1469,29 +1602,50 @@ hdastatus(Audio *adev, void *a, long n, vlong)
                                        (w->pincap & Phdmi) ? " hdmi" : "",
                                        (w->pincap & Peapd) ? " eapd" : ""
                                );
+                               break;
+                       default:
+                               s = seprint(s, e, "%s %d %lux",
+                                       widtype[w->type&7], w->id.nid,
+                                       (ulong)w->cap);
+                       }
+                       if(w->nlist > 0){
+                               s = seprint(s, e, " ← ");
+                               for(k=0; k<w->nlist; k++){
+                                       if(k > 0)
+                                               s = seprint(s, e, ", ");
+                                       if(w->list[k] != nil)
+                                               s = seprint(s, e, "%s %d", widtype[w->list[k]->type&7], w->list[k]->id.nid);
+                               }
                        }
+                       s = seprint(s, e, "\n");
                }
        }
 
-       s = seprint(s, e, "outpath ");
-       for(w=ctlr->sout.conv; w != nil; w = w->path){
-               s = seprint(s, e, "%s %3d %lux %lux %lux", widtype[w->type&7], w->id.nid,
-                       (ulong)w->cap, (ulong)w->pin, (ulong)w->pincap);
-               if(w == ctlr->sout.jack)
-                       break;
-               s = seprint(s, e, " → ");
+       if(ctlr->sout.conv != nil && ctlr->sout.jack != nil){
+               s = seprint(s, e, "outpath ");
+               for(w=ctlr->sout.conv; w != nil; w = w->path){
+                       s = seprint(s, e, "%s %d", widtype[w->type&7], w->id.nid);
+                       if(w == ctlr->sout.jack)
+                               break;
+                       s = seprint(s, e, " → ");
+               }
+               s = seprint(s, e, "\n");
+               if((w = findoutamp(&ctlr->sout)) != nil)
+                       s = seprint(s, e, "outamp %s %d\n", widtype[w->type&7], w->id.nid);
        }
-       s = seprint(s, e, "\n");
 
-       s = seprint(s, e, "inpath ");
-       for(w=ctlr->sin.jack; w != nil; w = w->path){
-               s = seprint(s, e, "%s %3d %lux %lux %lux", widtype[w->type&7], w->id.nid,
-                       (ulong)w->cap, (ulong)w->pin, (ulong)w->pincap);
-               if(w == ctlr->sin.conv)
-                       break;
-               s = seprint(s, e, " → ");
+       if(ctlr->sin.conv != nil && ctlr->sin.jack != nil){
+               s = seprint(s, e, "inpath ");
+               for(w=ctlr->sin.jack; w != nil; w = w->path){
+                       s = seprint(s, e, "%s %d", widtype[w->type&7], w->id.nid);
+                       if(w == ctlr->sin.conv)
+                               break;
+                       s = seprint(s, e, " → ");
+               }
+               s = seprint(s, e, "\n");
+               if((w = findinamp(&ctlr->sin)) != nil)
+                       s = seprint(s, e, "inamp %s %d\n", widtype[w->type&7], w->id.nid);
        }
-       s = seprint(s, e, "\n");
 
        return s - (char*)a;
 }
@@ -1592,6 +1746,12 @@ hdamatch(Pcidev *p)
                case (0x8086 << 16) | 0x080a:   /* Intel SCH (Oaktrail) */
                case (0x8086 << 16) | 0x1c20:   /* Intel PCH */
                case (0x8086 << 16) | 0x1e20:   /* Intel (Thinkpad x230t) */
+               case (0x8086 << 16) | 0x8c20:   /* Intel 8 Series/C220 Series */
+               case (0x8086 << 16) | 0x8ca0:   /* Intel 9 Series */
+               case (0x8086 << 16) | 0x9c20:   /* Intel 8 Series Lynx Point */
+               case (0x8086 << 16) | 0x9ca0:   /* Intel Wildcat Point */
+               case (0x8086 << 16) | 0xa170:   /* Intel Sunrise Point-H */
+               case (0x8086 << 16) | 0x3a6e:   /* Intel ICH10 */
 
                case (0x10de << 16) | 0x026c:   /* NVidia MCP51 (untested) */
                case (0x10de << 16) | 0x0371:   /* NVidia MCP55 (untested) */
@@ -1599,6 +1759,7 @@ hdamatch(Pcidev *p)
                case (0x10de << 16) | 0x03f0:   /* NVidia MCP61A (untested) */
                case (0x10de << 16) | 0x044a:   /* NVidia MCP65 (untested) */
                case (0x10de << 16) | 0x055c:   /* NVidia MCP67 (untested) */
+               case (0x10de << 16) | 0x0fbb:   /* NVidia GM204 (untested) */
 
                case (0x1002 << 16) | 0x437b:   /* ATI SB450 (untested) */
                case (0x1002 << 16) | 0x4383:   /* ATI SB600 */
@@ -1608,6 +1769,10 @@ hdamatch(Pcidev *p)
                case (0x1106 << 16) | 0x3288:   /* VIA (untested) */
                case (0x1039 << 16) | 0x7502:   /* SIS (untested) */
                case (0x10b9 << 16) | 0x5461:   /* ULI (untested) */
+
+               case (0x1022 << 16) | 0x780d:   /* AMD FCH Azalia Controller */
+
+               case (0x15ad << 16) | 0x1977:   /* Vmware */
                        return p;
                }
        return nil;
@@ -1676,7 +1841,7 @@ hdareset(Audio *adev)
        }
 
        /* pick a card from the list */
-       for(ctlr = cards; ctlr; ctlr = ctlr->next){
+       for(ctlr = cards; ctlr != nil; ctlr = ctlr->next){
                if(p = ctlr->pcidev){
                        ctlr->pcidev = nil;
                        goto Found;
@@ -1707,6 +1872,11 @@ Found:
                case 0x1e20:
                case 0x811b:    /* SCH */
                case 0x080a:
+               case 0x8c20:
+               case 0x8ca0:
+               case 0x9c20:
+               case 0x9ca0:
+               case 0xa170:
                        pcicfgw16(p, 0x78, pcicfgr16(p, 0x78) & ~0x800);
                }
        }
@@ -1757,17 +1927,18 @@ Found:
                print("#A%d: no audio codecs found\n", ctlr->no);
                return -1;
        }
+       muteall(ctlr);
 
        best = bestpin(ctlr, &cad, scoreout);
        if(best < 0)
                print("#A%d: no output pins found\n", ctlr->no);
-       else if(connectpin(ctlr, &ctlr->sout, Waout, best, cad) < 0)
+       else if(connectpin(ctlr, &ctlr->sout, Waout, best, cad, nil) < 0)
                print("#A%d: error connecting output pin\n", ctlr->no);
 
        best = bestpin(ctlr, &cad, scorein);
        if(best < 0)
                print("#A%d: no input pins found\n", ctlr->no);
-       else if(connectpin(ctlr, &ctlr->sin, Wain, best, cad) < 0)
+       else if(connectpin(ctlr, &ctlr->sin, Wain, best, cad, nil) < 0)
                print("#A%d: error connecting input pin\n", ctlr->no);
 
        adev->read = hdaread;