diff options
Diffstat (limited to 'sound/pci')
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 130 |
1 files changed, 127 insertions, 3 deletions
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index dca8ba0b3ce9..764b32ba3636 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c | |||
@@ -266,6 +266,7 @@ struct alc_spec { | |||
266 | /* for pin sensing */ | 266 | /* for pin sensing */ |
267 | unsigned int sense_updated: 1; | 267 | unsigned int sense_updated: 1; |
268 | unsigned int jack_present: 1; | 268 | unsigned int jack_present: 1; |
269 | unsigned int master_sw: 1; | ||
269 | 270 | ||
270 | /* for virtual master */ | 271 | /* for virtual master */ |
271 | hda_nid_t vmaster_nid; | 272 | hda_nid_t vmaster_nid; |
@@ -3828,7 +3829,101 @@ static struct snd_kcontrol_new alc260_pc_beep_mixer[] = { | |||
3828 | { } /* end */ | 3829 | { } /* end */ |
3829 | }; | 3830 | }; |
3830 | 3831 | ||
3832 | /* update HP, line and mono out pins according to the master switch */ | ||
3833 | static void alc260_hp_master_update(struct hda_codec *codec, | ||
3834 | hda_nid_t hp, hda_nid_t line, | ||
3835 | hda_nid_t mono) | ||
3836 | { | ||
3837 | struct alc_spec *spec = codec->spec; | ||
3838 | unsigned int val = spec->master_sw ? PIN_HP : 0; | ||
3839 | /* change HP and line-out pins */ | ||
3840 | snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, | ||
3841 | val); | ||
3842 | snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, | ||
3843 | val); | ||
3844 | /* mono (speaker) depending on the HP jack sense */ | ||
3845 | val = (val && !spec->jack_present) ? PIN_OUT : 0; | ||
3846 | snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, | ||
3847 | val); | ||
3848 | } | ||
3849 | |||
3850 | static int alc260_hp_master_sw_get(struct snd_kcontrol *kcontrol, | ||
3851 | struct snd_ctl_elem_value *ucontrol) | ||
3852 | { | ||
3853 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
3854 | struct alc_spec *spec = codec->spec; | ||
3855 | *ucontrol->value.integer.value = spec->master_sw; | ||
3856 | return 0; | ||
3857 | } | ||
3858 | |||
3859 | static int alc260_hp_master_sw_put(struct snd_kcontrol *kcontrol, | ||
3860 | struct snd_ctl_elem_value *ucontrol) | ||
3861 | { | ||
3862 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
3863 | struct alc_spec *spec = codec->spec; | ||
3864 | int val = !!*ucontrol->value.integer.value; | ||
3865 | hda_nid_t hp, line, mono; | ||
3866 | |||
3867 | if (val == spec->master_sw) | ||
3868 | return 0; | ||
3869 | spec->master_sw = val; | ||
3870 | hp = (kcontrol->private_value >> 16) & 0xff; | ||
3871 | line = (kcontrol->private_value >> 8) & 0xff; | ||
3872 | mono = kcontrol->private_value & 0xff; | ||
3873 | alc260_hp_master_update(codec, hp, line, mono); | ||
3874 | return 1; | ||
3875 | } | ||
3876 | |||
3877 | static struct snd_kcontrol_new alc260_hp_output_mixer[] = { | ||
3878 | { | ||
3879 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
3880 | .name = "Master Playback Switch", | ||
3881 | .info = snd_ctl_boolean_mono_info, | ||
3882 | .get = alc260_hp_master_sw_get, | ||
3883 | .put = alc260_hp_master_sw_put, | ||
3884 | .private_value = (0x0f << 16) | (0x10 << 8) | 0x11 | ||
3885 | }, | ||
3886 | HDA_CODEC_VOLUME("Front Playback Volume", 0x08, 0x0, HDA_OUTPUT), | ||
3887 | HDA_BIND_MUTE("Front Playback Switch", 0x08, 2, HDA_INPUT), | ||
3888 | HDA_CODEC_VOLUME("Headphone Playback Volume", 0x09, 0x0, HDA_OUTPUT), | ||
3889 | HDA_BIND_MUTE("Headphone Playback Switch", 0x09, 2, HDA_INPUT), | ||
3890 | HDA_CODEC_VOLUME_MONO("Speaker Playback Volume", 0x0a, 1, 0x0, | ||
3891 | HDA_OUTPUT), | ||
3892 | HDA_BIND_MUTE_MONO("Speaker Playback Switch", 0x0a, 1, 2, HDA_INPUT), | ||
3893 | { } /* end */ | ||
3894 | }; | ||
3895 | |||
3896 | static struct hda_verb alc260_hp_unsol_verbs[] = { | ||
3897 | {0x10, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, | ||
3898 | {}, | ||
3899 | }; | ||
3900 | |||
3901 | static void alc260_hp_automute(struct hda_codec *codec) | ||
3902 | { | ||
3903 | struct alc_spec *spec = codec->spec; | ||
3904 | unsigned int present; | ||
3905 | |||
3906 | present = snd_hda_codec_read(codec, 0x10, 0, | ||
3907 | AC_VERB_GET_PIN_SENSE, 0); | ||
3908 | spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; | ||
3909 | alc260_hp_master_update(codec, 0x0f, 0x10, 0x11); | ||
3910 | } | ||
3911 | |||
3912 | static void alc260_hp_unsol_event(struct hda_codec *codec, unsigned int res) | ||
3913 | { | ||
3914 | if ((res >> 26) == ALC880_HP_EVENT) | ||
3915 | alc260_hp_automute(codec); | ||
3916 | } | ||
3917 | |||
3831 | static struct snd_kcontrol_new alc260_hp_3013_mixer[] = { | 3918 | static struct snd_kcontrol_new alc260_hp_3013_mixer[] = { |
3919 | { | ||
3920 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
3921 | .name = "Master Playback Switch", | ||
3922 | .info = snd_ctl_boolean_mono_info, | ||
3923 | .get = alc260_hp_master_sw_get, | ||
3924 | .put = alc260_hp_master_sw_put, | ||
3925 | .private_value = (0x10 << 16) | (0x15 << 8) | 0x11 | ||
3926 | }, | ||
3832 | HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT), | 3927 | HDA_CODEC_VOLUME("Front Playback Volume", 0x09, 0x0, HDA_OUTPUT), |
3833 | HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT), | 3928 | HDA_CODEC_MUTE("Front Playback Switch", 0x10, 0x0, HDA_OUTPUT), |
3834 | HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT), | 3929 | HDA_CODEC_VOLUME("Aux-In Playback Volume", 0x07, 0x06, HDA_INPUT), |
@@ -3840,6 +3935,29 @@ static struct snd_kcontrol_new alc260_hp_3013_mixer[] = { | |||
3840 | { } /* end */ | 3935 | { } /* end */ |
3841 | }; | 3936 | }; |
3842 | 3937 | ||
3938 | static struct hda_verb alc260_hp_3013_unsol_verbs[] = { | ||
3939 | {0x15, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC880_HP_EVENT}, | ||
3940 | {}, | ||
3941 | }; | ||
3942 | |||
3943 | static void alc260_hp_3013_automute(struct hda_codec *codec) | ||
3944 | { | ||
3945 | struct alc_spec *spec = codec->spec; | ||
3946 | unsigned int present; | ||
3947 | |||
3948 | present = snd_hda_codec_read(codec, 0x15, 0, | ||
3949 | AC_VERB_GET_PIN_SENSE, 0); | ||
3950 | spec->jack_present = (present & AC_PINSENSE_PRESENCE) != 0; | ||
3951 | alc260_hp_master_update(codec, 0x10, 0x15, 0x11); | ||
3952 | } | ||
3953 | |||
3954 | static void alc260_hp_3013_unsol_event(struct hda_codec *codec, | ||
3955 | unsigned int res) | ||
3956 | { | ||
3957 | if ((res >> 26) == ALC880_HP_EVENT) | ||
3958 | alc260_hp_3013_automute(codec); | ||
3959 | } | ||
3960 | |||
3843 | /* Fujitsu S702x series laptops. ALC260 pin usage: Mic/Line jack = 0x12, | 3961 | /* Fujitsu S702x series laptops. ALC260 pin usage: Mic/Line jack = 0x12, |
3844 | * HP jack = 0x14, CD audio = 0x16, internal speaker = 0x10. | 3962 | * HP jack = 0x14, CD audio = 0x16, internal speaker = 0x10. |
3845 | */ | 3963 | */ |
@@ -4897,10 +5015,11 @@ static struct alc_config_preset alc260_presets[] = { | |||
4897 | .input_mux = &alc260_capture_source, | 5015 | .input_mux = &alc260_capture_source, |
4898 | }, | 5016 | }, |
4899 | [ALC260_HP] = { | 5017 | [ALC260_HP] = { |
4900 | .mixers = { alc260_base_output_mixer, | 5018 | .mixers = { alc260_hp_output_mixer, |
4901 | alc260_input_mixer, | 5019 | alc260_input_mixer, |
4902 | alc260_capture_alt_mixer }, | 5020 | alc260_capture_alt_mixer }, |
4903 | .init_verbs = { alc260_init_verbs }, | 5021 | .init_verbs = { alc260_init_verbs, |
5022 | alc260_hp_unsol_verbs }, | ||
4904 | .num_dacs = ARRAY_SIZE(alc260_dac_nids), | 5023 | .num_dacs = ARRAY_SIZE(alc260_dac_nids), |
4905 | .dac_nids = alc260_dac_nids, | 5024 | .dac_nids = alc260_dac_nids, |
4906 | .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), | 5025 | .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), |
@@ -4908,12 +5027,15 @@ static struct alc_config_preset alc260_presets[] = { | |||
4908 | .num_channel_mode = ARRAY_SIZE(alc260_modes), | 5027 | .num_channel_mode = ARRAY_SIZE(alc260_modes), |
4909 | .channel_mode = alc260_modes, | 5028 | .channel_mode = alc260_modes, |
4910 | .input_mux = &alc260_capture_source, | 5029 | .input_mux = &alc260_capture_source, |
5030 | .unsol_event = alc260_hp_unsol_event, | ||
5031 | .init_hook = alc260_hp_automute, | ||
4911 | }, | 5032 | }, |
4912 | [ALC260_HP_3013] = { | 5033 | [ALC260_HP_3013] = { |
4913 | .mixers = { alc260_hp_3013_mixer, | 5034 | .mixers = { alc260_hp_3013_mixer, |
4914 | alc260_input_mixer, | 5035 | alc260_input_mixer, |
4915 | alc260_capture_alt_mixer }, | 5036 | alc260_capture_alt_mixer }, |
4916 | .init_verbs = { alc260_hp_3013_init_verbs }, | 5037 | .init_verbs = { alc260_hp_3013_init_verbs, |
5038 | alc260_hp_3013_unsol_verbs }, | ||
4917 | .num_dacs = ARRAY_SIZE(alc260_dac_nids), | 5039 | .num_dacs = ARRAY_SIZE(alc260_dac_nids), |
4918 | .dac_nids = alc260_dac_nids, | 5040 | .dac_nids = alc260_dac_nids, |
4919 | .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), | 5041 | .num_adc_nids = ARRAY_SIZE(alc260_hp_adc_nids), |
@@ -4921,6 +5043,8 @@ static struct alc_config_preset alc260_presets[] = { | |||
4921 | .num_channel_mode = ARRAY_SIZE(alc260_modes), | 5043 | .num_channel_mode = ARRAY_SIZE(alc260_modes), |
4922 | .channel_mode = alc260_modes, | 5044 | .channel_mode = alc260_modes, |
4923 | .input_mux = &alc260_capture_source, | 5045 | .input_mux = &alc260_capture_source, |
5046 | .unsol_event = alc260_hp_3013_unsol_event, | ||
5047 | .init_hook = alc260_hp_3013_automute, | ||
4924 | }, | 5048 | }, |
4925 | [ALC260_FUJITSU_S702X] = { | 5049 | [ALC260_FUJITSU_S702X] = { |
4926 | .mixers = { alc260_fujitsu_mixer, | 5050 | .mixers = { alc260_fujitsu_mixer, |