X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=sound%2Fpci%2Fhda%2Fpatch_analog.c;h=1a36137e13eca5f3d488402da0084500fa4489bc;hb=01f5966d2f36f08eb6604665eade69c9f38ffaed;hp=2c58d7b05aba3d8bd2f751f1a940ffdd14bc9301;hpb=c23127566c7a54c8413bf1b99becea76072f467e;p=safe%2Fjmp%2Flinux-2.6 diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 2c58d7b..1a36137 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -72,6 +72,8 @@ struct ad198x_spec { hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; unsigned int jack_present :1; + unsigned int inv_jack_detect:1; /* inverted jack-detection */ + unsigned int inv_eapd:1; /* inverted EAPD implementation */ #ifdef CONFIG_SND_HDA_POWER_SAVE struct hda_loopback_check loopback; @@ -155,15 +157,19 @@ static const char *ad_slave_sws[] = { static void ad198x_free_kctls(struct hda_codec *codec); +#ifdef CONFIG_SND_HDA_INPUT_BEEP /* additional beep mixers; the actual parameters are overwritten at build */ static struct snd_kcontrol_new ad_beep_mixer[] = { HDA_CODEC_VOLUME("Beep Playback Volume", 0, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("Beep Playback Switch", 0, 0, HDA_OUTPUT), + HDA_CODEC_MUTE_BEEP("Beep Playback Switch", 0, 0, HDA_OUTPUT), { } /* end */ }; #define set_beep_amp(spec, nid, idx, dir) \ ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */ +#else +#define set_beep_amp(spec, nid, idx, dir) /* NOP */ +#endif static int ad198x_build_controls(struct hda_codec *codec) { @@ -193,6 +199,7 @@ static int ad198x_build_controls(struct hda_codec *codec) } /* create beep controls if needed */ +#ifdef CONFIG_SND_HDA_INPUT_BEEP if (spec->beep_amp) { struct snd_kcontrol_new *knew; for (knew = ad_beep_mixer; knew->name; knew++) { @@ -201,11 +208,14 @@ static int ad198x_build_controls(struct hda_codec *codec) if (!kctl) return -ENOMEM; kctl->private_value = spec->beep_amp; - err = snd_hda_ctl_add(codec, kctl); + err = snd_hda_ctl_add(codec, + get_amp_nid_(spec->beep_amp), + kctl); if (err < 0) return err; } } +#endif /* if we have no master control, let's create it */ if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) { @@ -449,7 +459,7 @@ static struct hda_codec_ops ad198x_patch_ops = { /* * EAPD control - * the private value = nid | (invert << 8) + * the private value = nid */ #define ad198x_eapd_info snd_ctl_boolean_mono_info @@ -458,8 +468,7 @@ static int ad198x_eapd_get(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ad198x_spec *spec = codec->spec; - int invert = (kcontrol->private_value >> 8) & 1; - if (invert) + if (spec->inv_eapd) ucontrol->value.integer.value[0] = ! spec->cur_eapd; else ucontrol->value.integer.value[0] = spec->cur_eapd; @@ -471,11 +480,10 @@ static int ad198x_eapd_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ad198x_spec *spec = codec->spec; - int invert = (kcontrol->private_value >> 8) & 1; hda_nid_t nid = kcontrol->private_value & 0xff; unsigned int eapd; eapd = !!ucontrol->value.integer.value[0]; - if (invert) + if (spec->inv_eapd) eapd = !eapd; if (eapd == spec->cur_eapd) return 0; @@ -669,39 +677,13 @@ static struct hda_input_mux ad1986a_automic_capture_source = { }, }; -static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { +static struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = { HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), - HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "External Amplifier", - .info = ad198x_eapd_info, - .get = ad198x_eapd_get, - .put = ad198x_eapd_put, - .private_value = 0x1b | (1 << 8), /* port-D, inversed */ - }, { } /* end */ }; -static struct snd_kcontrol_new ad1986a_samsung_mixers[] = { - HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), - HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw), +static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = { HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), @@ -722,19 +704,25 @@ static struct snd_kcontrol_new ad1986a_samsung_mixers[] = { .info = ad198x_eapd_info, .get = ad198x_eapd_get, .put = ad198x_eapd_put, - .private_value = 0x1b | (1 << 8), /* port-D, inversed */ + .private_value = 0x1b, /* port-D */ }, { } /* end */ }; +static struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = { + HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT), + HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT), + { } /* end */ +}; + /* re-connect the mic boost input according to the jack sensing */ static void ad1986a_automic(struct hda_codec *codec) { unsigned int present; - present = snd_hda_codec_read(codec, 0x1f, 0, AC_VERB_GET_PIN_SENSE, 0); + present = snd_hda_jack_detect(codec, 0x1f); /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */ snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL, - (present & AC_PINSENSE_PRESENCE) ? 0 : 2); + present ? 0 : 2); } #define AD1986A_MIC_EVENT 0x36 @@ -773,11 +761,10 @@ static void ad1986a_update_hp(struct hda_codec *codec) static void ad1986a_hp_automute(struct hda_codec *codec) { struct ad198x_spec *spec = codec->spec; - unsigned int present; - present = snd_hda_codec_read(codec, 0x1a, 0, AC_VERB_GET_PIN_SENSE, 0); - /* Lenovo N100 seems to report the reversed bit for HP jack-sensing */ - spec->jack_present = !(present & 0x80000000); + spec->jack_present = snd_hda_jack_detect(codec, 0x1a); + if (spec->inv_jack_detect) + spec->jack_present = !spec->jack_present; ad1986a_update_hp(codec); } @@ -816,7 +803,7 @@ static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol, return change; } -static struct snd_kcontrol_new ad1986a_laptop_automute_mixers[] = { +static struct snd_kcontrol_new ad1986a_automute_master_mixers[] = { HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol), { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -826,33 +813,10 @@ static struct snd_kcontrol_new ad1986a_laptop_automute_mixers[] = { .put = ad1986a_hp_master_sw_put, .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT), }, - HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Mic Boost", 0x0f, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Source", - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "External Amplifier", - .info = ad198x_eapd_info, - .get = ad198x_eapd_get, - .put = ad198x_eapd_put, - .private_value = 0x1b | (1 << 8), /* port-D, inversed */ - }, { } /* end */ }; + /* * initialization verbs */ @@ -981,6 +945,27 @@ static struct hda_verb ad1986a_hp_init_verbs[] = { {} }; +static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case AD1986A_HP_EVENT: + ad1986a_hp_automute(codec); + break; + case AD1986A_MIC_EVENT: + ad1986a_automic(codec); + break; + } +} + +static int ad1986a_samsung_p50_init(struct hda_codec *codec) +{ + ad198x_init(codec); + ad1986a_hp_automute(codec); + ad1986a_automic(codec); + return 0; +} + /* models */ enum { @@ -991,6 +976,7 @@ enum { AD1986A_LAPTOP_AUTOMUTE, AD1986A_ULTRA, AD1986A_SAMSUNG, + AD1986A_SAMSUNG_P50, AD1986A_MODELS }; @@ -1002,6 +988,7 @@ static const char *ad1986a_models[AD1986A_MODELS] = { [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute", [AD1986A_ULTRA] = "ultra", [AD1986A_SAMSUNG] = "samsung", + [AD1986A_SAMSUNG_P50] = "samsung-p50", }; static struct snd_pci_quirk ad1986a_cfg_tbl[] = { @@ -1024,6 +1011,7 @@ static struct snd_pci_quirk ad1986a_cfg_tbl[] = { SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba", AD1986A_LAPTOP_EAPD), SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK), SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP), + SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50), SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA), SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG), SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK), @@ -1047,8 +1035,7 @@ static struct hda_amp_list ad1986a_loopbacks[] = { static int is_jack_available(struct hda_codec *codec, hda_nid_t nid) { - unsigned int conf = snd_hda_codec_read(codec, nid, 0, - AC_VERB_GET_CONFIG_DEFAULT, 0); + unsigned int conf = snd_hda_codec_get_pincfg(codec, nid); return get_defcfg_connect(conf) != AC_JACK_PORT_NONE; } @@ -1086,6 +1073,7 @@ static int patch_ad1986a(struct hda_codec *codec) spec->loopback.amplist = ad1986a_loopbacks; #endif spec->vmaster_nid = 0x1b; + spec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */ codec->patch_ops = ad198x_patch_ops; @@ -1112,7 +1100,10 @@ static int patch_ad1986a(struct hda_codec *codec) spec->multiout.dac_nids = ad1986a_laptop_dac_nids; break; case AD1986A_LAPTOP_EAPD: - spec->mixers[0] = ad1986a_laptop_eapd_mixers; + spec->num_mixers = 3; + spec->mixers[0] = ad1986a_laptop_master_mixers; + spec->mixers[1] = ad1986a_laptop_eapd_mixers; + spec->mixers[2] = ad1986a_laptop_intmic_mixers; spec->num_init_verbs = 2; spec->init_verbs[1] = ad1986a_eapd_init_verbs; spec->multiout.max_channels = 2; @@ -1123,7 +1114,9 @@ static int patch_ad1986a(struct hda_codec *codec) spec->input_mux = &ad1986a_laptop_eapd_capture_source; break; case AD1986A_SAMSUNG: - spec->mixers[0] = ad1986a_samsung_mixers; + spec->num_mixers = 2; + spec->mixers[0] = ad1986a_laptop_master_mixers; + spec->mixers[1] = ad1986a_laptop_eapd_mixers; spec->num_init_verbs = 3; spec->init_verbs[1] = ad1986a_eapd_init_verbs; spec->init_verbs[2] = ad1986a_automic_verbs; @@ -1136,8 +1129,28 @@ static int patch_ad1986a(struct hda_codec *codec) codec->patch_ops.unsol_event = ad1986a_automic_unsol_event; codec->patch_ops.init = ad1986a_automic_init; break; + case AD1986A_SAMSUNG_P50: + spec->num_mixers = 2; + spec->mixers[0] = ad1986a_automute_master_mixers; + spec->mixers[1] = ad1986a_laptop_eapd_mixers; + spec->num_init_verbs = 4; + spec->init_verbs[1] = ad1986a_eapd_init_verbs; + spec->init_verbs[2] = ad1986a_automic_verbs; + spec->init_verbs[3] = ad1986a_hp_init_verbs; + spec->multiout.max_channels = 2; + spec->multiout.num_dacs = 1; + spec->multiout.dac_nids = ad1986a_laptop_dac_nids; + if (!is_jack_available(codec, 0x25)) + spec->multiout.dig_out_nid = 0; + spec->input_mux = &ad1986a_automic_capture_source; + codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event; + codec->patch_ops.init = ad1986a_samsung_p50_init; + break; case AD1986A_LAPTOP_AUTOMUTE: - spec->mixers[0] = ad1986a_laptop_automute_mixers; + spec->num_mixers = 3; + spec->mixers[0] = ad1986a_automute_master_mixers; + spec->mixers[1] = ad1986a_laptop_eapd_mixers; + spec->mixers[2] = ad1986a_laptop_intmic_mixers; spec->num_init_verbs = 3; spec->init_verbs[1] = ad1986a_eapd_init_verbs; spec->init_verbs[2] = ad1986a_hp_init_verbs; @@ -1149,6 +1162,10 @@ static int patch_ad1986a(struct hda_codec *codec) spec->input_mux = &ad1986a_laptop_eapd_capture_source; codec->patch_ops.unsol_event = ad1986a_hp_unsol_event; codec->patch_ops.init = ad1986a_hp_init; + /* Lenovo N100 seems to report the reversed bit + * for HP jack-sensing + */ + spec->inv_jack_detect = 1; break; case AD1986A_ULTRA: spec->mixers[0] = ad1986a_laptop_eapd_mixers; @@ -1536,8 +1553,7 @@ static void ad1981_hp_automute(struct hda_codec *codec) { unsigned int present; - present = snd_hda_codec_read(codec, 0x06, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x06); snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); } @@ -1557,8 +1573,7 @@ static void ad1981_hp_automic(struct hda_codec *codec) }; unsigned int present; - present = snd_hda_codec_read(codec, 0x08, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x08); if (present) snd_hda_sequence_write(codec, mic_jack_on); else @@ -1774,6 +1789,14 @@ static int patch_ad1981(struct hda_codec *codec) codec->patch_ops.init = ad1981_hp_init; codec->patch_ops.unsol_event = ad1981_hp_unsol_event; + /* set the upper-limit for mixer amp to 0dB for avoiding the + * possible damage by overloading + */ + snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); break; case AD1981_THINKPAD: spec->mixers[0] = ad1981_thinkpad_mixers; @@ -2109,7 +2132,7 @@ static struct snd_kcontrol_new ad1988_laptop_mixers[] = { .info = ad198x_eapd_info, .get = ad198x_eapd_get, .put = ad198x_eapd_put, - .private_value = 0x12 | (1 << 8), /* port-D, inversed */ + .private_value = 0x12, /* port-D */ }, { } /* end */ @@ -2513,7 +2536,7 @@ static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res) { if ((res >> 26) != AD1988_HP_EVENT) return; - if (snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) & (1 << 31)) + if (snd_hda_jack_detect(codec, 0x11)) snd_hda_sequence_write(codec, ad1988_laptop_hp_on); else snd_hda_sequence_write(codec, ad1988_laptop_hp_off); @@ -2558,6 +2581,8 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name, knew->name = kstrdup(name, GFP_KERNEL); if (! knew->name) return -ENOMEM; + if (get_amp_nid_(val)) + knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val); knew->private_value = val; return 0; } @@ -2971,7 +2996,8 @@ static int patch_ad1988(struct hda_codec *codec) board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST, ad1988_models, ad1988_cfg_tbl); if (board_config < 0) { - printk(KERN_INFO "hda_codec: Unknown model for AD1988, trying auto-probe from BIOS...\n"); + printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n", + codec->chip_name); board_config = AD1988_AUTO; } @@ -3047,6 +3073,7 @@ static int patch_ad1988(struct hda_codec *codec) spec->input_mux = &ad1988_laptop_capture_source; spec->num_mixers = 1; spec->mixers[0] = ad1988_laptop_mixers; + spec->inv_eapd = 1; /* inverted EAPD */ spec->num_init_verbs = 1; spec->init_verbs[0] = ad1988_laptop_init_verbs; if (board_config == AD1988_LAPTOP_DIG) @@ -3257,7 +3284,7 @@ static const char *ad1884_slave_vols[] = { "Mic Playback Volume", "CD Playback Volume", "Internal Mic Playback Volume", - "Docking Mic Playback Volume" + "Docking Mic Playback Volume", /* "Beep Playback Volume", */ "IEC958 Playback Volume", NULL @@ -3691,19 +3718,29 @@ static struct hda_amp_list ad1884a_loopbacks[] = { * Port F: Internal speakers */ -static struct hda_input_mux ad1884a_laptop_capture_source = { - .num_items = 4, - .items = { - { "Mic", 0x0 }, /* port-B */ - { "Internal Mic", 0x1 }, /* port-C */ - { "Dock Mic", 0x4 }, /* port-E */ - { "Mix", 0x3 }, - }, -}; +static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); + int mute = (!ucontrol->value.integer.value[0] && + !ucontrol->value.integer.value[1]); + /* toggle GPIO1 according to the mute state */ + snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, + mute ? 0x02 : 0x0); + return ret; +} static struct snd_kcontrol_new ad1884a_laptop_mixers[] = { HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, 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 = ad1884a_mobile_master_sw_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), + }, HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), @@ -3718,26 +3755,20 @@ static struct snd_kcontrol_new ad1884a_laptop_mixers[] = { HDA_CODEC_VOLUME("Dock Mic Boost", 0x25, 0x0, HDA_OUTPUT), HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), - HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT), - { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - /* The multiple "Capture Source" controls confuse alsamixer - * So call somewhat different.. - */ - /* .name = "Capture Source", */ - .name = "Input Source", - .count = 2, - .info = ad198x_mux_enum_info, - .get = ad198x_mux_enum_get, - .put = ad198x_mux_enum_put, - }, { } /* end */ }; static struct snd_kcontrol_new ad1884a_mobile_mixers[] = { HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), - HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT), + /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, 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 = ad1884a_mobile_master_sw_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), + }, HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT), @@ -3752,8 +3783,7 @@ static void ad1884a_hp_automute(struct hda_codec *codec) { unsigned int present; - present = snd_hda_codec_read(codec, 0x11, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x11); snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE, @@ -3765,8 +3795,7 @@ static void ad1884a_hp_automic(struct hda_codec *codec) { unsigned int present; - present = snd_hda_codec_read(codec, 0x14, 0, - AC_VERB_GET_PIN_SENSE, 0) & 0x80000000; + present = snd_hda_jack_detect(codec, 0x14); snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, present ? 0 : 1); } @@ -3796,6 +3825,57 @@ static int ad1884a_hp_init(struct hda_codec *codec) return 0; } +/* mute internal speaker if HP or docking HP is plugged */ +static void ad1884a_laptop_automute(struct hda_codec *codec) +{ + unsigned int present; + + present = snd_hda_jack_detect(codec, 0x11); + if (!present) + present = snd_hda_jack_detect(codec, 0x12); + snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0, + HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); + snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE, + present ? 0x00 : 0x02); +} + +/* switch to external mic if plugged */ +static void ad1884a_laptop_automic(struct hda_codec *codec) +{ + unsigned int idx; + + if (snd_hda_jack_detect(codec, 0x14)) + idx = 0; + else if (snd_hda_jack_detect(codec, 0x1c)) + idx = 4; + else + idx = 1; + snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx); +} + +/* unsolicited event for HP jack sensing */ +static void ad1884a_laptop_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case AD1884A_HP_EVENT: + ad1884a_laptop_automute(codec); + break; + case AD1884A_MIC_EVENT: + ad1884a_laptop_automic(codec); + break; + } +} + +/* initialize jack-sensing, too */ +static int ad1884a_laptop_init(struct hda_codec *codec) +{ + ad198x_init(codec); + ad1884a_laptop_automute(codec); + ad1884a_laptop_automic(codec); + return 0; +} + /* additional verbs for laptop model */ static struct hda_verb ad1884a_laptop_verbs[] = { /* Port-A (HP) pin - always unmuted */ @@ -3803,18 +3883,75 @@ static struct hda_verb ad1884a_laptop_verbs[] = { /* Port-F (int speaker) mixer - route only from analog mixer */ {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, - /* Port-F pin */ - {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Port-F (int speaker) pin */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* required for compaq 6530s/6531s speaker output */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Port-C pin - internal mic-in */ {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ + /* Port-D (docking line-out) pin - default unmuted */ + {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, /* analog mix */ {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, /* unsolicited event for pin-sense */ {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, + {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, + {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, + /* allow to touch GPIO1 (for mute control) */ + {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */ + { } /* end */ +}; + +static struct hda_verb ad1884a_mobile_verbs[] = { + /* DACs; unmute as default */ + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ + /* Port-A (HP) mixer - route only from analog mixer */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Port-A pin */ + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Port-A (HP) pin - always unmuted */ + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Port-B (mic jack) pin */ + {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ + /* Port-C (int mic) pin */ + {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */ + /* Port-F (int speaker) mixer - route only from analog mixer */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Port-F pin */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Analog mixer; mute as default */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + /* Analog Mix output amp */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* capture sources */ + /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* unsolicited event for pin-sense */ + {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, + {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, + /* allow to touch GPIO1 (for mute control) */ + {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */ { } /* end */ }; @@ -3878,8 +4015,7 @@ static void ad1984a_thinkpad_automute(struct hda_codec *codec) { unsigned int present; - present = snd_hda_codec_read(codec, 0x11, 0, AC_VERB_GET_PIN_SENSE, 0) - & AC_PINSENSE_PRESENCE; + present = snd_hda_jack_detect(codec, 0x11); snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0, HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0); } @@ -3902,6 +4038,125 @@ static int ad1984a_thinkpad_init(struct hda_codec *codec) } /* + * HP Touchsmart + * port-A (0x11) - front hp-out + * port-B (0x14) - unused + * port-C (0x15) - unused + * port-D (0x12) - rear line out + * port-E (0x1c) - front mic-in + * port-F (0x16) - Internal speakers + * digital-mic (0x17) - Internal mic + */ + +static struct hda_verb ad1984a_touchsmart_verbs[] = { + /* DACs; unmute as default */ + {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ + {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */ + /* Port-A (HP) mixer - route only from analog mixer */ + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Port-A pin */ + {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + /* Port-A (HP) pin - always unmuted */ + {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + /* Port-E (int speaker) mixer - route only from analog mixer */ + {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03}, + /* Port-E pin */ + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE}, + {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80}, + /* Port-F (int speaker) mixer - route only from analog mixer */ + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)}, + /* Port-F pin */ + {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP}, + {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* Analog mixer; mute as default */ + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)}, + {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)}, + /* Analog Mix output amp */ + {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* capture sources */ + /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */ + {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0}, + {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE}, + /* unsolicited event for pin-sense */ + {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT}, + {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT}, + /* allow to touch GPIO1 (for mute control) */ + {0x01, AC_VERB_SET_GPIO_MASK, 0x02}, + {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02}, + {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */ + /* internal mic - dmic */ + {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN}, + /* set magic COEFs for dmic */ + {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7}, + {0x01, AC_VERB_SET_PROC_COEF, 0x08}, + { } /* end */ +}; + +static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = { + HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT), +/* HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, 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 = ad1884a_mobile_master_sw_put, + .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT), + }, + HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT), + HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Mic Boost", 0x25, 0x0, HDA_OUTPUT), + HDA_CODEC_VOLUME("Internal Mic Boost", 0x17, 0x0, HDA_INPUT), + { } /* end */ +}; + +/* switch to external mic if plugged */ +static void ad1984a_touchsmart_automic(struct hda_codec *codec) +{ + if (snd_hda_jack_detect(codec, 0x1c)) + snd_hda_codec_write(codec, 0x0c, 0, + AC_VERB_SET_CONNECT_SEL, 0x4); + else + snd_hda_codec_write(codec, 0x0c, 0, + AC_VERB_SET_CONNECT_SEL, 0x5); +} + + +/* unsolicited event for HP jack sensing */ +static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec, + unsigned int res) +{ + switch (res >> 26) { + case AD1884A_HP_EVENT: + ad1884a_hp_automute(codec); + break; + case AD1884A_MIC_EVENT: + ad1984a_touchsmart_automic(codec); + break; + } +} + +/* initialize jack-sensing, too */ +static int ad1984a_touchsmart_init(struct hda_codec *codec) +{ + ad198x_init(codec); + ad1884a_hp_automute(codec); + ad1984a_touchsmart_automic(codec); + return 0; +} + + +/* */ enum { @@ -3909,6 +4164,7 @@ enum { AD1884A_LAPTOP, AD1884A_MOBILE, AD1884A_THINKPAD, + AD1984A_TOUCHSMART, AD1884A_MODELS }; @@ -3917,6 +4173,7 @@ static const char *ad1884a_models[AD1884A_MODELS] = { [AD1884A_LAPTOP] = "laptop", [AD1884A_MOBILE] = "mobile", [AD1884A_THINKPAD] = "thinkpad", + [AD1984A_TOUCHSMART] = "touchsmart", }; static struct snd_pci_quirk ad1884a_cfg_tbl[] = { @@ -3924,10 +4181,12 @@ static struct snd_pci_quirk ad1884a_cfg_tbl[] = { SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP), SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE), SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE), - SND_PCI_QUIRK(0x103c, 0x30e6, "HP 6730b", AD1884A_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x30e7, "HP EliteBook 8530p", AD1884A_LAPTOP), - SND_PCI_QUIRK(0x103c, 0x3614, "HP 6730s", AD1884A_LAPTOP), + SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP), + SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP), + SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP), + SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE), SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD), + SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART), {} }; @@ -3976,16 +4235,31 @@ static int patch_ad1884a(struct hda_codec *codec) spec->mixers[0] = ad1884a_laptop_mixers; spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs; spec->multiout.dig_out_nid = 0; - spec->input_mux = &ad1884a_laptop_capture_source; - codec->patch_ops.unsol_event = ad1884a_hp_unsol_event; - codec->patch_ops.init = ad1884a_hp_init; + codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event; + codec->patch_ops.init = ad1884a_laptop_init; + /* set the upper-limit for mixer amp to 0dB for avoiding the + * possible damage by overloading + */ + snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); break; case AD1884A_MOBILE: spec->mixers[0] = ad1884a_mobile_mixers; - spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs; + spec->init_verbs[0] = ad1884a_mobile_verbs; spec->multiout.dig_out_nid = 0; codec->patch_ops.unsol_event = ad1884a_hp_unsol_event; codec->patch_ops.init = ad1884a_hp_init; + /* set the upper-limit for mixer amp to 0dB for avoiding the + * possible damage by overloading + */ + snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); break; case AD1884A_THINKPAD: spec->mixers[0] = ad1984a_thinkpad_mixers; @@ -3996,6 +4270,21 @@ static int patch_ad1884a(struct hda_codec *codec) codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event; codec->patch_ops.init = ad1984a_thinkpad_init; break; + case AD1984A_TOUCHSMART: + spec->mixers[0] = ad1984a_touchsmart_mixers; + spec->init_verbs[0] = ad1984a_touchsmart_verbs; + spec->multiout.dig_out_nid = 0; + codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event; + codec->patch_ops.init = ad1984a_touchsmart_init; + /* set the upper-limit for mixer amp to 0dB for avoiding the + * possible damage by overloading + */ + snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT, + (0x17 << AC_AMPCAP_OFFSET_SHIFT) | + (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) | + (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) | + (1 << AC_AMPCAP_MUTE_SHIFT)); + break; } return 0;