diff options
author | Takashi Iwai <tiwai@suse.de> | 2013-06-24 10:00:21 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2013-06-25 02:13:51 -0400 |
commit | 7eebffd3f4328c6dc220521f14b384affdaf9427 (patch) | |
tree | cdf1a8acef8802984e3de1ee84d964ed5fa62215 /sound/pci/hda | |
parent | 1ca2f2ec9e74e9d6e398e09b6468b4462c6d6b6e (diff) |
ALSA: hda - Add auto_mute_via_amp flag to generic parser
Add a new flag, auto_mute_via_amp, to determine the behavior of the
headphone / line-out auto-mute. When this flag is set, the generic
driver mutes the speaker and line outputs via the amp mute of each
pin, instead of changing the pin control values.
This is introduced for devices that don't work expectedly with the pin
control values; for example, some devices are known to keep enabling
the speaker outputs no matter which pin control values are set on the
speaker pins.
The driver doesn't check actually whether the pins have the output amp
caps, but assumes that the proper mixer (mute) controls are created on
all these pins. If not the case, you can't use this flag for your
device.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda')
-rw-r--r-- | sound/pci/hda/hda_generic.c | 47 | ||||
-rw-r--r-- | sound/pci/hda/hda_generic.h | 4 |
2 files changed, 50 insertions, 1 deletions
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) | |||
133 | val = snd_hda_get_bool_hint(codec, "line_in_auto_switch"); | 133 | val = snd_hda_get_bool_hint(codec, "line_in_auto_switch"); |
134 | if (val >= 0) | 134 | if (val >= 0) |
135 | spec->line_in_auto_switch = !!val; | 135 | spec->line_in_auto_switch = !!val; |
136 | val = snd_hda_get_bool_hint(codec, "auto_mute_via_amp"); | ||
137 | if (val >= 0) | ||
138 | spec->auto_mute_via_amp = !!val; | ||
136 | val = snd_hda_get_bool_hint(codec, "need_dac_fix"); | 139 | val = snd_hda_get_bool_hint(codec, "need_dac_fix"); |
137 | if (val >= 0) | 140 | if (val >= 0) |
138 | spec->need_dac_fix = !!val; | 141 | spec->need_dac_fix = !!val; |
@@ -808,6 +811,9 @@ static void resume_path_from_idx(struct hda_codec *codec, int path_idx) | |||
808 | * Helper functions for creating mixer ctl elements | 811 | * Helper functions for creating mixer ctl elements |
809 | */ | 812 | */ |
810 | 813 | ||
814 | static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, | ||
815 | struct snd_ctl_elem_value *ucontrol); | ||
816 | |||
811 | enum { | 817 | enum { |
812 | HDA_CTL_WIDGET_VOL, | 818 | HDA_CTL_WIDGET_VOL, |
813 | HDA_CTL_WIDGET_MUTE, | 819 | HDA_CTL_WIDGET_MUTE, |
@@ -815,7 +821,15 @@ enum { | |||
815 | }; | 821 | }; |
816 | static const struct snd_kcontrol_new control_templates[] = { | 822 | static const struct snd_kcontrol_new control_templates[] = { |
817 | HDA_CODEC_VOLUME(NULL, 0, 0, 0), | 823 | HDA_CODEC_VOLUME(NULL, 0, 0, 0), |
818 | HDA_CODEC_MUTE(NULL, 0, 0, 0), | 824 | /* only the put callback is replaced for handling the special mute */ |
825 | { | ||
826 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
827 | .subdevice = HDA_SUBDEV_AMP_FLAG, | ||
828 | .info = snd_hda_mixer_amp_switch_info, | ||
829 | .get = snd_hda_mixer_amp_switch_get, | ||
830 | .put = hda_gen_mixer_mute_put, /* replaced */ | ||
831 | .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), | ||
832 | }, | ||
819 | HDA_BIND_MUTE(NULL, 0, 0, 0), | 833 | HDA_BIND_MUTE(NULL, 0, 0, 0), |
820 | }; | 834 | }; |
821 | 835 | ||
@@ -922,6 +936,23 @@ static int add_stereo_sw(struct hda_codec *codec, const char *pfx, | |||
922 | return add_sw_ctl(codec, pfx, cidx, chs, path); | 936 | return add_sw_ctl(codec, pfx, cidx, chs, path); |
923 | } | 937 | } |
924 | 938 | ||
939 | /* playback mute control with the software mute bit check */ | ||
940 | static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, | ||
941 | struct snd_ctl_elem_value *ucontrol) | ||
942 | { | ||
943 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
944 | struct hda_gen_spec *spec = codec->spec; | ||
945 | |||
946 | if (spec->auto_mute_via_amp) { | ||
947 | hda_nid_t nid = get_amp_nid(kcontrol); | ||
948 | bool enabled = !((spec->mute_bits >> nid) & 1); | ||
949 | ucontrol->value.integer.value[0] &= enabled; | ||
950 | ucontrol->value.integer.value[1] &= enabled; | ||
951 | } | ||
952 | |||
953 | return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); | ||
954 | } | ||
955 | |||
925 | /* any ctl assigned to the path with the given index? */ | 956 | /* any ctl assigned to the path with the given index? */ |
926 | static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type) | 957 | static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type) |
927 | { | 958 | { |
@@ -3719,6 +3750,16 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, | |||
3719 | unsigned int val, oldval; | 3750 | unsigned int val, oldval; |
3720 | if (!nid) | 3751 | if (!nid) |
3721 | break; | 3752 | break; |
3753 | |||
3754 | if (spec->auto_mute_via_amp) { | ||
3755 | if (mute) | ||
3756 | spec->mute_bits |= (1ULL << nid); | ||
3757 | else | ||
3758 | spec->mute_bits &= ~(1ULL << nid); | ||
3759 | set_pin_eapd(codec, nid, !mute); | ||
3760 | continue; | ||
3761 | } | ||
3762 | |||
3722 | oldval = snd_hda_codec_get_pin_target(codec, nid); | 3763 | oldval = snd_hda_codec_get_pin_target(codec, nid); |
3723 | if (oldval & PIN_IN) | 3764 | if (oldval & PIN_IN) |
3724 | continue; /* no mute for inputs */ | 3765 | continue; /* no mute for inputs */ |
@@ -3786,6 +3827,10 @@ static void call_update_outputs(struct hda_codec *codec) | |||
3786 | spec->automute_hook(codec); | 3827 | spec->automute_hook(codec); |
3787 | else | 3828 | else |
3788 | snd_hda_gen_update_outputs(codec); | 3829 | snd_hda_gen_update_outputs(codec); |
3830 | |||
3831 | /* sync the whole vmaster slaves to reflect the new auto-mute status */ | ||
3832 | if (spec->auto_mute_via_amp && !codec->bus->shutdown) | ||
3833 | snd_ctl_sync_vmaster(spec->vmaster_mute.sw_kctl, false); | ||
3789 | } | 3834 | } |
3790 | 3835 | ||
3791 | /* standard HP-automute helper */ | 3836 | /* 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 { | |||
209 | unsigned int master_mute:1; /* master mute over all */ | 209 | unsigned int master_mute:1; /* master mute over all */ |
210 | unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ | 210 | unsigned int keep_vref_in_automute:1; /* Don't clear VREF in automute */ |
211 | unsigned int line_in_auto_switch:1; /* allow line-in auto switch */ | 211 | unsigned int line_in_auto_switch:1; /* allow line-in auto switch */ |
212 | unsigned int auto_mute_via_amp:1; /* auto-mute via amp instead of pinctl */ | ||
212 | 213 | ||
213 | /* parser behavior flags; set before snd_hda_gen_parse_auto_config() */ | 214 | /* parser behavior flags; set before snd_hda_gen_parse_auto_config() */ |
214 | unsigned int suppress_auto_mute:1; /* suppress input jack auto mute */ | 215 | unsigned int suppress_auto_mute:1; /* suppress input jack auto mute */ |
@@ -237,6 +238,9 @@ struct hda_gen_spec { | |||
237 | unsigned int have_aamix_ctl:1; | 238 | unsigned int have_aamix_ctl:1; |
238 | unsigned int hp_mic_jack_modes:1; | 239 | unsigned int hp_mic_jack_modes:1; |
239 | 240 | ||
241 | /* additional mute flags (only effective with auto_mute_via_amp=1) */ | ||
242 | u64 mute_bits; | ||
243 | |||
240 | /* badness tables for output path evaluations */ | 244 | /* badness tables for output path evaluations */ |
241 | const struct badness_table *main_out_badness; | 245 | const struct badness_table *main_out_badness; |
242 | const struct badness_table *extra_out_badness; | 246 | const struct badness_table *extra_out_badness; |