shithub: riscv

Download patch

ref: da2d0ee780c1542b2cbcb1f7dbd808c59356ae46
parent: 82362507f758da7cd9e31eb4b5befbb76535a596
author: cinap_lenrek <cinap_lenrek@gmx.de>
date: Sun Sep 29 11:44:26 EDT 2013

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.

--- a/sys/src/9/pc/audiohda.c
+++ b/sys/src/9/pc/audiohda.c
@@ -568,6 +568,30 @@
 	}
 }
 
+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 @@
 }
 
 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,12 +699,41 @@
 		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)
 {
 	Widget *next;
@@ -667,14 +743,15 @@
 		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 @@
 		w->list = p;
 	}
 	w->list[w->nlist++] = src;
-	return;
 }
 
 static void
@@ -738,11 +814,10 @@
 {
 	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,8 +843,20 @@
 
 	/* 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){
 Nomem:
@@ -780,17 +867,12 @@
 	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 @@
 
 	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 @@
 }
 
 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 @@
 	if(jack->type != Wpin)
 		return -1;
 
-	conv = findpath(jack, type);
+	conv = findpath(jack, type, route);
 	if(conv == nil)
 		return -1;
 
@@ -989,8 +1072,8 @@
 	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 @@
 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 @@
 	e = p + n;
 	
 	for(; p < e; p = x){
+		route = nil;
 		if(x = strchr(p, '\n'))
 			*x++ = 0;
 		else
@@ -1183,18 +1267,18 @@
 			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,16 +1412,51 @@
 	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;
 		break;
@@ -1352,15 +1471,16 @@
 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 @@
 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 @@
 {
 	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 @@
 	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 @@
 					(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 @@
 	}
 
 	/* 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 @@
 		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;
--- a/sys/src/9/port/devaudio.c
+++ b/sys/src/9/port/devaudio.c
@@ -24,7 +24,7 @@
 	Audio *adev;
 
 	char *data;
-	char buf[1024+1];
+	char buf[4000+1];
 };
 
 enum {
--