ALSA: hda - Fix multi-io channel mode management
authorTakashi Iwai <tiwai@suse.de>
Mon, 7 Jan 2013 15:44:06 +0000 (16:44 +0100)
committerTakashi Iwai <tiwai@suse.de>
Sat, 12 Jan 2013 07:44:08 +0000 (08:44 +0100)
The multi-io channels can vary not only from 1 to 6 but also may vary
from 6 to 8 or such.  At the same time, there are more speaker pins
available than the primary output pins.  So, we need three variables
to check: the minimum channel counts for primary outputs, the current
channel counts for primary outputs, and the minimum channel counts for
all outputs.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/pci/hda/hda_generic.c
sound/pci/hda/hda_generic.h

index 1fbc1b32359c9d41fb83014903f7763a743a201d..afa54f87b6916d16da74e560680db7b30ac84278 100644 (file)
@@ -1125,6 +1125,25 @@ static int check_aamix_out_path(struct hda_codec *codec, int path_idx)
        return snd_hda_get_path_idx(codec, path);
 }
 
+/* fill the empty entries in the dac array for speaker/hp with the
+ * shared dac pointed by the paths
+ */
+static void refill_shared_dacs(struct hda_codec *codec, int num_outs,
+                              hda_nid_t *dacs, int *path_idx)
+{
+       struct nid_path *path;
+       int i;
+
+       for (i = 0; i < num_outs; i++) {
+               if (dacs[i])
+                       continue;
+               path = snd_hda_get_path_from_idx(codec, path_idx[i]);
+               if (!path)
+                       continue;
+               dacs[i] = path->path[0];
+       }
+}
+
 /* fill in the dac_nids table from the parsed pin configuration */
 static int fill_and_eval_dacs(struct hda_codec *codec,
                              bool fill_hardwired,
@@ -1183,19 +1202,6 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
                                   spec->private_dac_nids, spec->out_paths,
                                   &main_out_badness);
 
-       /* re-count num_dacs and squash invalid entries */
-       spec->multiout.num_dacs = 0;
-       for (i = 0; i < cfg->line_outs; i++) {
-               if (spec->private_dac_nids[i])
-                       spec->multiout.num_dacs++;
-               else {
-                       memmove(spec->private_dac_nids + i,
-                               spec->private_dac_nids + i + 1,
-                               sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
-                       spec->private_dac_nids[cfg->line_outs - 1] = 0;
-               }
-       }
-
        if (fill_mio_first &&
            cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
                /* try to fill multi-io first */
@@ -1246,16 +1252,41 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
                if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2)
                        spec->multi_ios = 1; /* give badness */
 
+       /* re-count num_dacs and squash invalid entries */
+       spec->multiout.num_dacs = 0;
+       for (i = 0; i < cfg->line_outs; i++) {
+               if (spec->private_dac_nids[i])
+                       spec->multiout.num_dacs++;
+               else {
+                       memmove(spec->private_dac_nids + i,
+                               spec->private_dac_nids + i + 1,
+                               sizeof(hda_nid_t) * (cfg->line_outs - i - 1));
+                       spec->private_dac_nids[cfg->line_outs - 1] = 0;
+               }
+       }
+
+       spec->ext_channel_count = spec->min_channel_count =
+               spec->multiout.num_dacs;
+
        if (spec->multi_ios == 2) {
                for (i = 0; i < 2; i++)
                        spec->private_dac_nids[spec->multiout.num_dacs++] =
                                spec->multi_io[i].dac;
-               spec->ext_channel_count = 2;
        } else if (spec->multi_ios) {
                spec->multi_ios = 0;
                badness += BAD_MULTI_IO;
        }
 
+       /* re-fill the shared DAC for speaker / headphone */
+       if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+               refill_shared_dacs(codec, cfg->hp_outs,
+                                  spec->multiout.hp_out_nid,
+                                  spec->hp_paths);
+       if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+               refill_shared_dacs(codec, cfg->speaker_outs,
+                                  spec->multiout.extra_out_nid,
+                                  spec->speaker_paths);
+
        return badness;
 }
 
@@ -1610,14 +1641,15 @@ static int ch_mode_info(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct hda_gen_spec *spec = codec->spec;
+       int chs;
 
        uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
        uinfo->count = 1;
        uinfo->value.enumerated.items = spec->multi_ios + 1;
        if (uinfo->value.enumerated.item > spec->multi_ios)
                uinfo->value.enumerated.item = spec->multi_ios;
-       sprintf(uinfo->value.enumerated.name, "%dch",
-               (uinfo->value.enumerated.item + 1) * 2);
+       chs = uinfo->value.enumerated.item * 2 + spec->min_channel_count;
+       sprintf(uinfo->value.enumerated.name, "%dch", chs);
        return 0;
 }
 
@@ -1626,7 +1658,8 @@ static int ch_mode_get(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct hda_gen_spec *spec = codec->spec;
-       ucontrol->value.enumerated.item[0] = (spec->ext_channel_count - 1) / 2;
+       ucontrol->value.enumerated.item[0] =
+               (spec->ext_channel_count - spec->min_channel_count) / 2;
        return 0;
 }
 
@@ -1674,9 +1707,9 @@ static int ch_mode_put(struct snd_kcontrol *kcontrol,
        ch = ucontrol->value.enumerated.item[0];
        if (ch < 0 || ch > spec->multi_ios)
                return -EINVAL;
-       if (ch == (spec->ext_channel_count - 1) / 2)
+       if (ch == (spec->ext_channel_count - spec->min_channel_count) / 2)
                return 0;
-       spec->ext_channel_count = (ch + 1) * 2;
+       spec->ext_channel_count = ch * 2 + spec->min_channel_count;
        for (i = 0; i < spec->multi_ios; i++)
                set_multi_io(codec, i, i < ch);
        spec->multiout.max_channels = max(spec->ext_channel_count,
@@ -3127,17 +3160,16 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
        if (err < 0)
                return err;
 
-       /* check the multiple speaker pins */
-       if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
-               spec->const_channel_count = cfg->line_outs * 2;
-       else
-               spec->const_channel_count = cfg->speaker_outs * 2;
-
-       if (spec->multi_ios > 0)
-               spec->multiout.max_channels = max(spec->ext_channel_count,
-                                                 spec->const_channel_count);
-       else
-               spec->multiout.max_channels = spec->multiout.num_dacs * 2;
+       spec->const_channel_count = spec->ext_channel_count;
+       /* check the multiple speaker and headphone pins */
+       if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+               spec->const_channel_count = max(spec->const_channel_count,
+                                               cfg->speaker_outs * 2);
+       if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+               spec->const_channel_count = max(spec->const_channel_count,
+                                               cfg->hp_outs * 2);
+       spec->multiout.max_channels = max(spec->ext_channel_count,
+                                         spec->const_channel_count);
 
        err = check_auto_mute_availability(codec);
        if (err < 0)
index 00a1eab2c0b266ecd06d1360e5f7c3c81434ddb4..b65769cbde2be030c36902b9d776de7ebe6f6f07 100644 (file)
@@ -117,8 +117,20 @@ struct hda_gen_spec {
        unsigned int cur_mux[3];
 
        /* channel model */
-       int const_channel_count;        /* min. channel count (for speakers) */
-       int ext_channel_count;          /* current channel count for multi-io */
+       /* min_channel_count contains the minimum channel count for primary
+        * outputs.  When multi_ios is set, the channels can be configured
+        * between min_channel_count and (min_channel_count + multi_ios * 2).
+        *
+        * ext_channel_count contains the current channel count of the primary
+        * out.  This varies in the range above.
+        *
+        * Meanwhile, const_channel_count is the channel count for all outputs
+        * including headphone and speakers.  It's a constant value, and the
+        * PCM is set up as max(ext_channel_count, const_channel_count).
+        */
+       int min_channel_count;          /* min. channel count for primary out */
+       int ext_channel_count;          /* current channel count for primary */
+       int const_channel_count;        /* channel count for all */
 
        /* PCM information */
        struct hda_pcm pcm_rec[3];      /* used in build_pcms() */