On Tue, Feb 08, 2011 at 04:08:53AM +0000, Jacob Meuser wrote: > widget connection lists are described as a series of nids. the widget > gives the number of connection list entries. if the most significant > bit of a connection list entry is set, all nids between the last nid > in the connection list and the nid in the entry with the most significant > bit set are connected. > > this means the number of connected nids isn't necessarily the same as > the number of connection list entries. azalia currently assumes they > are the same, and ends up ignoring some of the connections, which leads > to widgets being considered unusable. > > the following runs through the connection list entries twice. the > first is to get the number of connections. then once the number of > connections is known, an array to hold the nids of the connected > widgets is allocated. then loop over the connection list entries > again and insert the nids into the array. > > this affects almost all Analog Devices (AD198x) and Sigmatel/IDT > (STAC*/92HD*) codecs. > > please test and report to me. please include a dmesg and a diff of > 'mixerctl -v' output from before and after applying the diff. thanks.
two positive reports for IDT codecs. but I have not yet heard from anyone with an analog devices codec. -- jake...@sdf.lonestar.org SDF Public Access UNIX System - http://sdf.lonestar.org Index: azalia.c =================================================================== RCS file: /cvs/src/sys/dev/pci/azalia.c,v retrieving revision 1.188 diff -u -p azalia.c --- azalia.c 12 Sep 2010 03:17:34 -0000 1.188 +++ azalia.c 9 Feb 2011 05:09:50 -0000 @@ -3330,7 +3330,7 @@ azalia_widget_init_connection(widget_t *this, const co uint32_t result; int err; int i, j, k; - int length, bits, conn, last; + int length, nconn, bits, conn, last; this->selected = -1; if ((this->widgetcap & COP_AWCAP_CONNLIST) == 0) @@ -3349,32 +3349,59 @@ azalia_widget_init_connection(widget_t *this, const co if (length == 0) return 0; - this->nconnections = length; - this->connections = malloc(sizeof(nid_t) * length, M_DEVBUF, M_NOWAIT); + /* + * 'length' is the number of entries, not the number of + * connections. Find the number of connections, 'nconn', so + * enough space can be allocated for the list of connected + * nids. + */ + nconn = last = 0; + for (i = 0; i < length;) { + err = azalia_comresp(codec, this->nid, + CORB_GET_CONNECTION_LIST_ENTRY, i, &result); + if (err) + return err; + for (k = 0; i < length && (k < 32 / bits); k++) { + conn = (result >> (k * bits)) & ((1 << bits) - 1); + /* If high bit is set, this is the end of a continuous + * list that started with the last connection. + */ + if ((nconn > 0) && (conn & (1 << (bits - 1)))) + nconn += (conn & ~(1 << (bits - 1))) - last; + else + nconn++; + last = conn; + i++; + } + } + + this->connections = malloc(sizeof(nid_t) * nconn, M_DEVBUF, M_NOWAIT); if (this->connections == NULL) { printf("%s: out of memory\n", XNAME(codec->az)); return ENOMEM; } - for (i = 0; i < length;) { + for (i = 0; i < nconn;) { err = azalia_comresp(codec, this->nid, CORB_GET_CONNECTION_LIST_ENTRY, i, &result); if (err) return err; - for (k = 0; i < length && (k < 32 / bits); k++) { + for (k = 0; i < nconn && (k < 32 / bits); k++) { conn = (result >> (k * bits)) & ((1 << bits) - 1); /* If high bit is set, this is the end of a continuous * list that started with the last connection. */ if ((i > 0) && (conn & (1 << (bits - 1)))) { - last = this->connections[i - 1]; - for (j = 1; i < length && j <= conn - last; j++) + for (j = 1; i < nconn && j <= conn - last; j++) this->connections[i++] = last + j; } else { this->connections[i++] = conn; } + last = conn; } } - if (length > 0) { + this->nconnections = nconn; + + if (nconn > 0) { err = azalia_comresp(codec, this->nid, CORB_GET_CONNECTION_SELECT_CONTROL, 0, &result); if (err)