the idea is that when a codec is capable of playing at least 6 channels, the output jacks should be connected to unique DACs by default. it's not possible to do multichannel playback if all the output jacks are connected to the same DAC.
it is also common for multichannel capable codecs to have "+2" capabilities. perhaps you have seen "7.1+2 audio" in motherboard literature. that means the device is capable of playing a 7.1 (8 channels, 2 each for front, rear, center/lfe, side; 5.1 is 6 channels, 2 each for front, rear, center/lfe; if it can to 7.1 it can do 5.1 as well) multichannel stream, and a separate stereo stream, concurrently. these codecs are usually found in desktop machines, and there will usually be a headphone jack in the front of the machine, in addition to several jacks in the rear. this diff tries to connect such a front headphone jack to a unique DAC, so it can be used to play back a stereo stream while a multichannel stream is also playing. however, if the front headphone DAC is not be used separately (which requires using aucat(1) in a multi-stream configuration, which I won't describe now as this is already getting long), it will be connected to the first stereo channels. in other words, if you are just playing some stereo music, both the "front" jack (usually green) on the rear panel, and the "headphone" jack (also usually green) on the front panel will get the music. testing this diff is actually pretty easy. just send me a complete dmesg and complete 'mixerctl -v output' after applying the diff, building the kernel and rebooting. I can figure out from that if the diff is doing what it's supposed to do. this needs a bit of testing. many laptops are actually capable of 5.1 playback, so although this mostly affects desktop systems, please test on laptops as well. thanks. -- 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.131 diff -u -p azalia.c --- azalia.c 12 May 2009 09:32:28 -0000 1.131 +++ azalia.c 17 May 2009 19:48:45 -0000 @@ -212,6 +212,7 @@ int azalia_codec_find_defadc_sub(codec_t *, nid_t, int int azalia_codec_init_volgroups(codec_t *); int azalia_codec_sort_pins(codec_t *); int azalia_codec_select_micadc(codec_t *); +int azalia_codec_select_dacs(codec_t *); int azalia_codec_select_spkrdac(codec_t *); int azalia_codec_find_inputmixer(codec_t *); @@ -1361,6 +1362,17 @@ azalia_codec_init(codec_t *this) if (err) return err; + /* If the codec can do multichannel, select different DACs for + * the output pins. Also select a unique DAC for the front + * headphone pin, if one exists. + */ + this->fhp_dac = -1; + if (this->na_dacs >= 3 && this->nopins >= 3) { + err = azalia_codec_select_dacs(this); + if (err) + return err; + } + err = azalia_codec_select_spkrdac(this); if (err) return err; @@ -1489,7 +1501,7 @@ azalia_codec_sort_pins(codec_t *this) struct io_pin opins[MAX_PINS], opins_d[MAX_PINS]; struct io_pin ipins[MAX_PINS], ipins_d[MAX_PINS]; int nopins, nopins_d, nipins, nipins_d; - int prio, add, nd, conv; + int prio, loc, add, nd, conv; int i, j, k; nopins = nopins_d = nipins = nipins_d = 0; @@ -1499,6 +1511,10 @@ azalia_codec_sort_pins(codec_t *this) if (!w->enable || w->type != COP_AWTYPE_PIN_COMPLEX) continue; + loc = 0; + if (this->na_dacs >= 3) + loc = CORB_CD_LOC_GEO(w->d.pin.config); + prio = w->d.pin.association << 4 | w->d.pin.sequence; conv = -1; @@ -1529,7 +1545,8 @@ azalia_codec_sort_pins(codec_t *this) if (add && nopins < MAX_PINS) { opins[nopins].nid = w->nid; opins[nopins].conv = conv; - opins[nopins].prio = prio | (nd << 8); + prio |= (nd << 8) | (loc << 9); + opins[nopins].prio = prio; nopins++; } } @@ -1695,6 +1712,80 @@ azalia_codec_sort_pins(codec_t *this) #undef MAX_PINS } +int +azalia_codec_select_dacs(codec_t *this) +{ + widget_t *w; + nid_t *convs; + int nconv, conv; + int i, j, k, err, isfhp; + + convs = malloc(this->na_dacs * sizeof(nid_t), M_DEVBUF, + M_NOWAIT | M_ZERO); + if (convs == NULL) + return(ENOMEM); + + nconv = 0; + for (i = 0; i < this->nopins; i++) { + isfhp = 0; + w = &this->w[this->opins[i].nid]; + + if (w->d.pin.device == CORB_CD_HEADPHONE && + CORB_CD_LOC_GEO(w->d.pin.config) == CORB_CD_FRONT) { + isfhp = 1; + } + + conv = this->opins[i].conv; + for (j = 0; j < nconv; j++) { + if (conv == convs[j]) + break; + } + if (j == nconv) { + convs[nconv++] = conv; + if (isfhp) + this->fhp_dac = conv; + if (nconv >= this->na_dacs) { + return(0); + } + } else { + /* find a different dac */ + conv = -1; + for (j = 0; j < w->nconnections; j++) { + if (!azalia_widget_enabled(this, + w->connections[j])) + continue; + conv = azalia_codec_find_defdac(this, + w->connections[j], 1); + if (conv == -1) + continue; + for (k = 0; k < nconv; k++) { + if (conv == convs[k]) + break; + } + if (k == nconv) + break; + } + if (j < w->nconnections && conv != -1) { + err = this->comresp(this, w->nid, + CORB_SET_CONNECTION_SELECT_CONTROL, j, 0); + if (err) + return(err); + w->selected = j; + this->opins[i].conv = conv; + if (isfhp) + this->fhp_dac = conv; + convs[nconv++] = conv; + if (nconv >= this->na_dacs) + return(0); + } + } + } + + free(convs, M_DEVBUF); + + return(0); +} + /* Connect the speaker to a DAC that no other output pin is connected * to by default. If that is not possible, connect to a DAC other * than the one the first output pin is connected to. @@ -2321,7 +2412,8 @@ azalia_codec_connect_stream(codec_t *this, int dir, ui stream_chan = (number << 4); if (curchan < nchan) { stream_chan |= curchan; - } else if (w->nid == this->spkr_dac) { + } else if (w->nid == this->spkr_dac || + w->nid == this->fhp_dac) { stream_chan |= 0; /* first channel(s) */ } else stream_chan = 0; /* idle stream */ Index: azalia.h =================================================================== RCS file: /cvs/src/sys/dev/pci/azalia.h,v retrieving revision 1.45 diff -u -p azalia.h --- azalia.h 12 May 2009 09:32:28 -0000 1.45 +++ azalia.h 17 May 2009 19:48:44 -0000 @@ -669,6 +669,7 @@ typedef struct codec_t { nid_t speaker; /* fixed (internal) speaker */ nid_t spkr_dac; nid_t input_mixer; + nid_t fhp_dac; int spkr_muters; int spkr_mute_method;