Commit a1e908ed authored by Takashi Iwai's avatar Takashi Iwai

ALSA: hda - Fix conflicts between Loopback Mixing and Independent HP

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: default avatarTakashi Iwai <tiwai@suse.de>
parent f87498b6
...@@ -1845,6 +1845,10 @@ static int indep_hp_get(struct snd_kcontrol *kcontrol, ...@@ -1845,6 +1845,10 @@ static int indep_hp_get(struct snd_kcontrol *kcontrol,
return 0; 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, static int indep_hp_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
...@@ -1860,11 +1864,31 @@ static int indep_hp_put(struct snd_kcontrol *kcontrol, ...@@ -1860,11 +1864,31 @@ static int indep_hp_put(struct snd_kcontrol *kcontrol,
} }
if (spec->indep_hp_enabled != select) { 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; spec->indep_hp_enabled = select;
if (spec->indep_hp_enabled) if (spec->indep_hp_enabled)
spec->multiout.hp_out_nid[0] = 0; *dacp = 0;
else else
spec->multiout.hp_out_nid[0] = spec->alt_dac_nid; *dacp = spec->alt_dac_nid;
ret = 1; ret = 1;
} }
unlock: unlock:
...@@ -1884,16 +1908,21 @@ static const struct snd_kcontrol_new indep_hp_ctl = { ...@@ -1884,16 +1908,21 @@ static const struct snd_kcontrol_new indep_hp_ctl = {
static int create_indep_hp_ctls(struct hda_codec *codec) static int create_indep_hp_ctls(struct hda_codec *codec)
{ {
struct hda_gen_spec *spec = codec->spec; struct hda_gen_spec *spec = codec->spec;
hda_nid_t dac;
if (!spec->indep_hp) if (!spec->indep_hp)
return 0; 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; spec->indep_hp = 0;
return 0; return 0;
} }
spec->indep_hp_enabled = false; 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)) if (!snd_hda_gen_add_kctl(spec, NULL, &indep_hp_ctl))
return -ENOMEM; return -ENOMEM;
return 0; return 0;
...@@ -2026,14 +2055,24 @@ static int loopback_mixing_get(struct snd_kcontrol *kcontrol, ...@@ -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, 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; struct nid_path *nomix_path, *mix_path;
nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx); nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx);
mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx); mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx);
if (!nomix_path || !mix_path) if (!nomix_path || !mix_path)
return; 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) { if (do_mix) {
snd_hda_activate_path(codec, nomix_path, false, true); snd_hda_activate_path(codec, nomix_path, false, true);
snd_hda_activate_path(codec, mix_path, true, true); snd_hda_activate_path(codec, mix_path, true, true);
...@@ -2054,11 +2093,14 @@ static int loopback_mixing_put(struct snd_kcontrol *kcontrol, ...@@ -2054,11 +2093,14 @@ static int loopback_mixing_put(struct snd_kcontrol *kcontrol,
return 0; return 0;
spec->aamix_mode = val; spec->aamix_mode = val;
update_aamix_paths(codec, val, spec->out_paths[0], 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], 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], 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; return 1;
} }
...@@ -2081,6 +2123,7 @@ static int create_loopback_mixing_ctl(struct hda_codec *codec) ...@@ -2081,6 +2123,7 @@ static int create_loopback_mixing_ctl(struct hda_codec *codec)
return 0; return 0;
if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum)) if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum))
return -ENOMEM; return -ENOMEM;
spec->have_aamix_ctl = 1;
return 0; return 0;
} }
......
...@@ -212,6 +212,7 @@ struct hda_gen_spec { ...@@ -212,6 +212,7 @@ struct hda_gen_spec {
unsigned int no_analog:1; /* digital I/O only */ unsigned int no_analog:1; /* digital I/O only */
unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */ unsigned int dyn_adc_switch:1; /* switch ADCs (for ALC275) */
unsigned int indep_hp_enabled:1; /* independent HP enabled */ unsigned int indep_hp_enabled:1; /* independent HP enabled */
unsigned int have_aamix_ctl:1;
/* loopback mixing mode */ /* loopback mixing mode */
bool aamix_mode; bool aamix_mode;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment