diff options
Diffstat (limited to 'sound/pci/hda/patch_analog.c')
| -rw-r--r-- | sound/pci/hda/patch_analog.c | 126 | 
1 files changed, 99 insertions, 27 deletions
diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c index 0cbdd87dde6..06275f8807a 100644 --- a/sound/pci/hda/patch_analog.c +++ b/sound/pci/hda/patch_analog.c @@ -21,7 +21,6 @@  #include <linux/init.h>  #include <linux/slab.h> -#include <linux/pci.h>  #include <linux/module.h>  #include <sound/core.h> @@ -139,6 +138,20 @@ static int ad198x_suspend(struct hda_codec *codec)  }  #endif +/* follow EAPD via vmaster hook */ +static void ad_vmaster_eapd_hook(void *private_data, int enabled) +{ +	struct hda_codec *codec = private_data; +	struct ad198x_spec *spec = codec->spec; + +	if (!spec->eapd_nid) +		return; +	if (codec->inv_eapd) +		enabled = !enabled; +	snd_hda_codec_update_cache(codec, spec->eapd_nid, 0, +				   AC_VERB_SET_EAPD_BTLENABLE, +				   enabled ? 0x02 : 0x00); +}  /*   * Automatic parse of I/O pins from the BIOS configuration @@ -171,7 +184,7 @@ static const struct hda_codec_ops ad198x_auto_patch_ops = {  }; -static int ad198x_parse_auto_config(struct hda_codec *codec) +static int ad198x_parse_auto_config(struct hda_codec *codec, bool indep_hp)  {  	struct ad198x_spec *spec = codec->spec;  	struct auto_pin_cfg *cfg = &spec->gen.autocfg; @@ -181,7 +194,8 @@ static int ad198x_parse_auto_config(struct hda_codec *codec)  	codec->no_trigger_sense = 1;  	codec->no_sticky_stream = 1; -	spec->gen.indep_hp = 1; +	spec->gen.indep_hp = indep_hp; +	spec->gen.add_stereo_mix_input = 1;  	err = snd_hda_parse_pin_defcfg(codec, cfg, NULL, 0);  	if (err < 0) @@ -219,8 +233,27 @@ static int alloc_ad_spec(struct hda_codec *codec)  static void ad_fixup_inv_jack_detect(struct hda_codec *codec,  				     const struct hda_fixup *fix, int action)  { -	if (action == HDA_FIXUP_ACT_PRE_PROBE) +	struct ad198x_spec *spec = codec->spec; + +	if (action == HDA_FIXUP_ACT_PRE_PROBE) {  		codec->inv_jack_detect = 1; +		spec->gen.keep_eapd_on = 1; +		spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; +		spec->eapd_nid = 0x1b; +	} +} + +/* Toshiba Satellite L40 implements EAPD in a standard way unlike others */ +static void ad1986a_fixup_eapd(struct hda_codec *codec, +			       const struct hda_fixup *fix, int action) +{ +	struct ad198x_spec *spec = codec->spec; + +	if (action == HDA_FIXUP_ACT_PRE_PROBE) { +		codec->inv_eapd = 0; +		spec->gen.keep_eapd_on = 1; +		spec->eapd_nid = 0x1b; +	}  }  enum { @@ -230,6 +263,7 @@ enum {  	AD1986A_FIXUP_3STACK,  	AD1986A_FIXUP_LAPTOP,  	AD1986A_FIXUP_LAPTOP_IMIC, +	AD1986A_FIXUP_EAPD,  };  static const struct hda_fixup ad1986a_fixups[] = { @@ -260,11 +294,11 @@ static const struct hda_fixup ad1986a_fixups[] = {  		.v.pins = (const struct hda_pintbl[]) {  			{ 0x1a, 0x02214021 }, /* headphone */  			{ 0x1b, 0x01014011 }, /* front */ -			{ 0x1c, 0x01013012 }, /* surround */ -			{ 0x1d, 0x01019015 }, /* clfe */ +			{ 0x1c, 0x01813030 }, /* line-in */ +			{ 0x1d, 0x01a19020 }, /* rear mic */  			{ 0x1e, 0x411111f0 }, /* N/A */  			{ 0x1f, 0x02a190f0 }, /* mic */ -			{ 0x20, 0x018130f0 }, /* line-in */ +			{ 0x20, 0x411111f0 }, /* N/A */  			{}  		},  	}, @@ -290,13 +324,19 @@ static const struct hda_fixup ad1986a_fixups[] = {  		.chained_before = 1,  		.chain_id = AD1986A_FIXUP_LAPTOP,  	}, +	[AD1986A_FIXUP_EAPD] = { +		.type = HDA_FIXUP_FUNC, +		.v.func = ad1986a_fixup_eapd, +	},  };  static const struct snd_pci_quirk ad1986a_fixup_tbl[] = {  	SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_FIXUP_LAPTOP_IMIC), +	SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8JN", AD1986A_FIXUP_EAPD),  	SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8100, "ASUS P5", AD1986A_FIXUP_3STACK),  	SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8200, "ASUS M2", AD1986A_FIXUP_3STACK),  	SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_FIXUP_3STACK), +	SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40", AD1986A_FIXUP_EAPD),  	SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_FIXUP_LAPTOP),  	SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_FIXUP_SAMSUNG),  	SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_FIXUP_ULTRA), @@ -320,6 +360,14 @@ static int patch_ad1986a(struct hda_codec *codec)  {  	int err;  	struct ad198x_spec *spec; +	static hda_nid_t preferred_pairs[] = { +		0x1a, 0x03, +		0x1b, 0x03, +		0x1c, 0x04, +		0x1d, 0x05, +		0x1e, 0x03, +		0 +	};  	err = alloc_ad_spec(codec);  	if (err < 0) @@ -340,12 +388,17 @@ static int patch_ad1986a(struct hda_codec *codec)  	 * So, let's disable the shared stream.  	 */  	spec->gen.multiout.no_share_stream = 1; +	/* give fixed DAC/pin pairs */ +	spec->gen.preferred_dacs = preferred_pairs; + +	/* AD1986A can't manage the dynamic pin on/off smoothly */ +	spec->gen.auto_mute_via_amp = 1;  	snd_hda_pick_fixup(codec, ad1986a_fixup_models, ad1986a_fixup_tbl,  			   ad1986a_fixups);  	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); -	err = ad198x_parse_auto_config(codec); +	err = ad198x_parse_auto_config(codec, false);  	if (err < 0) {  		snd_hda_gen_free(codec);  		return err; @@ -438,6 +491,8 @@ static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec)  static int patch_ad1983(struct hda_codec *codec)  {  	struct ad198x_spec *spec; +	static hda_nid_t conn_0c[] = { 0x08 }; +	static hda_nid_t conn_0d[] = { 0x09 };  	int err;  	err = alloc_ad_spec(codec); @@ -445,9 +500,15 @@ static int patch_ad1983(struct hda_codec *codec)  		return err;  	spec = codec->spec; +	spec->gen.mixer_nid = 0x0e;  	spec->gen.beep_nid = 0x10;  	set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); -	err = ad198x_parse_auto_config(codec); + +	/* limit the loopback routes not to confuse the parser */ +	snd_hda_override_conn_list(codec, 0x0c, ARRAY_SIZE(conn_0c), conn_0c); +	snd_hda_override_conn_list(codec, 0x0d, ARRAY_SIZE(conn_0d), conn_0d); + +	err = ad198x_parse_auto_config(codec, false);  	if (err < 0)  		goto error;  	err = ad1983_add_spdif_mux_ctl(codec); @@ -465,19 +526,6 @@ static int patch_ad1983(struct hda_codec *codec)   * AD1981 HD specific   */ -/* follow EAPD via vmaster hook */ -static void ad_vmaster_eapd_hook(void *private_data, int enabled) -{ -	struct hda_codec *codec = private_data; -	struct ad198x_spec *spec = codec->spec; - -	if (!spec->eapd_nid) -		return; -	snd_hda_codec_update_cache(codec, spec->eapd_nid, 0, -				   AC_VERB_SET_EAPD_BTLENABLE, -				   enabled ? 0x02 : 0x00); -} -  static void ad1981_fixup_hp_eapd(struct hda_codec *codec,  				 const struct hda_fixup *fix, int action)  { @@ -547,7 +595,7 @@ static int patch_ad1981(struct hda_codec *codec)  	snd_hda_pick_fixup(codec, NULL, ad1981_fixup_tbl, ad1981_fixups);  	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); -	err = ad198x_parse_auto_config(codec); +	err = ad198x_parse_auto_config(codec, false);  	if (err < 0)  		goto error;  	err = ad1983_add_spdif_mux_ctl(codec); @@ -873,7 +921,7 @@ static int patch_ad1988(struct hda_codec *codec)  	snd_hda_pick_fixup(codec, ad1988_fixup_models, NULL, ad1988_fixups);  	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); -	err = ad198x_parse_auto_config(codec); +	err = ad198x_parse_auto_config(codec, true);  	if (err < 0)  		goto error;  	err = ad1988_add_spdif_mux_ctl(codec); @@ -957,6 +1005,7 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec,  	switch (action) {  	case HDA_FIXUP_ACT_PRE_PROBE:  		spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook; +		spec->gen.own_eapd_ctl = 1;  		snd_hda_sequence_write_cache(codec, gpio_init_verbs);  		break;  	case HDA_FIXUP_ACT_PROBE: @@ -968,6 +1017,21 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec,  	}  } +static void ad1884_fixup_thinkpad(struct hda_codec *codec, +				  const struct hda_fixup *fix, int action) +{ +	struct ad198x_spec *spec = codec->spec; + +	if (action == HDA_FIXUP_ACT_PRE_PROBE) { +		spec->gen.keep_eapd_on = 1; +		spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook; +		spec->eapd_nid = 0x12; +		/* Analog PC Beeper - allow firmware/ACPI beeps */ +		spec->beep_amp = HDA_COMPOSE_AMP_VAL(0x20, 3, 3, HDA_INPUT); +		spec->gen.beep_nid = 0; /* no digital beep */ +	} +} +  /* set magic COEFs for dmic */  static const struct hda_verb ad1884_dmic_init_verbs[] = {  	{0x01, AC_VERB_SET_COEF_INDEX, 0x13f7}, @@ -979,6 +1043,7 @@ enum {  	AD1884_FIXUP_AMP_OVERRIDE,  	AD1884_FIXUP_HP_EAPD,  	AD1884_FIXUP_DMIC_COEF, +	AD1884_FIXUP_THINKPAD,  	AD1884_FIXUP_HP_TOUCHSMART,  }; @@ -997,6 +1062,12 @@ static const struct hda_fixup ad1884_fixups[] = {  		.type = HDA_FIXUP_VERBS,  		.v.verbs = ad1884_dmic_init_verbs,  	}, +	[AD1884_FIXUP_THINKPAD] = { +		.type = HDA_FIXUP_FUNC, +		.v.func = ad1884_fixup_thinkpad, +		.chained = true, +		.chain_id = AD1884_FIXUP_DMIC_COEF, +	},  	[AD1884_FIXUP_HP_TOUCHSMART] = {  		.type = HDA_FIXUP_VERBS,  		.v.verbs = ad1884_dmic_init_verbs, @@ -1008,7 +1079,7 @@ static const struct hda_fixup ad1884_fixups[] = {  static const struct snd_pci_quirk ad1884_fixup_tbl[] = {  	SND_PCI_QUIRK(0x103c, 0x2a82, "HP Touchsmart", AD1884_FIXUP_HP_TOUCHSMART),  	SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD), -	SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_DMIC_COEF), +	SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_THINKPAD),  	{}  }; @@ -1024,13 +1095,14 @@ static int patch_ad1884(struct hda_codec *codec)  	spec = codec->spec;  	spec->gen.mixer_nid = 0x20; +	spec->gen.mixer_merge_nid = 0x21;  	spec->gen.beep_nid = 0x10;  	set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);  	snd_hda_pick_fixup(codec, NULL, ad1884_fixup_tbl, ad1884_fixups);  	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); -	err = ad198x_parse_auto_config(codec); +	err = ad198x_parse_auto_config(codec, true);  	if (err < 0)  		goto error;  	err = ad1983_add_spdif_mux_ctl(codec); @@ -1072,7 +1144,7 @@ static int patch_ad1882(struct hda_codec *codec)  	spec->gen.mixer_merge_nid = 0x21;  	spec->gen.beep_nid = 0x10;  	set_beep_amp(spec, 0x10, 0, HDA_OUTPUT); -	err = ad198x_parse_auto_config(codec); +	err = ad198x_parse_auto_config(codec, true);  	if (err < 0)  		goto error;  	err = ad1988_add_spdif_mux_ctl(codec);  | 
