diff options
Diffstat (limited to 'sound/pci/hda/patch_realtek.c')
| -rw-r--r-- | sound/pci/hda/patch_realtek.c | 715 | 
1 files changed, 630 insertions, 85 deletions
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f15c36bde54..6bf47f7326a 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -34,7 +34,6 @@  #include "hda_codec.h"  #include "hda_local.h"  #include "hda_auto_parser.h" -#include "hda_beep.h"  #include "hda_jack.h"  #include "hda_generic.h" @@ -53,6 +52,20 @@ enum {  	ALC_INIT_GPIO3,  }; +enum { +	ALC_HEADSET_MODE_UNKNOWN, +	ALC_HEADSET_MODE_UNPLUGGED, +	ALC_HEADSET_MODE_HEADSET, +	ALC_HEADSET_MODE_MIC, +	ALC_HEADSET_MODE_HEADPHONE, +}; + +enum { +	ALC_HEADSET_TYPE_UNKNOWN, +	ALC_HEADSET_TYPE_CTIA, +	ALC_HEADSET_TYPE_OMTP, +}; +  struct alc_customize_define {  	unsigned int  sku_cfg;  	unsigned char port_connectivity; @@ -86,6 +99,13 @@ struct alc_spec {  	int mute_led_polarity;  	hda_nid_t mute_led_nid; +	unsigned int gpio_led; /* used for alc269_fixup_hp_gpio_led() */ + +	hda_nid_t headset_mic_pin; +	hda_nid_t headphone_mic_pin; +	int current_headset_mode; +	int current_headset_type; +  	/* hooks */  	void (*init_hook)(struct hda_codec *codec);  #ifdef CONFIG_PM @@ -805,17 +825,7 @@ static inline void alc_shutup(struct hda_codec *codec)  	snd_hda_shutup_pins(codec);  } -static void alc_free(struct hda_codec *codec) -{ -	struct alc_spec *spec = codec->spec; - -	if (!spec) -		return; - -	snd_hda_gen_spec_free(&spec->gen); -	snd_hda_detach_beep_device(codec); -	kfree(spec); -} +#define alc_free	snd_hda_gen_free  #ifdef CONFIG_PM  static void alc_power_eapd(struct hda_codec *codec) @@ -1401,6 +1411,7 @@ static int patch_alc880(struct hda_codec *codec)  	spec = codec->spec;  	spec->gen.need_dac_fix = 1; +	spec->gen.beep_nid = 0x01;  	snd_hda_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl,  		       alc880_fixups); @@ -1411,12 +1422,8 @@ static int patch_alc880(struct hda_codec *codec)  	if (err < 0)  		goto error; -	if (!spec->gen.no_analog) { -		err = snd_hda_attach_beep_device(codec, 0x1); -		if (err < 0) -			goto error; +	if (!spec->gen.no_analog)  		set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); -	}  	codec->patch_ops = alc_patch_ops;  	codec->patch_ops.unsol_event = alc880_unsol_event; @@ -1455,6 +1462,7 @@ enum {  	ALC260_FIXUP_HP_B1900,  	ALC260_FIXUP_KN1,  	ALC260_FIXUP_FSC_S7020, +	ALC260_FIXUP_FSC_S7020_JWSE,  };  static void alc260_gpio1_automute(struct hda_codec *codec) @@ -1516,14 +1524,17 @@ static void alc260_fixup_fsc_s7020(struct hda_codec *codec,  				   const struct hda_fixup *fix, int action)  {  	struct alc_spec *spec = codec->spec; - -	switch (action) { -	case HDA_FIXUP_ACT_PRE_PROBE: -		spec->gen.add_out_jack_modes = 1; -		break; -	case HDA_FIXUP_ACT_PROBE: +	if (action == HDA_FIXUP_ACT_PROBE)  		spec->init_amp = ALC_INIT_NONE; -		break; +} + +static void alc260_fixup_fsc_s7020_jwse(struct hda_codec *codec, +				   const struct hda_fixup *fix, int action) +{ +	struct alc_spec *spec = codec->spec; +	if (action == HDA_FIXUP_ACT_PRE_PROBE) { +		spec->gen.add_jack_modes = 1; +		spec->gen.hp_mic = 1;  	}  } @@ -1586,6 +1597,12 @@ static const struct hda_fixup alc260_fixups[] = {  		.type = HDA_FIXUP_FUNC,  		.v.func = alc260_fixup_fsc_s7020,  	}, +	[ALC260_FIXUP_FSC_S7020_JWSE] = { +		.type = HDA_FIXUP_FUNC, +		.v.func = alc260_fixup_fsc_s7020_jwse, +		.chained = true, +		.chain_id = ALC260_FIXUP_FSC_S7020, +	},  };  static const struct snd_pci_quirk alc260_fixup_tbl[] = { @@ -1602,6 +1619,14 @@ static const struct snd_pci_quirk alc260_fixup_tbl[] = {  	{}  }; +static const struct hda_model_fixup alc260_fixup_models[] = { +	{.id = ALC260_FIXUP_GPIO1, .name = "gpio1"}, +	{.id = ALC260_FIXUP_COEF, .name = "coef"}, +	{.id = ALC260_FIXUP_FSC_S7020, .name = "fujitsu"}, +	{.id = ALC260_FIXUP_FSC_S7020_JWSE, .name = "fujitsu-jwse"}, +	{} +}; +  /*   */  static int patch_alc260(struct hda_codec *codec) @@ -1619,8 +1644,10 @@ static int patch_alc260(struct hda_codec *codec)  	 * it's almost harmless.  	 */  	spec->gen.prefer_hp_amp = 1; +	spec->gen.beep_nid = 0x01; -	snd_hda_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups); +	snd_hda_pick_fixup(codec, alc260_fixup_models, alc260_fixup_tbl, +			   alc260_fixups);  	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);  	/* automatic parse from the BIOS config */ @@ -1628,12 +1655,8 @@ static int patch_alc260(struct hda_codec *codec)  	if (err < 0)  		goto error; -	if (!spec->gen.no_analog) { -		err = snd_hda_attach_beep_device(codec, 0x1); -		if (err < 0) -			goto error; +	if (!spec->gen.no_analog)  		set_beep_amp(spec, 0x07, 0x05, HDA_INPUT); -	}  	codec->patch_ops = alc_patch_ops;  	spec->shutup = alc_eapd_shutup; @@ -2132,17 +2155,16 @@ static int patch_alc882(struct hda_codec *codec)  	alc_auto_parse_customize_define(codec); +	if (has_cdefine_beep(codec)) +		spec->gen.beep_nid = 0x01; +  	/* automatic parse from the BIOS config */  	err = alc882_parse_auto_config(codec);  	if (err < 0)  		goto error; -	if (!spec->gen.no_analog && has_cdefine_beep(codec)) { -		err = snd_hda_attach_beep_device(codec, 0x1); -		if (err < 0) -			goto error; +	if (!spec->gen.no_analog && spec->gen.beep_nid)  		set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); -	}  	codec->patch_ops = alc_patch_ops; @@ -2295,17 +2317,16 @@ static int patch_alc262(struct hda_codec *codec)  	alc_auto_parse_customize_define(codec); +	if (has_cdefine_beep(codec)) +		spec->gen.beep_nid = 0x01; +  	/* automatic parse from the BIOS config */  	err = alc262_parse_auto_config(codec);  	if (err < 0)  		goto error; -	if (!spec->gen.no_analog && has_cdefine_beep(codec)) { -		err = snd_hda_attach_beep_device(codec, 0x1); -		if (err < 0) -			goto error; +	if (!spec->gen.no_analog && spec->gen.beep_nid)  		set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); -	}  	codec->patch_ops = alc_patch_ops;  	spec->shutup = alc_eapd_shutup; @@ -2386,16 +2407,7 @@ static const struct snd_pci_quirk alc268_fixup_tbl[] = {  static int alc268_parse_auto_config(struct hda_codec *codec)  {  	static const hda_nid_t alc268_ssids[] = { 0x15, 0x1b, 0x14, 0 }; -	struct alc_spec *spec = codec->spec; -	int err = alc_parse_auto_config(codec, NULL, alc268_ssids); -	if (err > 0) { -		if (!spec->gen.no_analog && -		    spec->gen.autocfg.speaker_pins[0] != 0x1d) { -			add_mixer(spec, alc268_beep_mixer); -			snd_hda_add_verbs(codec, alc268_beep_init_verbs); -		} -	} -	return err; +	return alc_parse_auto_config(codec, NULL, alc268_ssids);  }  /* @@ -2403,7 +2415,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec)  static int patch_alc268(struct hda_codec *codec)  {  	struct alc_spec *spec; -	int i, has_beep, err; +	int err;  	/* ALC268 has no aa-loopback mixer */  	err = alc_alloc_spec(codec, 0); @@ -2411,6 +2423,7 @@ static int patch_alc268(struct hda_codec *codec)  		return err;  	spec = codec->spec; +	spec->gen.beep_nid = 0x01;  	snd_hda_pick_fixup(codec, alc268_fixup_models, alc268_fixup_tbl, alc268_fixups);  	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); @@ -2420,18 +2433,10 @@ static int patch_alc268(struct hda_codec *codec)  	if (err < 0)  		goto error; -	has_beep = 0; -	for (i = 0; i < spec->num_mixers; i++) { -		if (spec->mixers[i] == alc268_beep_mixer) { -			has_beep = 1; -			break; -		} -	} - -	if (has_beep) { -		err = snd_hda_attach_beep_device(codec, 0x1); -		if (err < 0) -			goto error; +	if (err > 0 && !spec->gen.no_analog && +	    spec->gen.autocfg.speaker_pins[0] != 0x1d) { +		add_mixer(spec, alc268_beep_mixer); +		snd_hda_add_verbs(codec, alc268_beep_init_verbs);  		if (!query_amp_caps(codec, 0x1d, HDA_INPUT))  			/* override the amp caps for beep generator */  			snd_hda_override_amp_caps(codec, 0x1d, HDA_INPUT, @@ -2515,6 +2520,7 @@ enum {  	ALC269_TYPE_ALC280,  	ALC269_TYPE_ALC282,  	ALC269_TYPE_ALC284, +	ALC269_TYPE_ALC286,  };  /* @@ -2538,6 +2544,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)  	case ALC269_TYPE_ALC269VB:  	case ALC269_TYPE_ALC269VD:  	case ALC269_TYPE_ALC282: +	case ALC269_TYPE_ALC286:  		ssids = alc269_ssids;  		break;  	default: @@ -2631,7 +2638,8 @@ static void alc271_fixup_dmic(struct hda_codec *codec,  	};  	unsigned int cfg; -	if (strcmp(codec->chip_name, "ALC271X")) +	if (strcmp(codec->chip_name, "ALC271X") && +	    strcmp(codec->chip_name, "ALC269VB"))  		return;  	cfg = snd_hda_codec_get_pincfg(codec, 0x12);  	if (get_defcfg_connect(cfg) == AC_JACK_PORT_FIXED) @@ -2693,6 +2701,34 @@ static void alc269_fixup_quanta_mute(struct hda_codec *codec,  	spec->gen.automute_hook = alc269_quanta_automute;  } +static void alc269_x101_hp_automute_hook(struct hda_codec *codec, +					 struct hda_jack_tbl *jack) +{ +	struct alc_spec *spec = codec->spec; +	int vref; +	msleep(200); +	snd_hda_gen_hp_automute(codec, jack); + +	vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0; +	msleep(100); +	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, +			    vref); +	msleep(500); +	snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, +			    vref); +} + +static void alc269_fixup_x101_headset_mic(struct hda_codec *codec, +				     const struct hda_fixup *fix, int action) +{ +	struct alc_spec *spec = codec->spec; +	if (action == HDA_FIXUP_ACT_PRE_PROBE) { +		spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; +		spec->gen.hp_automute_hook = alc269_x101_hp_automute_hook; +	} +} + +  /* update mute-LED according to the speaker mute state via mic VREF pin */  static void alc269_fixup_mic_mute_hook(void *private_data, int enabled)  { @@ -2757,6 +2793,356 @@ static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec,  	}  } +/* turn on/off mute LED per vmaster hook */ +static void alc269_fixup_hp_gpio_mute_hook(void *private_data, int enabled) +{ +	struct hda_codec *codec = private_data; +	struct alc_spec *spec = codec->spec; +	unsigned int oldval = spec->gpio_led; + +	if (enabled) +		spec->gpio_led &= ~0x08; +	else +		spec->gpio_led |= 0x08; +	if (spec->gpio_led != oldval) +		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, +				    spec->gpio_led); +} + +/* turn on/off mic-mute LED per capture hook */ +static void alc269_fixup_hp_gpio_mic_mute_hook(struct hda_codec *codec, +			       struct snd_ctl_elem_value *ucontrol) +{ +	struct alc_spec *spec = codec->spec; +	unsigned int oldval = spec->gpio_led; + +	if (!ucontrol) +		return; + +	if (ucontrol->value.integer.value[0] || +	    ucontrol->value.integer.value[1]) +		spec->gpio_led &= ~0x10; +	else +		spec->gpio_led |= 0x10; +	if (spec->gpio_led != oldval) +		snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA, +				    spec->gpio_led); +} + +static void alc269_fixup_hp_gpio_led(struct hda_codec *codec, +				const struct hda_fixup *fix, int action) +{ +	struct alc_spec *spec = codec->spec; +	static const struct hda_verb gpio_init[] = { +		{ 0x01, AC_VERB_SET_GPIO_MASK, 0x18 }, +		{ 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x18 }, +		{} +	}; + +	if (action == HDA_FIXUP_ACT_PRE_PROBE) { +		spec->gen.vmaster_mute.hook = alc269_fixup_hp_gpio_mute_hook; +		spec->gen.cap_sync_hook = alc269_fixup_hp_gpio_mic_mute_hook; +		spec->gpio_led = 0; +		snd_hda_add_verbs(codec, gpio_init); +	} +} + +static void alc_headset_mode_unplugged(struct hda_codec *codec) +{ +	int val; + +	switch (codec->vendor_id) { +	case 0x10ec0283: +		alc_write_coef_idx(codec, 0x1b, 0x0c0b); +		alc_write_coef_idx(codec, 0x45, 0xc429); +		val = alc_read_coef_idx(codec, 0x35); +		alc_write_coef_idx(codec, 0x35, val & 0xbfff); +		alc_write_coef_idx(codec, 0x06, 0x2104); +		alc_write_coef_idx(codec, 0x1a, 0x0001); +		alc_write_coef_idx(codec, 0x26, 0x0004); +		alc_write_coef_idx(codec, 0x32, 0x42a3); +		break; +	case 0x10ec0292: +		alc_write_coef_idx(codec, 0x76, 0x000e); +		alc_write_coef_idx(codec, 0x6c, 0x2400); +		alc_write_coef_idx(codec, 0x18, 0x7308); +		alc_write_coef_idx(codec, 0x6b, 0xc429); +		break; +	case 0x10ec0668: +		alc_write_coef_idx(codec, 0x15, 0x0d40); +		alc_write_coef_idx(codec, 0xb7, 0x802b); +		break; +	} +	snd_printdd("Headset jack set to unplugged mode.\n"); +} + + +static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin, +				    hda_nid_t mic_pin) +{ +	int val; + +	switch (codec->vendor_id) { +	case 0x10ec0283: +		alc_write_coef_idx(codec, 0x45, 0xc429); +		snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); +		val = alc_read_coef_idx(codec, 0x35); +		alc_write_coef_idx(codec, 0x35, val | 1<<14); +		alc_write_coef_idx(codec, 0x06, 0x2100); +		alc_write_coef_idx(codec, 0x1a, 0x0021); +		alc_write_coef_idx(codec, 0x26, 0x008c); +		snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); +		break; +	case 0x10ec0292: +		snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); +		alc_write_coef_idx(codec, 0x19, 0xa208); +		alc_write_coef_idx(codec, 0x2e, 0xacf0); +		break; +	case 0x10ec0668: +		alc_write_coef_idx(codec, 0x11, 0x0001); +		snd_hda_set_pin_ctl_cache(codec, hp_pin, 0); +		alc_write_coef_idx(codec, 0xb7, 0x802b); +		alc_write_coef_idx(codec, 0xb5, 0x1040); +		val = alc_read_coef_idx(codec, 0xc3); +		alc_write_coef_idx(codec, 0xc3, val | 1<<12); +		snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50); +		break; +	} +	snd_printdd("Headset jack set to mic-in mode.\n"); +} + +static void alc_headset_mode_default(struct hda_codec *codec) +{ +	switch (codec->vendor_id) { +	case 0x10ec0283: +		alc_write_coef_idx(codec, 0x06, 0x2100); +		alc_write_coef_idx(codec, 0x32, 0x4ea3); +		break; +	case 0x10ec0292: +		alc_write_coef_idx(codec, 0x76, 0x000e); +		alc_write_coef_idx(codec, 0x6c, 0x2400); +		alc_write_coef_idx(codec, 0x6b, 0xc429); +		alc_write_coef_idx(codec, 0x18, 0x7308); +		break; +	case 0x10ec0668: +		alc_write_coef_idx(codec, 0x11, 0x0041); +		alc_write_coef_idx(codec, 0x15, 0x0d40); +		alc_write_coef_idx(codec, 0xb7, 0x802b); +		break; +	} +	snd_printdd("Headset jack set to headphone (default) mode.\n"); +} + +/* Iphone type */ +static void alc_headset_mode_ctia(struct hda_codec *codec) +{ +	switch (codec->vendor_id) { +	case 0x10ec0283: +		alc_write_coef_idx(codec, 0x45, 0xd429); +		alc_write_coef_idx(codec, 0x1b, 0x0c2b); +		alc_write_coef_idx(codec, 0x32, 0x4ea3); +		break; +	case 0x10ec0292: +		alc_write_coef_idx(codec, 0x6b, 0xd429); +		alc_write_coef_idx(codec, 0x76, 0x0008); +		alc_write_coef_idx(codec, 0x18, 0x7388); +		break; +	case 0x10ec0668: +		alc_write_coef_idx(codec, 0x15, 0x0d60); +		alc_write_coef_idx(codec, 0xc3, 0x0000); +		break; +	} +	snd_printdd("Headset jack set to iPhone-style headset mode.\n"); +} + +/* Nokia type */ +static void alc_headset_mode_omtp(struct hda_codec *codec) +{ +	switch (codec->vendor_id) { +	case 0x10ec0283: +		alc_write_coef_idx(codec, 0x45, 0xe429); +		alc_write_coef_idx(codec, 0x1b, 0x0c2b); +		alc_write_coef_idx(codec, 0x32, 0x4ea3); +		break; +	case 0x10ec0292: +		alc_write_coef_idx(codec, 0x6b, 0xe429); +		alc_write_coef_idx(codec, 0x76, 0x0008); +		alc_write_coef_idx(codec, 0x18, 0x7388); +		break; +	case 0x10ec0668: +		alc_write_coef_idx(codec, 0x15, 0x0d50); +		alc_write_coef_idx(codec, 0xc3, 0x0000); +		break; +	} +	snd_printdd("Headset jack set to Nokia-style headset mode.\n"); +} + +static void alc_determine_headset_type(struct hda_codec *codec) +{ +	int val; +	bool is_ctia = false; +	struct alc_spec *spec = codec->spec; + +	switch (codec->vendor_id) { +	case 0x10ec0283: +		alc_write_coef_idx(codec, 0x45, 0xd029); +		msleep(300); +		val = alc_read_coef_idx(codec, 0x46); +		is_ctia = (val & 0x0070) == 0x0070; +		break; +	case 0x10ec0292: +		alc_write_coef_idx(codec, 0x6b, 0xd429); +		msleep(300); +		val = alc_read_coef_idx(codec, 0x6c); +		is_ctia = (val & 0x001c) == 0x001c; +		break; +	case 0x10ec0668: +		alc_write_coef_idx(codec, 0x11, 0x0001); +		alc_write_coef_idx(codec, 0xb7, 0x802b); +		alc_write_coef_idx(codec, 0x15, 0x0d60); +		alc_write_coef_idx(codec, 0xc3, 0x0c00); +		msleep(300); +		val = alc_read_coef_idx(codec, 0xbe); +		is_ctia = (val & 0x1c02) == 0x1c02; +		break; +	} + +	snd_printdd("Headset jack detected iPhone-style headset: %s\n", +		    is_ctia ? "yes" : "no"); +	spec->current_headset_type = is_ctia ? ALC_HEADSET_TYPE_CTIA : ALC_HEADSET_TYPE_OMTP; +} + +static void alc_update_headset_mode(struct hda_codec *codec) +{ +	struct alc_spec *spec = codec->spec; + +	hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]]; +	hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0]; + +	int new_headset_mode; + +	if (!snd_hda_jack_detect(codec, hp_pin)) +		new_headset_mode = ALC_HEADSET_MODE_UNPLUGGED; +	else if (mux_pin == spec->headset_mic_pin) +		new_headset_mode = ALC_HEADSET_MODE_HEADSET; +	else if (mux_pin == spec->headphone_mic_pin) +		new_headset_mode = ALC_HEADSET_MODE_MIC; +	else +		new_headset_mode = ALC_HEADSET_MODE_HEADPHONE; + +	if (new_headset_mode == spec->current_headset_mode) +		return; + +	switch (new_headset_mode) { +	case ALC_HEADSET_MODE_UNPLUGGED: +		alc_headset_mode_unplugged(codec); +		spec->gen.hp_jack_present = false; +		break; +	case ALC_HEADSET_MODE_HEADSET: +		if (spec->current_headset_type == ALC_HEADSET_TYPE_UNKNOWN) +			alc_determine_headset_type(codec); +		if (spec->current_headset_type == ALC_HEADSET_TYPE_CTIA) +			alc_headset_mode_ctia(codec); +		else if (spec->current_headset_type == ALC_HEADSET_TYPE_OMTP) +			alc_headset_mode_omtp(codec); +		spec->gen.hp_jack_present = true; +		break; +	case ALC_HEADSET_MODE_MIC: +		alc_headset_mode_mic_in(codec, hp_pin, spec->headphone_mic_pin); +		spec->gen.hp_jack_present = false; +		break; +	case ALC_HEADSET_MODE_HEADPHONE: +		alc_headset_mode_default(codec); +		spec->gen.hp_jack_present = true; +		break; +	} +	if (new_headset_mode != ALC_HEADSET_MODE_MIC) { +		snd_hda_set_pin_ctl_cache(codec, hp_pin, +					  AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN); +		if (spec->headphone_mic_pin) +			snd_hda_set_pin_ctl_cache(codec, spec->headphone_mic_pin, +						  PIN_VREFHIZ); +	} +	spec->current_headset_mode = new_headset_mode; + +	snd_hda_gen_update_outputs(codec); +} + +static void alc_update_headset_mode_hook(struct hda_codec *codec, +			     struct snd_ctl_elem_value *ucontrol) +{ +	alc_update_headset_mode(codec); +} + +static void alc_update_headset_jack_cb(struct hda_codec *codec, struct hda_jack_tbl *jack) +{ +	struct alc_spec *spec = codec->spec; +	spec->current_headset_type = ALC_HEADSET_MODE_UNKNOWN; +	snd_hda_gen_hp_automute(codec, jack); +} + +static void alc_probe_headset_mode(struct hda_codec *codec) +{ +	int i; +	struct alc_spec *spec = codec->spec; +	struct auto_pin_cfg *cfg = &spec->gen.autocfg; + +	/* Find mic pins */ +	for (i = 0; i < cfg->num_inputs; i++) { +		if (cfg->inputs[i].is_headset_mic && !spec->headset_mic_pin) +			spec->headset_mic_pin = cfg->inputs[i].pin; +		if (cfg->inputs[i].is_headphone_mic && !spec->headphone_mic_pin) +			spec->headphone_mic_pin = cfg->inputs[i].pin; +	} + +	spec->gen.cap_sync_hook = alc_update_headset_mode_hook; +	spec->gen.automute_hook = alc_update_headset_mode; +	spec->gen.hp_automute_hook = alc_update_headset_jack_cb; +} + +static void alc_fixup_headset_mode(struct hda_codec *codec, +				const struct hda_fixup *fix, int action) +{ +	struct alc_spec *spec = codec->spec; + +	switch (action) { +	case HDA_FIXUP_ACT_PRE_PROBE: +		spec->parse_flags |= HDA_PINCFG_HEADSET_MIC | HDA_PINCFG_HEADPHONE_MIC; +		break; +	case HDA_FIXUP_ACT_PROBE: +		alc_probe_headset_mode(codec); +		break; +	case HDA_FIXUP_ACT_INIT: +		spec->current_headset_mode = 0; +		alc_update_headset_mode(codec); +		break; +	} +} + +static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec, +				const struct hda_fixup *fix, int action) +{ +	if (action == HDA_FIXUP_ACT_PRE_PROBE) { +		struct alc_spec *spec = codec->spec; +		spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; +	} +	else +		alc_fixup_headset_mode(codec, fix, action); +} + +static void alc_fixup_headset_mode_alc668(struct hda_codec *codec, +				const struct hda_fixup *fix, int action) +{ +	if (action == HDA_FIXUP_ACT_PRE_PROBE) { +		int val; +		alc_write_coef_idx(codec, 0xc4, 0x8000); +		val = alc_read_coef_idx(codec, 0xc2); +		alc_write_coef_idx(codec, 0xc2, val & 0xfe); +		snd_hda_set_pin_ctl_cache(codec, 0x18, 0); +	} +	alc_fixup_headset_mode(codec, fix, action); +} +  static void alc271_hp_gate_mic_jack(struct hda_codec *codec,  				    const struct hda_fixup *fix,  				    int action) @@ -2772,6 +3158,38 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec,  	}  } +static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec, +					     const struct hda_fixup *fix, +					     int action) +{ +	struct alc_spec *spec = codec->spec; +	struct auto_pin_cfg *cfg = &spec->gen.autocfg; +	int i; + +	/* The mic boosts on level 2 and 3 are too noisy +	   on the internal mic input. +	   Therefore limit the boost to 0 or 1. */ + +	if (action != HDA_FIXUP_ACT_PROBE) +		return; + +	for (i = 0; i < cfg->num_inputs; i++) { +		hda_nid_t nid = cfg->inputs[i].pin; +		unsigned int defcfg; +		if (cfg->inputs[i].type != AUTO_PIN_MIC) +			continue; +		defcfg = snd_hda_codec_get_pincfg(codec, nid); +		if (snd_hda_get_input_pin_attr(defcfg) != INPUT_PIN_ATTR_INT) +			continue; + +		snd_hda_override_amp_caps(codec, nid, HDA_INPUT, +					  (0x00 << AC_AMPCAP_OFFSET_SHIFT) | +					  (0x01 << AC_AMPCAP_NUM_STEPS_SHIFT) | +					  (0x2f << AC_AMPCAP_STEP_SIZE_SHIFT) | +					  (0 << AC_AMPCAP_MUTE_SHIFT)); +	} +} +  enum {  	ALC269_FIXUP_SONY_VAIO,  	ALC275_FIXUP_SONY_VAIO_GPIO2, @@ -2792,11 +3210,21 @@ enum {  	ALC269_FIXUP_HP_MUTE_LED,  	ALC269_FIXUP_HP_MUTE_LED_MIC1,  	ALC269_FIXUP_HP_MUTE_LED_MIC2, +	ALC269_FIXUP_HP_GPIO_LED,  	ALC269_FIXUP_INV_DMIC,  	ALC269_FIXUP_LENOVO_DOCK,  	ALC269_FIXUP_PINCFG_NO_HP_TO_LINEOUT, +	ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, +	ALC269_FIXUP_DELL2_MIC_NO_PRESENCE, +	ALC269_FIXUP_HEADSET_MODE, +	ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC, +	ALC269_FIXUP_ASUS_X101_FUNC, +	ALC269_FIXUP_ASUS_X101_VERB, +	ALC269_FIXUP_ASUS_X101,  	ALC271_FIXUP_AMIC_MIC2,  	ALC271_FIXUP_HP_GATE_MIC_JACK, +	ALC269_FIXUP_ACER_AC700, +	ALC269_FIXUP_LIMIT_INT_MIC_BOOST,  };  static const struct hda_fixup alc269_fixups[] = { @@ -2931,6 +3359,10 @@ static const struct hda_fixup alc269_fixups[] = {  		.type = HDA_FIXUP_FUNC,  		.v.func = alc269_fixup_hp_mute_led_mic2,  	}, +	[ALC269_FIXUP_HP_GPIO_LED] = { +		.type = HDA_FIXUP_FUNC, +		.v.func = alc269_fixup_hp_gpio_led, +	},  	[ALC269_FIXUP_INV_DMIC] = {  		.type = HDA_FIXUP_FUNC,  		.v.func = alc_fixup_inv_dmic_0x12, @@ -2949,6 +3381,59 @@ static const struct hda_fixup alc269_fixups[] = {  		.type = HDA_FIXUP_FUNC,  		.v.func = alc269_fixup_pincfg_no_hp_to_lineout,  	}, +	[ALC269_FIXUP_DELL1_MIC_NO_PRESENCE] = { +		.type = HDA_FIXUP_PINS, +		.v.pins = (const struct hda_pintbl[]) { +			{ 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */ +			{ 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */ +			{ } +		}, +		.chained = true, +		.chain_id = ALC269_FIXUP_HEADSET_MODE +	}, +	[ALC269_FIXUP_DELL2_MIC_NO_PRESENCE] = { +		.type = HDA_FIXUP_PINS, +		.v.pins = (const struct hda_pintbl[]) { +			{ 0x16, 0x21014020 }, /* dock line out */ +			{ 0x19, 0x21a19030 }, /* dock mic */ +			{ 0x1a, 0x01a1913c }, /* use as headset mic, without its own jack detect */ +			{ } +		}, +		.chained = true, +		.chain_id = ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC +	}, +	[ALC269_FIXUP_HEADSET_MODE] = { +		.type = HDA_FIXUP_FUNC, +		.v.func = alc_fixup_headset_mode, +	}, +	[ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC] = { +		.type = HDA_FIXUP_FUNC, +		.v.func = alc_fixup_headset_mode_no_hp_mic, +	}, +	[ALC269_FIXUP_ASUS_X101_FUNC] = { +		.type = HDA_FIXUP_FUNC, +		.v.func = alc269_fixup_x101_headset_mic, +	}, +	[ALC269_FIXUP_ASUS_X101_VERB] = { +		.type = HDA_FIXUP_VERBS, +		.v.verbs = (const struct hda_verb[]) { +			{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0}, +			{0x20, AC_VERB_SET_COEF_INDEX, 0x08}, +			{0x20, AC_VERB_SET_PROC_COEF,  0x0310}, +			{ } +		}, +		.chained = true, +		.chain_id = ALC269_FIXUP_ASUS_X101_FUNC +	}, +	[ALC269_FIXUP_ASUS_X101] = { +		.type = HDA_FIXUP_PINS, +		.v.pins = (const struct hda_pintbl[]) { +			{ 0x18, 0x04a1182c }, /* Headset mic */ +			{ } +		}, +		.chained = true, +		.chain_id = ALC269_FIXUP_ASUS_X101_VERB +	},  	[ALC271_FIXUP_AMIC_MIC2] = {  		.type = HDA_FIXUP_PINS,  		.v.pins = (const struct hda_pintbl[]) { @@ -2965,29 +3450,72 @@ static const struct hda_fixup alc269_fixups[] = {  		.chained = true,  		.chain_id = ALC271_FIXUP_AMIC_MIC2,  	}, +	[ALC269_FIXUP_ACER_AC700] = { +		.type = HDA_FIXUP_PINS, +		.v.pins = (const struct hda_pintbl[]) { +			{ 0x12, 0x99a3092f }, /* int-mic */ +			{ 0x14, 0x99130110 }, /* speaker */ +			{ 0x18, 0x03a11c20 }, /* mic */ +			{ 0x1e, 0x0346101e }, /* SPDIF1 */ +			{ 0x21, 0x0321101f }, /* HP out */ +			{ } +		}, +		.chained = true, +		.chain_id = ALC271_FIXUP_DMIC, +	}, +	[ALC269_FIXUP_LIMIT_INT_MIC_BOOST] = { +		.type = HDA_FIXUP_FUNC, +		.v.func = alc269_fixup_limit_int_mic_boost, +	},  };  static const struct snd_pci_quirk alc269_fixup_tbl[] = {  	SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC),  	SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC), +	SND_PCI_QUIRK(0x1028, 0x05bd, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05be, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05c4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05c5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05c6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05c7, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05c8, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05c9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05ca, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05cb, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05e9, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05ea, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05eb, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05ec, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05ed, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05ee, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05f3, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),  	SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), +	SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),  	SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),  	SND_PCI_QUIRK(0x103c, 0x1983, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),  	SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED), +	SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), +	SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),  	SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_DMIC),  	SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_DMIC), +	SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),  	SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),  	SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC), -	SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC), +	SND_PCI_QUIRK(0x1043, 0x1c23, "Asus X55U", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),  	SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC),  	SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC),  	SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC),  	SND_PCI_QUIRK(0x1043, 0x83ce, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC), +	SND_PCI_QUIRK(0x1043, 0x8516, "ASUS X101CH", ALC269_FIXUP_ASUS_X101),  	SND_PCI_QUIRK(0x104d, 0x9073, "Sony VAIO", ALC275_FIXUP_SONY_VAIO_GPIO2),  	SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),  	SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),  	SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),  	SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z), +	SND_PCI_QUIRK(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700),  	SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK),  	SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK),  	SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC), @@ -3063,6 +3591,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {  	{.id = ALC271_FIXUP_DMIC, .name = "alc271-dmic"},  	{.id = ALC269_FIXUP_INV_DMIC, .name = "inv-dmic"},  	{.id = ALC269_FIXUP_LENOVO_DOCK, .name = "lenovo-dock"}, +	{.id = ALC269_FIXUP_HP_GPIO_LED, .name = "hp-gpio-led"},  	{}  }; @@ -3131,6 +3660,9 @@ static int patch_alc269(struct hda_codec *codec)  	alc_auto_parse_customize_define(codec); +	if (has_cdefine_beep(codec)) +		spec->gen.beep_nid = 0x01; +  	switch (codec->vendor_id) {  	case 0x10ec0269:  		spec->codec_variant = ALC269_TYPE_ALC269VA; @@ -3172,6 +3704,9 @@ static int patch_alc269(struct hda_codec *codec)  	case 0x10ec0292:  		spec->codec_variant = ALC269_TYPE_ALC284;  		break; +	case 0x10ec0286: +		spec->codec_variant = ALC269_TYPE_ALC286; +		break;  	}  	/* automatic parse from the BIOS config */ @@ -3179,12 +3714,8 @@ static int patch_alc269(struct hda_codec *codec)  	if (err < 0)  		goto error; -	if (!spec->gen.no_analog && has_cdefine_beep(codec)) { -		err = snd_hda_attach_beep_device(codec, 0x1); -		if (err < 0) -			goto error; +	if (!spec->gen.no_analog && spec->gen.beep_nid)  		set_beep_amp(spec, 0x0b, 0x04, HDA_INPUT); -	}  	codec->patch_ops = alc_patch_ops;  #ifdef CONFIG_PM @@ -3292,6 +3823,7 @@ static int patch_alc861(struct hda_codec *codec)  		return err;  	spec = codec->spec; +	spec->gen.beep_nid = 0x23;  	snd_hda_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups);  	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); @@ -3301,12 +3833,8 @@ static int patch_alc861(struct hda_codec *codec)  	if (err < 0)  		goto error; -	if (!spec->gen.no_analog) { -		err = snd_hda_attach_beep_device(codec, 0x23); -		if (err < 0) -			goto error; +	if (!spec->gen.no_analog)  		set_beep_amp(spec, 0x23, 0, HDA_OUTPUT); -	}  	codec->patch_ops = alc_patch_ops;  #ifdef CONFIG_PM @@ -3387,6 +3915,7 @@ static int patch_alc861vd(struct hda_codec *codec)  		return err;  	spec = codec->spec; +	spec->gen.beep_nid = 0x23;  	snd_hda_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups);  	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); @@ -3396,12 +3925,8 @@ static int patch_alc861vd(struct hda_codec *codec)  	if (err < 0)  		goto error; -	if (!spec->gen.no_analog) { -		err = snd_hda_attach_beep_device(codec, 0x23); -		if (err < 0) -			goto error; +	if (!spec->gen.no_analog)  		set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); -	}  	codec->patch_ops = alc_patch_ops; @@ -3480,6 +4005,8 @@ enum {  	ALC662_FIXUP_NO_JACK_DETECT,  	ALC662_FIXUP_ZOTAC_Z68,  	ALC662_FIXUP_INV_DMIC, +	ALC668_FIXUP_DELL_MIC_NO_PRESENCE, +	ALC668_FIXUP_HEADSET_MODE,  };  static const struct hda_fixup alc662_fixups[] = { @@ -3640,6 +4167,20 @@ static const struct hda_fixup alc662_fixups[] = {  		.type = HDA_FIXUP_FUNC,  		.v.func = alc_fixup_inv_dmic_0x12,  	}, +	[ALC668_FIXUP_DELL_MIC_NO_PRESENCE] = { +		.type = HDA_FIXUP_PINS, +		.v.pins = (const struct hda_pintbl[]) { +			{ 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */ +			{ 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */ +			{ } +		}, +		.chained = true, +		.chain_id = ALC668_FIXUP_HEADSET_MODE +	}, +	[ALC668_FIXUP_HEADSET_MODE] = { +		.type = HDA_FIXUP_FUNC, +		.v.func = alc_fixup_headset_mode_alc668, +	},  };  static const struct snd_pci_quirk alc662_fixup_tbl[] = { @@ -3648,6 +4189,8 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {  	SND_PCI_QUIRK(0x1025, 0x031c, "Gateway NV79", ALC662_FIXUP_SKU_IGNORE),  	SND_PCI_QUIRK(0x1025, 0x0349, "eMachines eM250", ALC662_FIXUP_INV_DMIC),  	SND_PCI_QUIRK(0x1025, 0x038b, "Acer Aspire 8943G", ALC662_FIXUP_ASPIRE), +	SND_PCI_QUIRK(0x1028, 0x05d8, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), +	SND_PCI_QUIRK(0x1028, 0x05db, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),  	SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),  	SND_PCI_QUIRK(0x1043, 0x8469, "ASUS mobo", ALC662_FIXUP_NO_JACK_DETECT),  	SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2), @@ -3784,10 +4327,14 @@ static int patch_alc662(struct hda_codec *codec)  	alc_auto_parse_customize_define(codec); +	if (has_cdefine_beep(codec)) +		spec->gen.beep_nid = 0x01; +  	if ((alc_get_coef0(codec) & (1 << 14)) &&  	    codec->bus->pci->subsystem_vendor == 0x1025 &&  	    spec->cdefine.platform_type == 1) { -		if (alc_codec_rename(codec, "ALC272X") < 0) +		err = alc_codec_rename(codec, "ALC272X"); +		if (err < 0)  			goto error;  	} @@ -3796,10 +4343,7 @@ static int patch_alc662(struct hda_codec *codec)  	if (err < 0)  		goto error; -	if (!spec->gen.no_analog && has_cdefine_beep(codec)) { -		err = snd_hda_attach_beep_device(codec, 0x1); -		if (err < 0) -			goto error; +	if (!spec->gen.no_analog && spec->gen.beep_nid) {  		switch (codec->vendor_id) {  		case 0x10ec0662:  			set_beep_amp(spec, 0x0b, 0x05, HDA_INPUT); @@ -3878,6 +4422,7 @@ static const struct hda_codec_preset snd_hda_preset_realtek[] = {  	{ .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 },  	{ .id = 0x10ec0283, .name = "ALC283", .patch = patch_alc269 },  	{ .id = 0x10ec0284, .name = "ALC284", .patch = patch_alc269 }, +	{ .id = 0x10ec0286, .name = "ALC286", .patch = patch_alc269 },  	{ .id = 0x10ec0290, .name = "ALC290", .patch = patch_alc269 },  	{ .id = 0x10ec0292, .name = "ALC292", .patch = patch_alc269 },  	{ .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",  |