ALSA: hda - Fix conflicts between Loopback Mixing and Independent HP
authorTakashi Iwai <tiwai@suse.de>
Mon, 21 Jan 2013 14:11:25 +0000 (15:11 +0100)
committerTakashi Iwai <tiwai@suse.de>
Mon, 21 Jan 2013 14:11:25 +0000 (15:11 +0100)
This patch eventually fixes two issues:
- Handle the case where the primary output is a headphone and can have
  independent HP mode;
  so far we checked only the case where the headphone is the secondary
  output.

- Fix the conflict of HP independent mode and aamix mode;
  when switched to aamix mode, the DAC might be also switched to
  another widget shared with other outputs.  Then even if we disable
  the DAC for the original output, it doesn't change -- because the
  active route is from another (shared) DAC to HP pin through aamix.
  So, in such a case, we have to prohibit the switch to aamix for HP
  routes.

This fixes issues appearing on VT codecs.

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

index e26e8d3430f2f472e1318993d5a0bcdac4cce90f..6d1e843c6e8dfdb783208428f47d79872352aaa5 100644 (file)
@@ -1845,6 +1845,10 @@ static int indep_hp_get(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
+static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
+                              int nomix_path_idx, int mix_path_idx,
+                              int out_type);
+
 static int indep_hp_put(struct snd_kcontrol *kcontrol,
                        struct snd_ctl_elem_value *ucontrol)
 {
@@ -1860,11 +1864,31 @@ static int indep_hp_put(struct snd_kcontrol *kcontrol,
        }
 
        if (spec->indep_hp_enabled != select) {
+               hda_nid_t *dacp;
+               if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+                       dacp = &spec->private_dac_nids[0];
+               else
+                       dacp = &spec->multiout.hp_out_nid[0];
+
+               /* update HP aamix paths in case it conflicts with indep HP */
+               if (spec->have_aamix_ctl) {
+                       if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+                               update_aamix_paths(codec, spec->aamix_mode,
+                                                  spec->out_paths[0],
+                                                  spec->aamix_out_paths[0],
+                                                  spec->autocfg.line_out_type);
+                       else
+                               update_aamix_paths(codec, spec->aamix_mode,
+                                                  spec->hp_paths[0],
+                                                  spec->aamix_out_paths[1],
+                                                  AUTO_PIN_HP_OUT);
+               }
+
                spec->indep_hp_enabled = select;
                if (spec->indep_hp_enabled)
-                       spec->multiout.hp_out_nid[0] = 0;
+                       *dacp = 0;
                else
-                       spec->multiout.hp_out_nid[0] = spec->alt_dac_nid;
+                       *dacp = spec->alt_dac_nid;
                ret = 1;
        }
  unlock:
@@ -1884,16 +1908,21 @@ static const struct snd_kcontrol_new indep_hp_ctl = {
 static int create_indep_hp_ctls(struct hda_codec *codec)
 {
        struct hda_gen_spec *spec = codec->spec;
+       hda_nid_t dac;
 
        if (!spec->indep_hp)
                return 0;
-       if (!spec->multiout.hp_out_nid[0]) {
+       if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+               dac = spec->multiout.dac_nids[0];
+       else
+               dac = spec->multiout.hp_out_nid[0];
+       if (!dac) {
                spec->indep_hp = 0;
                return 0;
        }
 
        spec->indep_hp_enabled = false;
-       spec->alt_dac_nid = spec->multiout.hp_out_nid[0];
+       spec->alt_dac_nid = dac;
        if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl))
                return -ENOMEM;
        return 0;
@@ -2026,14 +2055,24 @@ static int loopback_mixing_get(struct snd_kcontrol *kcontrol,
 }
 
 static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
-                              int nomix_path_idx, int mix_path_idx)
+                              int nomix_path_idx, int mix_path_idx,
+                              int out_type)
 {
+       struct hda_gen_spec *spec = codec->spec;
        struct nid_path *nomix_path, *mix_path;
 
        nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx);
        mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx);
        if (!nomix_path || !mix_path)
                return;
+
+       /* if HP aamix path is driven from a different DAC and the
+        * independent HP mode is ON, can't turn on aamix path
+        */
+       if (out_type == AUTO_PIN_HP_OUT && spec->indep_hp_enabled &&
+           mix_path->path[0] != spec->alt_dac_nid)
+               do_mix = false;
+
        if (do_mix) {
                snd_hda_activate_path(codec, nomix_path, false, true);
                snd_hda_activate_path(codec, mix_path, true, true);
@@ -2054,11 +2093,14 @@ static int loopback_mixing_put(struct snd_kcontrol *kcontrol,
                return 0;
        spec->aamix_mode = val;
        update_aamix_paths(codec, val, spec->out_paths[0],
-                          spec->aamix_out_paths[0]);
+                          spec->aamix_out_paths[0],
+                          spec->autocfg.line_out_type);
        update_aamix_paths(codec, val, spec->hp_paths[0],
-                          spec->aamix_out_paths[1]);
+                          spec->aamix_out_paths[1],
+                          AUTO_PIN_HP_OUT);
        update_aamix_paths(codec, val, spec->speaker_paths[0],
-                          spec->aamix_out_paths[2]);
+                          spec->aamix_out_paths[2],
+                          AUTO_PIN_SPEAKER_OUT);
        return 1;
 }
 
@@ -2081,6 +2123,7 @@ static int create_loopback_mixing_ctl(struct hda_codec *codec)
                return 0;
        if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum))
                return -ENOMEM;
+       spec->have_aamix_ctl = 1;
        return 0;
 }
 
index 696b6068a889d2e89819f489b37f9032d4259a9a..9c63555b971cdced88b6e6fda5aa33e77352e873 100644 (file)
@@ -212,6 +212,7 @@ struct hda_gen_spec {
        unsigned int no_analog:1; /* digital I/O only */
        unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */
        unsigned int indep_hp_enabled:1; /* independent HP enabled */
+       unsigned int have_aamix_ctl:1;
 
        /* loopback mixing mode */
        bool aamix_mode;