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;

Reply via email to