diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index 4b1524a861f3..1485d871d628 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -133,6 +133,9 @@ static void parse_user_hints(struct hda_codec *codec) val = snd_hda_get_bool_hint(codec, "line_in_auto_switch"); if (val >= 0) spec->line_in_auto_switch = !!val; + val = snd_hda_get_bool_hint(codec, "auto_mute_via_amp"); + if (val >= 0) + spec->auto_mute_via_amp = !!val; val = snd_hda_get_bool_hint(codec, "need_dac_fix"); if (val >= 0) spec->need_dac_fix = !!val; @@ -808,6 +811,9 @@ static void resume_path_from_idx(struct hda_codec *codec, int path_idx) * Helper functions for creating mixer ctl elements */ +static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); + enum { HDA_CTL_WIDGET_VOL, HDA_CTL_WIDGET_MUTE, @@ -815,7 +821,15 @@ enum { }; static const struct snd_kcontrol_new control_templates[] = { HDA_CODEC_VOLUME(NULL, 0, 0, 0), - HDA_CODEC_MUTE(NULL, 0, 0, 0), + /* only the put callback is replaced for handling the special mute */ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .subdevice = HDA_SUBDEV_AMP_FLAG, + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = hda_gen_mixer_mute_put, /* replaced */ + .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), + }, HDA_BIND_MUTE(NULL, 0, 0, 0), }; @@ -922,6 +936,23 @@ static int add_stereo_sw(struct hda_codec *codec, const char *pfx, return add_sw_ctl(codec, pfx, cidx, chs, path); } +/* playback mute control with the software mute bit check */ +static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct hda_gen_spec *spec = codec->spec; + + if (spec->auto_mute_via_amp) { + hda_nid_t nid = get_amp_nid(kcontrol); + bool enabled = !((spec->mute_bits >> nid) & 1); + ucontrol->value.integer.value[0] &= enabled; + ucontrol->value.integer.value[1] &= enabled; + } + + return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); +} + /* any ctl assigned to the path with the given index? */ static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type) { @@ -3719,6 +3750,16 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, unsigned int val, oldval; if (!nid) break; + + if (spec->auto_mute_via_amp) { + if (mute) + spec->mute_bits |= (1ULL << nid); + else + spec->mute_bits &= ~(1ULL << nid); + set_pin_eapd(codec, nid, !mute); + continue; + } + oldval = snd_hda_codec_get_pin_target(codec, nid); if (oldval & PIN_IN) continue; /* no mute for inputs */ @@ -3786,6 +3827,10 @@ static void call_update_outputs(struct hda_codec *codec) spec->automute_hook(codec); else snd_hda_gen_update_outputs(codec); + + /* sync the whole vmaster slaves to reflect the new auto-mute status */ + if (spec->auto_mute_via_amp && !codec->bus->shutdown) + snd_ctl_sync_vmaster(spec->vmaster_mute.sw_kctl, false); } /* standard HP-automute helper */ diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h index 76200314ee95..e199a852388b 100644 --- a/sound/pci/hda/hda_generic.h +++ b/sound/pci/hda/hda_generic.h @@ -209,6 +209,7 @@ struct hda_gen_spec { unsigned int master_mute:1; /* master mute over all */ unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ unsigned int line_in_auto_switch:1; /* allow line-in auto switch */ + unsigned int auto_mute_via_amp:1; /* auto-mute via amp instead of pinctl */ /* parser behavior flags; set before snd_hda_gen_parse_auto_config() */ unsigned int suppress_auto_mute:1; /* suppress input jack auto mute */ @@ -237,6 +238,9 @@ struct hda_gen_spec { unsigned int have_aamix_ctl:1; unsigned int hp_mic_jack_modes:1; + /* additional mute flags (only effective with auto_mute_via_amp=1) */ + u64 mute_bits; + /* badness tables for output path evaluations */ const struct badness_table *main_out_badness; const struct badness_table *extra_out_badness;