audiohda: allow manual pin routing, more verbose audiostat, initial mute of al widgets
the automatic routing from jack to dac/adc sometimes gets us a path thats not audible. manually specifying a route path gets us arround these. the syntax is just a comma separated list of node ids in the "pin" and "inpin" audioctl commands instead of a single pin node id. to find alternative paths, audiostat now lists all the widgets; not just the pins; and ther input connections. initially mute all pins and amps of all function groups. connectpath() and disconnectpath() will mute and unmute the widgets as required later.
This commit is contained in:
parent
82362507f7
commit
da2d0ee780
2 changed files with 219 additions and 73 deletions
|
@ -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);
|
||||
|
@ -1328,15 +1412,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,15 +1471,16 @@ 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];
|
||||
|
@ -1375,9 +1495,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 +1562,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 +1573,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 +1593,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;
|
||||
}
|
||||
|
@ -1678,7 +1823,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;
|
||||
|
@ -1759,17 +1904,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;
|
||||
|
|
|
@ -24,7 +24,7 @@ struct Audiochan
|
|||
Audio *adev;
|
||||
|
||||
char *data;
|
||||
char buf[1024+1];
|
||||
char buf[4000+1];
|
||||
};
|
||||
|
||||
enum {
|
||||
|
|
Loading…
Reference in a new issue