From 834be88d136ee82828e3ce1b34fa7a1dcf947b81 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 1 Mar 2006 14:16:17 +0100 Subject: [ALSA] hda-codec - Fix ALC262 for Fujitsu laptop Modules: HDA Codec driver,HDA generic driver Add 'fujitsu' model for ALC262 patch to support a FSC laptop. The internal speaker is turned on/off with jack sensing. Also fixed alc262 'basic' model. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 6 +- sound/pci/hda/hda_local.h | 5 ++ sound/pci/hda/patch_realtek.c | 159 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 155 insertions(+), 15 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 0d1566a3996..fc91256e42e 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -729,7 +729,8 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info, /* * read AMP value. The volume is between 0 to 0x7f, 0x80 = mute bit. */ -static int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int index) +int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, + int direction, int index) { struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index)); if (! info) @@ -740,7 +741,8 @@ static int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch /* * update the AMP value, mask = bit mask to set, val = the value */ -static int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val) +int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, + int direction, int idx, int mask, int val) { struct hda_amp_info *info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx)); diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index c82d2a72d13..548a8b69848 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -66,6 +66,11 @@ int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_hda_mixer_amp_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +/* lowlevel accessor with caching; use carefully */ +int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch, + int direction, int index); +int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, + int direction, int idx, int mask, int val); /* mono switch binding multiple inputs */ #define HDA_BIND_MUTE_MONO(xname, nid, channel, indices, direction) \ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 219ddf0b8d4..5de754a51fc 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -75,6 +75,7 @@ enum { /* ALC262 models */ enum { ALC262_BASIC, + ALC262_FUJITSU, ALC262_AUTO, ALC262_MODEL_LAST /* last tag */ }; @@ -145,6 +146,10 @@ struct alc_spec { struct snd_kcontrol_new *kctl_alloc; struct hda_input_mux private_imux; hda_nid_t private_dac_nids[5]; + + /* for pin sensing */ + unsigned int sense_updated: 1; + unsigned int jack_present: 1; }; /* @@ -4194,19 +4199,9 @@ static struct snd_kcontrol_new alc262_base_mixer[] = { HDA_CODEC_MUTE("Headphone Playback Switch", 0x15, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x0e, 2, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x16, 2, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x08, 0x0, HDA_INPUT), - HDA_CODEC_MUTE("Capture Switch", 0x08, 0x0, HDA_INPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .count = 1, - .info = alc882_mux_enum_info, - .get = alc882_mux_enum_get, - .put = alc882_mux_enum_put, - }, { } /* end */ -}; - +}; + #define alc262_capture_mixer alc882_capture_mixer #define alc262_capture_alt_mixer alc882_capture_alt_mixer @@ -4289,6 +4284,129 @@ static struct hda_verb alc262_init_verbs[] = { { } }; +/* + * fujitsu model + * 0x14 = headphone/spdif-out, 0x15 = internal speaker + */ + +#define ALC_HP_EVENT 0x37 + +static struct hda_verb alc262_fujitsu_unsol_verbs[] = { + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | ALC_HP_EVENT}, + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {} +}; + +static struct hda_input_mux alc262_fujitsu_capture_source = { + .num_items = 2, + .items = { + { "Mic", 0x0 }, + { "CD", 0x4 }, + }, +}; + +/* mute/unmute internal speaker according to the hp jack and mute state */ +static void alc262_fujitsu_automute(struct hda_codec *codec, int force) +{ + struct alc_spec *spec = codec->spec; + unsigned int mute; + + if (force || ! spec->sense_updated) { + unsigned int present; + /* need to execute and sync at first */ + snd_hda_codec_read(codec, 0x14, 0, AC_VERB_SET_PIN_SENSE, 0); + present = snd_hda_codec_read(codec, 0x14, 0, + AC_VERB_GET_PIN_SENSE, 0); + spec->jack_present = (present & 0x80000000) != 0; + spec->sense_updated = 1; + } + if (spec->jack_present) { + /* mute internal speaker */ + snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, + 0x80, 0x80); + snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, + 0x80, 0x80); + } else { + /* unmute internal speaker if necessary */ + mute = snd_hda_codec_amp_read(codec, 0x14, 0, HDA_OUTPUT, 0); + snd_hda_codec_amp_update(codec, 0x15, 0, HDA_OUTPUT, 0, + 0x80, mute & 0x80); + mute = snd_hda_codec_amp_read(codec, 0x14, 1, HDA_OUTPUT, 0); + snd_hda_codec_amp_update(codec, 0x15, 1, HDA_OUTPUT, 0, + 0x80, mute & 0x80); + } +} + +/* unsolicited event for HP jack sensing */ +static void alc262_fujitsu_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + if ((res >> 26) != ALC_HP_EVENT) + return; + alc262_fujitsu_automute(codec, 1); +} + +/* bind volumes of both NID 0x0c and 0x0d */ +static int alc262_fujitsu_master_vol_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + long *valp = ucontrol->value.integer.value; + int change; + + change = snd_hda_codec_amp_update(codec, 0x0c, 0, HDA_OUTPUT, 0, + 0x7f, valp[0] & 0x7f); + change |= snd_hda_codec_amp_update(codec, 0x0c, 1, HDA_OUTPUT, 0, + 0x7f, valp[1] & 0x7f); + snd_hda_codec_amp_update(codec, 0x0d, 0, HDA_OUTPUT, 0, + 0x7f, valp[0] & 0x7f); + snd_hda_codec_amp_update(codec, 0x0d, 1, HDA_OUTPUT, 0, + 0x7f, valp[1] & 0x7f); + return change; +} + +/* bind hp and internal speaker mute (with plug check) */ +static int alc262_fujitsu_master_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + long *valp = ucontrol->value.integer.value; + int change; + + change = snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0, + 0x80, valp[0] ? 0 : 0x80); + change |= snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0, + 0x80, valp[1] ? 0 : 0x80); + if (change || codec->in_resume) + alc262_fujitsu_automute(codec, codec->in_resume); + return change; +} + +static struct snd_kcontrol_new alc262_fujitsu_mixer[] = { + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Volume", + .info = snd_hda_mixer_amp_volume_info, + .get = snd_hda_mixer_amp_volume_get, + .put = alc262_fujitsu_master_vol_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x0c, 3, 0, HDA_OUTPUT), + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Master Playback Switch", + .info = snd_hda_mixer_amp_switch_info, + .get = snd_hda_mixer_amp_switch_get, + .put = alc262_fujitsu_master_sw_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT), + }, + HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT), + HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT), + HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT), + { } /* end */ +}; + /* add playback controls from the parsed DAC table */ static int alc262_auto_create_multi_out_ctls(struct alc_spec *spec, const struct auto_pin_cfg *cfg) { @@ -4479,6 +4597,8 @@ static int alc262_auto_init(struct hda_codec *codec) */ static struct hda_board_config alc262_cfg_tbl[] = { { .modelname = "basic", .config = ALC262_BASIC }, + { .modelname = "fujitsu", .config = ALC262_FUJITSU }, + { .pci_subvendor = 0x10cf, .pci_subdevice = 0x1397, .config = ALC262_FUJITSU }, { .modelname = "auto", .config = ALC262_AUTO }, {} }; @@ -4494,6 +4614,17 @@ static struct alc_config_preset alc262_presets[] = { .channel_mode = alc262_modes, .input_mux = &alc262_capture_source, }, + [ALC262_FUJITSU] = { + .mixers = { alc262_fujitsu_mixer }, + .init_verbs = { alc262_init_verbs, alc262_fujitsu_unsol_verbs }, + .num_dacs = ARRAY_SIZE(alc262_dac_nids), + .dac_nids = alc262_dac_nids, + .hp_nid = 0x03, + .dig_out_nid = ALC262_DIGOUT_NID, + .num_channel_mode = ARRAY_SIZE(alc262_modes), + .channel_mode = alc262_modes, + .input_mux = &alc262_fujitsu_capture_source, + }, }; static int patch_alc262(struct hda_codec *codec) @@ -4568,7 +4699,9 @@ static int patch_alc262(struct hda_codec *codec) codec->patch_ops = alc_patch_ops; if (board_config == ALC262_AUTO) codec->patch_ops.init = alc262_auto_init; - + if (board_config == ALC262_FUJITSU) + codec->patch_ops.unsol_event = alc262_fujitsu_unsol_event; + return 0; } -- cgit v1.2.3