xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision 4961167bf7482944ca09a6f71263b9e47f949851)
1d0fa1179SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2c577b8a1SJoseph Chan /*
3c577b8a1SJoseph Chan  * Universal Interface for Intel High Definition Audio Codec
4c577b8a1SJoseph Chan  *
58e86597fSLydia Wang  * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
6c577b8a1SJoseph Chan  *
78e86597fSLydia Wang  *  (C) 2006-2009 VIA Technology, Inc.
88e86597fSLydia Wang  *  (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
9c577b8a1SJoseph Chan  */
10c577b8a1SJoseph Chan 
11c577b8a1SJoseph Chan /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
12c577b8a1SJoseph Chan /*									     */
13c577b8a1SJoseph Chan /* 2006-03-03  Lydia Wang  Create the basic patch to support VT1708 codec    */
14c577b8a1SJoseph Chan /* 2006-03-14  Lydia Wang  Modify hard code for some pin widget nid	     */
15c577b8a1SJoseph Chan /* 2006-08-02  Lydia Wang  Add support to VT1709 codec			     */
16c577b8a1SJoseph Chan /* 2006-09-08  Lydia Wang  Fix internal loopback recording source select bug */
17f7278fd0SJosepch Chan /* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization	     */
18f7278fd0SJosepch Chan /* 2007-09-17  Lydia Wang  Add VT1708B codec support			    */
1976d9b0ddSHarald Welte /* 2007-11-14  Lydia Wang  Add VT1708A codec HP and CD pin connect config    */
20fb4cb772SHarald Welte /* 2008-02-03  Lydia Wang  Fix Rear channels and Back channels inverse issue */
21d949cac1SHarald Welte /* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support	     */
2269e52a80SHarald Welte /* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin	     */
230aa62aefSHarald Welte /* 2008-04-09  Lydia Wang  Add Independent HP feature			     */
2498aa34c0SHarald Welte /* 2008-05-28  Lydia Wang  Add second S/PDIF Out support for VT1702	     */
25d7426329SHarald Welte /* 2008-09-15  Logan Li	   Add VT1708S Mic Boost workaround/backdoor	     */
268e86597fSLydia Wang /* 2009-02-16  Logan Li	   Add support for VT1718S			     */
278e86597fSLydia Wang /* 2009-03-13  Logan Li	   Add support for VT1716S			     */
288e86597fSLydia Wang /* 2009-04-14  Lydai Wang  Add support for VT1828S and VT2020		     */
298e86597fSLydia Wang /* 2009-07-08  Lydia Wang  Add support for VT2002P			     */
308e86597fSLydia Wang /* 2009-07-21  Lydia Wang  Add support for VT1812			     */
3136dd5c4aSLydia Wang /* 2009-09-19  Lydia Wang  Add support for VT1818S			     */
32c577b8a1SJoseph Chan /*									     */
33c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
34c577b8a1SJoseph Chan 
35c577b8a1SJoseph Chan 
36c577b8a1SJoseph Chan #include <linux/init.h>
37c577b8a1SJoseph Chan #include <linux/delay.h>
38c577b8a1SJoseph Chan #include <linux/slab.h>
39da155d5bSPaul Gortmaker #include <linux/module.h>
40c577b8a1SJoseph Chan #include <sound/core.h>
410aa62aefSHarald Welte #include <sound/asoundef.h>
42be57bfffSPierre-Louis Bossart #include <sound/hda_codec.h>
43c577b8a1SJoseph Chan #include "hda_local.h"
44128bc4baSTakashi Iwai #include "hda_auto_parser.h"
451835a0f9STakashi Iwai #include "hda_jack.h"
46b3f6008fSTakashi Iwai #include "hda_generic.h"
47c577b8a1SJoseph Chan 
48c577b8a1SJoseph Chan /* Pin Widget NID */
4976d9b0ddSHarald Welte #define VT1708_HP_PIN_NID	0x20
5076d9b0ddSHarald Welte #define VT1708_CD_PIN_NID	0x24
51c577b8a1SJoseph Chan 
52d7426329SHarald Welte enum VIA_HDA_CODEC {
53d7426329SHarald Welte 	UNKNOWN = -1,
54d7426329SHarald Welte 	VT1708,
55d7426329SHarald Welte 	VT1709_10CH,
56d7426329SHarald Welte 	VT1709_6CH,
57d7426329SHarald Welte 	VT1708B_8CH,
58d7426329SHarald Welte 	VT1708B_4CH,
59d7426329SHarald Welte 	VT1708S,
60518bf3baSLydia Wang 	VT1708BCE,
61d7426329SHarald Welte 	VT1702,
62eb7188caSLydia Wang 	VT1718S,
63f3db423dSLydia Wang 	VT1716S,
6425eaba2fSLydia Wang 	VT2002P,
65ab6734e7SLydia Wang 	VT1812,
6611890956SLydia Wang 	VT1802,
6743737e0aSLydia Wang 	VT1705CF,
686121b84aSLydia Wang 	VT1808,
69d7426329SHarald Welte 	CODEC_TYPES,
70d7426329SHarald Welte };
71d7426329SHarald Welte 
7211890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \
7311890956SLydia Wang 	((spec)->codec_type == VT2002P ||\
7411890956SLydia Wang 	 (spec)->codec_type == VT1812 ||\
7511890956SLydia Wang 	 (spec)->codec_type == VT1802)
7611890956SLydia Wang 
771f2e99feSLydia Wang struct via_spec {
78b3f6008fSTakashi Iwai 	struct hda_gen_spec gen;
79b3f6008fSTakashi Iwai 
801f2e99feSLydia Wang 	/* HP mode source */
81f3db423dSLydia Wang 	unsigned int dmic_enabled;
821f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
831f2e99feSLydia Wang 
84e9d010c2STakashi Iwai 	/* analog low-power control */
85e9d010c2STakashi Iwai 	bool alc_mode;
86e9d010c2STakashi Iwai 
871f2e99feSLydia Wang 	/* work to check hp jack state */
88187d333eSTakashi Iwai 	int hp_work_active;
89e06e5a29STakashi Iwai 	int vt1708_jack_detect;
901f2e99feSLydia Wang };
911f2e99feSLydia Wang 
920341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
93b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
94b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
95b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
96b3f6008fSTakashi Iwai 				  int action);
97b3f6008fSTakashi Iwai 
98225068abSTakashi Iwai static const struct hda_codec_ops via_patch_ops; /* defined below */
99225068abSTakashi Iwai 
1005b0cb1d8SJaroslav Kysela static struct via_spec *via_new_spec(struct hda_codec *codec)
1015b0cb1d8SJaroslav Kysela {
1025b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1035b0cb1d8SJaroslav Kysela 
1045b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1055b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1065b0cb1d8SJaroslav Kysela 		return NULL;
1075b0cb1d8SJaroslav Kysela 
1085b0cb1d8SJaroslav Kysela 	codec->spec = spec;
109b3f6008fSTakashi Iwai 	snd_hda_gen_spec_init(&spec->gen);
1100341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
1110341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
1120341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
1130341ccd7SLydia Wang 		spec->codec_type = VT1708S;
11413961170STakashi Iwai 	spec->gen.indep_hp = 1;
11505909d5cSTakashi Iwai 	spec->gen.keep_eapd_on = 1;
11667ea698cSTakashi Iwai 	spec->gen.dac_min_mute = 1;
117b3f6008fSTakashi Iwai 	spec->gen.pcm_playback_hook = via_playback_pcm_hook;
11874f14b36STakashi Iwai 	spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
119967b1307STakashi Iwai 	codec->power_save_node = 1;
120688b12ccSTakashi Iwai 	spec->gen.power_down_unused = 1;
121225068abSTakashi Iwai 	codec->patch_ops = via_patch_ops;
1225b0cb1d8SJaroslav Kysela 	return spec;
1235b0cb1d8SJaroslav Kysela }
1245b0cb1d8SJaroslav Kysela 
125744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
126d7426329SHarald Welte {
1277639a06cSTakashi Iwai 	u32 vendor_id = codec->core.vendor_id;
128d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
129d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
130d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
131d7426329SHarald Welte 
132d7426329SHarald Welte 	/* get codec type */
133d7426329SHarald Welte 	if (ven_id != 0x1106)
134d7426329SHarald Welte 		codec_type = UNKNOWN;
135d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
136d7426329SHarald Welte 		codec_type = VT1708;
137d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
138d7426329SHarald Welte 		codec_type = VT1709_10CH;
139d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
140d7426329SHarald Welte 		codec_type = VT1709_6CH;
141518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
142d7426329SHarald Welte 		codec_type = VT1708B_8CH;
143518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
144518bf3baSLydia Wang 			codec_type = VT1708BCE;
145518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
146d7426329SHarald Welte 		codec_type = VT1708B_4CH;
147d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
148d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
149d7426329SHarald Welte 		codec_type = VT1708S;
150d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
151d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
152d7426329SHarald Welte 		codec_type = VT1702;
153eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
154eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
155eb7188caSLydia Wang 		codec_type = VT1718S;
156f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
157f3db423dSLydia Wang 		codec_type = VT1716S;
158bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
159bb3c6bfcSLydia Wang 		codec_type = VT1718S;
16025eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
16125eaba2fSLydia Wang 		codec_type = VT2002P;
162ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
163ab6734e7SLydia Wang 		codec_type = VT1812;
16436dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
16536dd5c4aSLydia Wang 		codec_type = VT1708S;
16611890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
16711890956SLydia Wang 		codec_type = VT1802;
16843737e0aSLydia Wang 	else if (dev_id == 0x4760)
16943737e0aSLydia Wang 		codec_type = VT1705CF;
1706121b84aSLydia Wang 	else if (dev_id == 0x4761 || dev_id == 0x4762)
1716121b84aSLydia Wang 		codec_type = VT1808;
172d7426329SHarald Welte 	else
173d7426329SHarald Welte 		codec_type = UNKNOWN;
174d7426329SHarald Welte 	return codec_type;
175d7426329SHarald Welte };
176d7426329SHarald Welte 
177ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec);
178ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec);
1791f2e99feSLydia Wang 
180187d333eSTakashi Iwai #define hp_detect_with_aa(codec) \
181187d333eSTakashi Iwai 	(snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
182187d333eSTakashi Iwai 	 !is_aa_path_mute(codec))
1831f2e99feSLydia Wang 
184b3f6008fSTakashi Iwai static void vt1708_stop_hp_work(struct hda_codec *codec)
1851f2e99feSLydia Wang {
186b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
187b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
1881f2e99feSLydia Wang 		return;
189187d333eSTakashi Iwai 	if (spec->hp_work_active) {
190b3f6008fSTakashi Iwai 		snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
1917eaa9161SWang Xingchao 		codec->jackpoll_interval = 0;
192b3f6008fSTakashi Iwai 		cancel_delayed_work_sync(&codec->jackpoll_work);
193b3f6008fSTakashi Iwai 		spec->hp_work_active = false;
194187d333eSTakashi Iwai 	}
195187d333eSTakashi Iwai }
196187d333eSTakashi Iwai 
197b3f6008fSTakashi Iwai static void vt1708_update_hp_work(struct hda_codec *codec)
198187d333eSTakashi Iwai {
199b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
200b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
201187d333eSTakashi Iwai 		return;
20205dc0fc9SDavid Henningsson 	if (spec->vt1708_jack_detect) {
203187d333eSTakashi Iwai 		if (!spec->hp_work_active) {
204b3f6008fSTakashi Iwai 			codec->jackpoll_interval = msecs_to_jiffies(100);
205b3f6008fSTakashi Iwai 			snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0);
2062f35c630STakashi Iwai 			schedule_delayed_work(&codec->jackpoll_work, 0);
207b3f6008fSTakashi Iwai 			spec->hp_work_active = true;
208187d333eSTakashi Iwai 		}
209b3f6008fSTakashi Iwai 	} else if (!hp_detect_with_aa(codec))
210b3f6008fSTakashi Iwai 		vt1708_stop_hp_work(codec);
2111f2e99feSLydia Wang }
212f5271101SLydia Wang 
21324088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
21424088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
21524088a58STakashi Iwai {
216dda415d4STakashi Iwai 	return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
21724088a58STakashi Iwai }
21824088a58STakashi Iwai 
21924088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
22024088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
22124088a58STakashi Iwai {
22224088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
223735c75cfSTakashi Iwai 	struct via_spec *spec = codec->spec;
224735c75cfSTakashi Iwai 
225735c75cfSTakashi Iwai 	ucontrol->value.enumerated.item[0] = spec->gen.power_down_unused;
22624088a58STakashi Iwai 	return 0;
22724088a58STakashi Iwai }
22824088a58STakashi Iwai 
22924088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
23024088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
23124088a58STakashi Iwai {
23224088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
23324088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
234688b12ccSTakashi Iwai 	bool val = !!ucontrol->value.enumerated.item[0];
23524088a58STakashi Iwai 
236735c75cfSTakashi Iwai 	if (val == spec->gen.power_down_unused)
23724088a58STakashi Iwai 		return 0;
238735c75cfSTakashi Iwai 	/* codec->power_save_node = val; */ /* widget PM seems yet broken */
239688b12ccSTakashi Iwai 	spec->gen.power_down_unused = val;
240e9d010c2STakashi Iwai 	analog_low_current_mode(codec);
24124088a58STakashi Iwai 	return 1;
24224088a58STakashi Iwai }
24324088a58STakashi Iwai 
2440e8f9862STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
24524088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
24624088a58STakashi Iwai 	.name = "Dynamic Power-Control",
24724088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
24824088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
24924088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
25024088a58STakashi Iwai };
25124088a58STakashi Iwai 
2524738465cSW. Trevor King #ifdef CONFIG_SND_HDA_INPUT_BEEP
2534738465cSW. Trevor King /* additional beep mixers; the actual parameters are overwritten at build */
2540e8f9862STakashi Iwai static const struct snd_kcontrol_new via_beep_mixer[] = {
2554738465cSW. Trevor King 	HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
2564738465cSW. Trevor King 	HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
2574738465cSW. Trevor King };
2584738465cSW. Trevor King 
2590e8f9862STakashi Iwai static int set_beep_amp(struct via_spec *spec, hda_nid_t nid,
2600e8f9862STakashi Iwai 			int idx, int dir)
2614738465cSW. Trevor King {
2620e8f9862STakashi Iwai 	struct snd_kcontrol_new *knew;
2630e8f9862STakashi Iwai 	unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
2640e8f9862STakashi Iwai 	int i;
2654738465cSW. Trevor King 
2660e8f9862STakashi Iwai 	spec->gen.beep_nid = nid;
2670e8f9862STakashi Iwai 	for (i = 0; i < ARRAY_SIZE(via_beep_mixer); i++) {
2680e8f9862STakashi Iwai 		knew = snd_hda_gen_add_kctl(&spec->gen, NULL,
2690e8f9862STakashi Iwai 					    &via_beep_mixer[i]);
2700e8f9862STakashi Iwai 		if (!knew)
2714738465cSW. Trevor King 			return -ENOMEM;
2720e8f9862STakashi Iwai 		knew->private_value = beep_amp;
2734738465cSW. Trevor King 	}
2744738465cSW. Trevor King 	return 0;
2754738465cSW. Trevor King }
2764738465cSW. Trevor King 
2770e8f9862STakashi Iwai static int auto_parse_beep(struct hda_codec *codec)
2784738465cSW. Trevor King {
2794738465cSW. Trevor King 	struct via_spec *spec = codec->spec;
2804738465cSW. Trevor King 	hda_nid_t nid;
2814738465cSW. Trevor King 
2824738465cSW. Trevor King 	for_each_hda_codec_node(nid, codec)
2830e8f9862STakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP)
2840e8f9862STakashi Iwai 			return set_beep_amp(spec, nid, 0, HDA_OUTPUT);
2850e8f9862STakashi Iwai 	return 0;
2864738465cSW. Trevor King }
2874738465cSW. Trevor King #else
2880e8f9862STakashi Iwai #define auto_parse_beep(codec)	0
2894738465cSW. Trevor King #endif
29024088a58STakashi Iwai 
291f5271101SLydia Wang /* check AA path's mute status */
292ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec)
293ada509ecSTakashi Iwai {
294ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
295ada509ecSTakashi Iwai 	const struct hda_amp_list *p;
2960186f4f4STakashi Iwai 	int ch, v;
297ada509ecSTakashi Iwai 
2980186f4f4STakashi Iwai 	p = spec->gen.loopback.amplist;
2990186f4f4STakashi Iwai 	if (!p)
3000186f4f4STakashi Iwai 		return true;
3010186f4f4STakashi Iwai 	for (; p->nid; p++) {
302ada509ecSTakashi Iwai 		for (ch = 0; ch < 2; ch++) {
303ada509ecSTakashi Iwai 			v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
304ada509ecSTakashi Iwai 						   p->idx);
305ada509ecSTakashi Iwai 			if (!(v & HDA_AMP_MUTE) && v > 0)
306ada509ecSTakashi Iwai 				return false;
307f5271101SLydia Wang 		}
308f5271101SLydia Wang 	}
309ada509ecSTakashi Iwai 	return true;
310f5271101SLydia Wang }
311f5271101SLydia Wang 
312f5271101SLydia Wang /* enter/exit analog low-current mode */
313e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force)
314f5271101SLydia Wang {
315f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
316ada509ecSTakashi Iwai 	bool enable;
317ada509ecSTakashi Iwai 	unsigned int verb, parm;
318f5271101SLydia Wang 
319967b1307STakashi Iwai 	if (!codec->power_save_node)
320e9d010c2STakashi Iwai 		enable = false;
321e9d010c2STakashi Iwai 	else
322b3f6008fSTakashi Iwai 		enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
323e9d010c2STakashi Iwai 	if (enable == spec->alc_mode && !force)
324e9d010c2STakashi Iwai 		return;
325e9d010c2STakashi Iwai 	spec->alc_mode = enable;
326f5271101SLydia Wang 
327f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
328f5271101SLydia Wang 	switch (spec->codec_type) {
329f5271101SLydia Wang 	case VT1708B_8CH:
330f5271101SLydia Wang 	case VT1708B_4CH:
331f5271101SLydia Wang 		verb = 0xf70;
332f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
333f5271101SLydia Wang 		break;
334f5271101SLydia Wang 	case VT1708S:
335eb7188caSLydia Wang 	case VT1718S:
336f3db423dSLydia Wang 	case VT1716S:
337f5271101SLydia Wang 		verb = 0xf73;
338f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
339f5271101SLydia Wang 		break;
340f5271101SLydia Wang 	case VT1702:
341f5271101SLydia Wang 		verb = 0xf73;
342f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
343f5271101SLydia Wang 		break;
34425eaba2fSLydia Wang 	case VT2002P:
345ab6734e7SLydia Wang 	case VT1812:
34611890956SLydia Wang 	case VT1802:
34725eaba2fSLydia Wang 		verb = 0xf93;
34825eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
34925eaba2fSLydia Wang 		break;
35043737e0aSLydia Wang 	case VT1705CF:
3516121b84aSLydia Wang 	case VT1808:
35243737e0aSLydia Wang 		verb = 0xf82;
35343737e0aSLydia Wang 		parm = enable ? 0x00 : 0xe0;  /* 0x00: 4/40x, 0xe0: 1x */
35443737e0aSLydia Wang 		break;
355f5271101SLydia Wang 	default:
356f5271101SLydia Wang 		return;		/* other codecs are not supported */
357f5271101SLydia Wang 	}
358f5271101SLydia Wang 	/* send verb */
3597639a06cSTakashi Iwai 	snd_hda_codec_write(codec, codec->core.afg, 0, verb, parm);
360f5271101SLydia Wang }
361f5271101SLydia Wang 
362e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec)
363e9d010c2STakashi Iwai {
364e9d010c2STakashi Iwai 	return __analog_low_current_mode(codec, false);
365e9d010c2STakashi Iwai }
366e9d010c2STakashi Iwai 
367b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
368b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
369b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
370b3f6008fSTakashi Iwai 				  int action)
371c577b8a1SJoseph Chan {
372b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
373b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
374c577b8a1SJoseph Chan }
375c577b8a1SJoseph Chan 
376c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
377c577b8a1SJoseph Chan {
378b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
379a8dca460STakashi Iwai 	snd_hda_gen_free(codec);
380c577b8a1SJoseph Chan }
381c577b8a1SJoseph Chan 
3822a43952aSTakashi Iwai #ifdef CONFIG_PM
38368cb2b55STakashi Iwai static int via_suspend(struct hda_codec *codec)
3841f2e99feSLydia Wang {
3851f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
386b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
38794c142a1SDavid Henningsson 
38894c142a1SDavid Henningsson 	/* Fix pop noise on headphones */
3892c38d990STakashi Iwai 	if (spec->codec_type == VT1802)
3902c38d990STakashi Iwai 		snd_hda_shutup_pins(codec);
39194c142a1SDavid Henningsson 
3921f2e99feSLydia Wang 	return 0;
3931f2e99feSLydia Wang }
3946b6d0007STakashi Iwai 
3956b6d0007STakashi Iwai static int via_resume(struct hda_codec *codec)
3966b6d0007STakashi Iwai {
3976b6d0007STakashi Iwai 	/* some delay here to make jack detection working (bko#98921) */
3986b6d0007STakashi Iwai 	msleep(10);
3996b6d0007STakashi Iwai 	codec->patch_ops.init(codec);
4001a462be5STakashi Iwai 	snd_hda_regmap_sync(codec);
4016b6d0007STakashi Iwai 	return 0;
4026b6d0007STakashi Iwai }
4031f2e99feSLydia Wang #endif
4041f2e99feSLydia Wang 
40583012a7cSTakashi Iwai #ifdef CONFIG_PM
406cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
407cb53c626STakashi Iwai {
408cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
409b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
410b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
411b3f6008fSTakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
412cb53c626STakashi Iwai }
413cb53c626STakashi Iwai #endif
414cb53c626STakashi Iwai 
415c577b8a1SJoseph Chan /*
416c577b8a1SJoseph Chan  */
4175d41762aSTakashi Iwai 
4185d41762aSTakashi Iwai static int via_init(struct hda_codec *codec);
4195d41762aSTakashi Iwai 
42090dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
4210e8f9862STakashi Iwai 	.build_controls = snd_hda_gen_build_controls,
422b3f6008fSTakashi Iwai 	.build_pcms = snd_hda_gen_build_pcms,
423c577b8a1SJoseph Chan 	.init = via_init,
424c577b8a1SJoseph Chan 	.free = via_free,
4254e2d16d3SDavid Henningsson 	.unsol_event = snd_hda_jack_unsol_event,
4262a43952aSTakashi Iwai #ifdef CONFIG_PM
4271f2e99feSLydia Wang 	.suspend = via_suspend,
4286b6d0007STakashi Iwai 	.resume = via_resume,
429cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
430cb53c626STakashi Iwai #endif
431c577b8a1SJoseph Chan };
432c577b8a1SJoseph Chan 
4334a79616dSTakashi Iwai 
434b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = {
435b3f6008fSTakashi Iwai 	/* power down jack detect function */
436b3f6008fSTakashi Iwai 	{0x1, 0xf81, 0x1},
437b3f6008fSTakashi Iwai 	{ }
4384a79616dSTakashi Iwai };
43976d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
44076d9b0ddSHarald Welte {
44176d9b0ddSHarald Welte 	unsigned int def_conf;
44276d9b0ddSHarald Welte 	unsigned char seqassoc;
44376d9b0ddSHarald Welte 
4442f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
44576d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
44676d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
44782ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
44882ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
44976d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
4502f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
45176d9b0ddSHarald Welte 	}
45276d9b0ddSHarald Welte 
45376d9b0ddSHarald Welte 	return;
45476d9b0ddSHarald Welte }
45576d9b0ddSHarald Welte 
456e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
4571f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
4581f2e99feSLydia Wang {
4591f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4601f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
4611f2e99feSLydia Wang 
4621f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
4631f2e99feSLydia Wang 		return 0;
464e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
4651f2e99feSLydia Wang 	return 0;
4661f2e99feSLydia Wang }
4671f2e99feSLydia Wang 
468e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
4691f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
4701f2e99feSLydia Wang {
4711f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4721f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
473187d333eSTakashi Iwai 	int val;
4741f2e99feSLydia Wang 
4751f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
4761f2e99feSLydia Wang 		return 0;
477187d333eSTakashi Iwai 	val = !!ucontrol->value.integer.value[0];
478187d333eSTakashi Iwai 	if (spec->vt1708_jack_detect == val)
479187d333eSTakashi Iwai 		return 0;
480187d333eSTakashi Iwai 	spec->vt1708_jack_detect = val;
481b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
482187d333eSTakashi Iwai 	return 1;
4831f2e99feSLydia Wang }
4841f2e99feSLydia Wang 
4850e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
4861f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4871f2e99feSLydia Wang 	.name = "Jack Detect",
4881f2e99feSLydia Wang 	.count = 1,
4891f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
490e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
491e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
4921f2e99feSLydia Wang };
4931f2e99feSLydia Wang 
4944abdbd1cSTakashi Iwai static const struct badness_table via_main_out_badness = {
4954abdbd1cSTakashi Iwai 	.no_primary_dac = 0x10000,
4964abdbd1cSTakashi Iwai 	.no_dac = 0x4000,
4974abdbd1cSTakashi Iwai 	.shared_primary = 0x10000,
4984abdbd1cSTakashi Iwai 	.shared_surr = 0x20,
4994abdbd1cSTakashi Iwai 	.shared_clfe = 0x20,
5004abdbd1cSTakashi Iwai 	.shared_surr_main = 0x20,
5014abdbd1cSTakashi Iwai };
5024abdbd1cSTakashi Iwai static const struct badness_table via_extra_out_badness = {
5034abdbd1cSTakashi Iwai 	.no_primary_dac = 0x4000,
5044abdbd1cSTakashi Iwai 	.no_dac = 0x4000,
5054abdbd1cSTakashi Iwai 	.shared_primary = 0x12,
5064abdbd1cSTakashi Iwai 	.shared_surr = 0x20,
5074abdbd1cSTakashi Iwai 	.shared_clfe = 0x20,
5084abdbd1cSTakashi Iwai 	.shared_surr_main = 0x10,
5094abdbd1cSTakashi Iwai };
5104abdbd1cSTakashi Iwai 
511b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec)
512b3f6008fSTakashi Iwai {
513b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
514b3f6008fSTakashi Iwai 	int err;
515b3f6008fSTakashi Iwai 
5164abdbd1cSTakashi Iwai 	spec->gen.main_out_badness = &via_main_out_badness;
5174abdbd1cSTakashi Iwai 	spec->gen.extra_out_badness = &via_extra_out_badness;
5184abdbd1cSTakashi Iwai 
519b3f6008fSTakashi Iwai 	err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
520b3f6008fSTakashi Iwai 	if (err < 0)
521b3f6008fSTakashi Iwai 		return err;
522b3f6008fSTakashi Iwai 
523b3f6008fSTakashi Iwai 	err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
524b3f6008fSTakashi Iwai 	if (err < 0)
525b3f6008fSTakashi Iwai 		return err;
526b3f6008fSTakashi Iwai 
5270e8f9862STakashi Iwai 	err = auto_parse_beep(codec);
5280e8f9862STakashi Iwai 	if (err < 0)
5290e8f9862STakashi Iwai 		return err;
5300e8f9862STakashi Iwai 
5310e8f9862STakashi Iwai 	if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &via_pin_power_ctl_enum))
5320e8f9862STakashi Iwai 		return -ENOMEM;
5330e8f9862STakashi Iwai 
534688b12ccSTakashi Iwai 	/* disable widget PM at start for compatibility */
535967b1307STakashi Iwai 	codec->power_save_node = 0;
536688b12ccSTakashi Iwai 	spec->gen.power_down_unused = 0;
537b3f6008fSTakashi Iwai 	return 0;
5384a918ffeSTakashi Iwai }
5394a918ffeSTakashi Iwai 
5405d41762aSTakashi Iwai static int via_init(struct hda_codec *codec)
5415d41762aSTakashi Iwai {
542e9d010c2STakashi Iwai 	/* init power states */
543e9d010c2STakashi Iwai 	__analog_low_current_mode(codec, true);
544e9d010c2STakashi Iwai 
545b3f6008fSTakashi Iwai 	snd_hda_gen_init(codec);
54611890956SLydia Wang 
547b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
54825eaba2fSLydia Wang 
549c577b8a1SJoseph Chan 	return 0;
550c577b8a1SJoseph Chan }
551c577b8a1SJoseph Chan 
552f672f65aSDavid Henningsson static int vt1708_build_controls(struct hda_codec *codec)
553f672f65aSDavid Henningsson {
554f672f65aSDavid Henningsson 	/* In order not to create "Phantom Jack" controls,
555f672f65aSDavid Henningsson 	   temporary enable jackpoll */
556f672f65aSDavid Henningsson 	int err;
557f672f65aSDavid Henningsson 	int old_interval = codec->jackpoll_interval;
558f672f65aSDavid Henningsson 	codec->jackpoll_interval = msecs_to_jiffies(100);
5590e8f9862STakashi Iwai 	err = snd_hda_gen_build_controls(codec);
560f672f65aSDavid Henningsson 	codec->jackpoll_interval = old_interval;
561f672f65aSDavid Henningsson 	return err;
562f672f65aSDavid Henningsson }
563f672f65aSDavid Henningsson 
564b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec)
565337b9d02STakashi Iwai {
566337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
567b3f6008fSTakashi Iwai 	int i, err;
568337b9d02STakashi Iwai 
569b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_pcms(codec);
5707639a06cSTakashi Iwai 	if (err < 0 || codec->core.vendor_id != 0x11061708)
571b3f6008fSTakashi Iwai 		return err;
572b3f6008fSTakashi Iwai 
573b3f6008fSTakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
574b3f6008fSTakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
575b3f6008fSTakashi Iwai 	 * disable the 24bit format, so far.
576b3f6008fSTakashi Iwai 	 */
577bbbc7e85STakashi Iwai 	for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) {
578bbbc7e85STakashi Iwai 		struct hda_pcm *info = spec->gen.pcm_rec[i];
579bbbc7e85STakashi Iwai 		if (!info)
580bbbc7e85STakashi Iwai 			continue;
581b3f6008fSTakashi Iwai 		if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
582b3f6008fSTakashi Iwai 		    info->pcm_type != HDA_PCM_TYPE_AUDIO)
583b3f6008fSTakashi Iwai 			continue;
584b3f6008fSTakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats =
585b3f6008fSTakashi Iwai 			SNDRV_PCM_FMTBIT_S16_LE;
586337b9d02STakashi Iwai 	}
587b3f6008fSTakashi Iwai 
5881c55d521STakashi Iwai 	return 0;
589337b9d02STakashi Iwai }
590337b9d02STakashi Iwai 
591c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
592c577b8a1SJoseph Chan {
593c577b8a1SJoseph Chan 	struct via_spec *spec;
594c577b8a1SJoseph Chan 	int err;
595c577b8a1SJoseph Chan 
596c577b8a1SJoseph Chan 	/* create a codec specific record */
5975b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
598c577b8a1SJoseph Chan 	if (spec == NULL)
599c577b8a1SJoseph Chan 		return -ENOMEM;
600c577b8a1SJoseph Chan 
601225068abSTakashi Iwai 	/* override some patch_ops */
602225068abSTakashi Iwai 	codec->patch_ops.build_controls = vt1708_build_controls;
603225068abSTakashi Iwai 	codec->patch_ops.build_pcms = vt1708_build_pcms;
604b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x17;
605b3f6008fSTakashi Iwai 
606b3f6008fSTakashi Iwai 	/* set jackpoll_interval while parsing the codec */
607b3f6008fSTakashi Iwai 	codec->jackpoll_interval = msecs_to_jiffies(100);
608b3f6008fSTakashi Iwai 	spec->vt1708_jack_detect = 1;
609b3f6008fSTakashi Iwai 
610b3f6008fSTakashi Iwai 	/* don't support the input jack switching due to lack of unsol event */
611b3f6008fSTakashi Iwai 	/* (it may work with polling, though, but it needs testing) */
612b3f6008fSTakashi Iwai 	spec->gen.suppress_auto_mic = 1;
613eb33ccf7STakashi Iwai 	/* Some machines show the broken speaker mute */
614eb33ccf7STakashi Iwai 	spec->gen.auto_mute_via_amp = 1;
615620e2b28STakashi Iwai 
61612daef65STakashi Iwai 	/* Add HP and CD pin config connect bit re-config action */
61712daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
61812daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
61912daef65STakashi Iwai 
620f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt1708_init_verbs);
621f8bfc628STakashi Iwai 	if (err < 0)
622f8bfc628STakashi Iwai 		goto error;
623f8bfc628STakashi Iwai 
624c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
62512daef65STakashi Iwai 	err = via_parse_auto_config(codec);
626fcbdcc1aSTakashi Iwai 	if (err < 0)
627fcbdcc1aSTakashi Iwai 		goto error;
628c577b8a1SJoseph Chan 
62912daef65STakashi Iwai 	/* add jack detect on/off control */
6300e8f9862STakashi Iwai 	if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1708_jack_detect_ctl)) {
6310e8f9862STakashi Iwai 		err = -ENOMEM;
6320e8f9862STakashi Iwai 		goto error;
6330e8f9862STakashi Iwai 	}
634c577b8a1SJoseph Chan 
635b3f6008fSTakashi Iwai 	/* clear jackpoll_interval again; it's set dynamically */
636b3f6008fSTakashi Iwai 	codec->jackpoll_interval = 0;
637b3f6008fSTakashi Iwai 
638c577b8a1SJoseph Chan 	return 0;
639fcbdcc1aSTakashi Iwai 
640fcbdcc1aSTakashi Iwai  error:
641fcbdcc1aSTakashi Iwai 	via_free(codec);
642fcbdcc1aSTakashi Iwai 	return err;
643c577b8a1SJoseph Chan }
644c577b8a1SJoseph Chan 
645ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec)
646c577b8a1SJoseph Chan {
647c577b8a1SJoseph Chan 	struct via_spec *spec;
648c577b8a1SJoseph Chan 	int err;
649c577b8a1SJoseph Chan 
650c577b8a1SJoseph Chan 	/* create a codec specific record */
6515b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
652c577b8a1SJoseph Chan 	if (spec == NULL)
653c577b8a1SJoseph Chan 		return -ENOMEM;
654c577b8a1SJoseph Chan 
655b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x18;
656620e2b28STakashi Iwai 
65712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
658fcbdcc1aSTakashi Iwai 	if (err < 0)
659fcbdcc1aSTakashi Iwai 		goto error;
660c577b8a1SJoseph Chan 
661f7278fd0SJosepch Chan 	return 0;
662fcbdcc1aSTakashi Iwai 
663fcbdcc1aSTakashi Iwai  error:
664fcbdcc1aSTakashi Iwai 	via_free(codec);
665fcbdcc1aSTakashi Iwai 	return err;
666f7278fd0SJosepch Chan }
667f7278fd0SJosepch Chan 
668518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
669ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec)
670f7278fd0SJosepch Chan {
671f7278fd0SJosepch Chan 	struct via_spec *spec;
672f7278fd0SJosepch Chan 	int err;
673f7278fd0SJosepch Chan 
674518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
675518bf3baSLydia Wang 		return patch_vt1708S(codec);
676ddd304d8STakashi Iwai 
677f7278fd0SJosepch Chan 	/* create a codec specific record */
6785b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
679f7278fd0SJosepch Chan 	if (spec == NULL)
680f7278fd0SJosepch Chan 		return -ENOMEM;
681f7278fd0SJosepch Chan 
682b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
683620e2b28STakashi Iwai 
684f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
68512daef65STakashi Iwai 	err = via_parse_auto_config(codec);
686fcbdcc1aSTakashi Iwai 	if (err < 0)
687fcbdcc1aSTakashi Iwai 		goto error;
688f7278fd0SJosepch Chan 
689f7278fd0SJosepch Chan 	return 0;
690fcbdcc1aSTakashi Iwai 
691fcbdcc1aSTakashi Iwai  error:
692fcbdcc1aSTakashi Iwai 	via_free(codec);
693fcbdcc1aSTakashi Iwai 	return err;
694f7278fd0SJosepch Chan }
695f7278fd0SJosepch Chan 
696d949cac1SHarald Welte /* Patch for VT1708S */
697096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = {
698d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
699d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
700bc7e7e5cSLydia Wang 	/* don't bybass mixer */
701bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
702d949cac1SHarald Welte 	{ }
703d949cac1SHarald Welte };
704d949cac1SHarald Welte 
7056369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
7066369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
7076369bcfcSLydia Wang {
708d045c5dcSTakashi Iwai 	snd_hda_override_wcaps(codec, pin,
709d045c5dcSTakashi Iwai 			       get_wcaps(codec, pin) | AC_WCAP_IN_AMP);
7106369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
7116369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
7126369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
7136369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
7146369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
7156369bcfcSLydia Wang }
7166369bcfcSLydia Wang 
717d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
718d949cac1SHarald Welte {
719d949cac1SHarald Welte 	struct via_spec *spec;
720d949cac1SHarald Welte 	int err;
721d949cac1SHarald Welte 
722d949cac1SHarald Welte 	/* create a codec specific record */
7235b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
724d949cac1SHarald Welte 	if (spec == NULL)
725d949cac1SHarald Welte 		return -ENOMEM;
726d949cac1SHarald Welte 
727b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
728d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
729d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
730620e2b28STakashi Iwai 
731518bf3baSLydia Wang 	/* correct names for VT1708BCE */
732ded255beSTakashi Iwai 	if (get_codec_type(codec) == VT1708BCE)
733ded255beSTakashi Iwai 		snd_hda_codec_set_name(codec, "VT1708BCE");
734bc92df7fSLydia Wang 	/* correct names for VT1705 */
735ded255beSTakashi Iwai 	if (codec->core.vendor_id == 0x11064397)
736ded255beSTakashi Iwai 		snd_hda_codec_set_name(codec, "VT1705");
737b3f6008fSTakashi Iwai 
738f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt1708S_init_verbs);
739f8bfc628STakashi Iwai 	if (err < 0)
740f8bfc628STakashi Iwai 		goto error;
741f8bfc628STakashi Iwai 
742b3f6008fSTakashi Iwai 	/* automatic parse from the BIOS config */
743b3f6008fSTakashi Iwai 	err = via_parse_auto_config(codec);
744fcbdcc1aSTakashi Iwai 	if (err < 0)
745fcbdcc1aSTakashi Iwai 		goto error;
746b3f6008fSTakashi Iwai 
747d949cac1SHarald Welte 	return 0;
748fcbdcc1aSTakashi Iwai 
749fcbdcc1aSTakashi Iwai  error:
750fcbdcc1aSTakashi Iwai 	via_free(codec);
751fcbdcc1aSTakashi Iwai 	return err;
752d949cac1SHarald Welte }
753d949cac1SHarald Welte 
754d949cac1SHarald Welte /* Patch for VT1702 */
755d949cac1SHarald Welte 
756096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = {
757bc7e7e5cSLydia Wang 	/* mixer enable */
758bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
759bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
760bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
761d949cac1SHarald Welte 	{ }
762d949cac1SHarald Welte };
763d949cac1SHarald Welte 
764d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
765d949cac1SHarald Welte {
766d949cac1SHarald Welte 	struct via_spec *spec;
767d949cac1SHarald Welte 	int err;
768d949cac1SHarald Welte 
769d949cac1SHarald Welte 	/* create a codec specific record */
7705b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
771d949cac1SHarald Welte 	if (spec == NULL)
772d949cac1SHarald Welte 		return -ENOMEM;
773d949cac1SHarald Welte 
774b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x1a;
775620e2b28STakashi Iwai 
77612daef65STakashi Iwai 	/* limit AA path volume to 0 dB */
77712daef65STakashi Iwai 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
77812daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
77912daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
78012daef65STakashi Iwai 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
78112daef65STakashi Iwai 				  (1 << AC_AMPCAP_MUTE_SHIFT));
78212daef65STakashi Iwai 
783f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt1702_init_verbs);
784f8bfc628STakashi Iwai 	if (err < 0)
785f8bfc628STakashi Iwai 		goto error;
786f8bfc628STakashi Iwai 
787d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
78812daef65STakashi Iwai 	err = via_parse_auto_config(codec);
789fcbdcc1aSTakashi Iwai 	if (err < 0)
790fcbdcc1aSTakashi Iwai 		goto error;
791d949cac1SHarald Welte 
792d949cac1SHarald Welte 	return 0;
793fcbdcc1aSTakashi Iwai 
794fcbdcc1aSTakashi Iwai  error:
795fcbdcc1aSTakashi Iwai 	via_free(codec);
796fcbdcc1aSTakashi Iwai 	return err;
797d949cac1SHarald Welte }
798d949cac1SHarald Welte 
799eb7188caSLydia Wang /* Patch for VT1718S */
800eb7188caSLydia Wang 
801096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = {
8024ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
8034ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
804eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
805eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
8065d41762aSTakashi Iwai 
807eb7188caSLydia Wang 	{ }
808eb7188caSLydia Wang };
809eb7188caSLydia Wang 
81030b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs
81130b45033STakashi Iwai  * This isn't listed from the raw info, but the chip has a secret connection.
81230b45033STakashi Iwai  */
81330b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec)
81430b45033STakashi Iwai {
81530b45033STakashi Iwai 	struct via_spec *spec = codec->spec;
81630b45033STakashi Iwai 	int i, nums;
81730b45033STakashi Iwai 	hda_nid_t conn[8];
81830b45033STakashi Iwai 	hda_nid_t nid;
81930b45033STakashi Iwai 
820b3f6008fSTakashi Iwai 	if (!spec->gen.mixer_nid)
82130b45033STakashi Iwai 		return 0;
822b3f6008fSTakashi Iwai 	nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
82330b45033STakashi Iwai 				       ARRAY_SIZE(conn) - 1);
82430b45033STakashi Iwai 	for (i = 0; i < nums; i++) {
82530b45033STakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
82630b45033STakashi Iwai 			return 0;
82730b45033STakashi Iwai 	}
82830b45033STakashi Iwai 
82930b45033STakashi Iwai 	/* find the primary DAC and add to the connection list */
8307639a06cSTakashi Iwai 	for_each_hda_codec_node(nid, codec) {
83130b45033STakashi Iwai 		unsigned int caps = get_wcaps(codec, nid);
83230b45033STakashi Iwai 		if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
83330b45033STakashi Iwai 		    !(caps & AC_WCAP_DIGITAL)) {
83430b45033STakashi Iwai 			conn[nums++] = nid;
83530b45033STakashi Iwai 			return snd_hda_override_conn_list(codec,
836b3f6008fSTakashi Iwai 							  spec->gen.mixer_nid,
83730b45033STakashi Iwai 							  nums, conn);
83830b45033STakashi Iwai 		}
83930b45033STakashi Iwai 	}
84030b45033STakashi Iwai 	return 0;
84130b45033STakashi Iwai }
84230b45033STakashi Iwai 
84330b45033STakashi Iwai 
844eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
845eb7188caSLydia Wang {
846eb7188caSLydia Wang 	struct via_spec *spec;
847eb7188caSLydia Wang 	int err;
848eb7188caSLydia Wang 
849eb7188caSLydia Wang 	/* create a codec specific record */
8505b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
851eb7188caSLydia Wang 	if (spec == NULL)
852eb7188caSLydia Wang 		return -ENOMEM;
853eb7188caSLydia Wang 
854b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
855d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
856d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
85730b45033STakashi Iwai 	add_secret_dac_path(codec);
858620e2b28STakashi Iwai 
859f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt1718S_init_verbs);
860f8bfc628STakashi Iwai 	if (err < 0)
861f8bfc628STakashi Iwai 		goto error;
862f8bfc628STakashi Iwai 
863eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
86412daef65STakashi Iwai 	err = via_parse_auto_config(codec);
865fcbdcc1aSTakashi Iwai 	if (err < 0)
866fcbdcc1aSTakashi Iwai 		goto error;
867eb7188caSLydia Wang 
868eb7188caSLydia Wang 	return 0;
869fcbdcc1aSTakashi Iwai 
870fcbdcc1aSTakashi Iwai  error:
871fcbdcc1aSTakashi Iwai 	via_free(codec);
872fcbdcc1aSTakashi Iwai 	return err;
873eb7188caSLydia Wang }
874f3db423dSLydia Wang 
875f3db423dSLydia Wang /* Patch for VT1716S */
876f3db423dSLydia Wang 
877f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
878f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
879f3db423dSLydia Wang {
880f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
881f3db423dSLydia Wang 	uinfo->count = 1;
882f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
883f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
884f3db423dSLydia Wang 	return 0;
885f3db423dSLydia Wang }
886f3db423dSLydia Wang 
887f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
888f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
889f3db423dSLydia Wang {
890f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
891f3db423dSLydia Wang 	int index = 0;
892f3db423dSLydia Wang 
893f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
894f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
895f3db423dSLydia Wang 	if (index != -1)
896f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
897f3db423dSLydia Wang 
898f3db423dSLydia Wang 	return 0;
899f3db423dSLydia Wang }
900f3db423dSLydia Wang 
901f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
902f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
903f3db423dSLydia Wang {
904f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
905f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
906f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
907f3db423dSLydia Wang 
908f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
909f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
910f3db423dSLydia Wang 	spec->dmic_enabled = index;
911f3db423dSLydia Wang 	return 1;
912f3db423dSLydia Wang }
913f3db423dSLydia Wang 
9140e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer_vol =
9150e8f9862STakashi Iwai 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT);
9160e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer_sw = {
917f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
918f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
9195b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
920f3db423dSLydia Wang 	 .count = 1,
921f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
922f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
923f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
924f3db423dSLydia Wang };
925f3db423dSLydia Wang 
926f3db423dSLydia Wang 
927f3db423dSLydia Wang /* mono-out mixer elements */
9280e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer =
9290e8f9862STakashi Iwai 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT);
930f3db423dSLydia Wang 
931096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = {
932f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
933f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
934f3db423dSLydia Wang 	/* don't bybass mixer */
935f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
936f3db423dSLydia Wang 	/* Enable mono output */
937f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
938f3db423dSLydia Wang 	{ }
939f3db423dSLydia Wang };
940f3db423dSLydia Wang 
941f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
942f3db423dSLydia Wang {
943f3db423dSLydia Wang 	struct via_spec *spec;
944f3db423dSLydia Wang 	int err;
945f3db423dSLydia Wang 
946f3db423dSLydia Wang 	/* create a codec specific record */
9475b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
948f3db423dSLydia Wang 	if (spec == NULL)
949f3db423dSLydia Wang 		return -ENOMEM;
950f3db423dSLydia Wang 
951b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
952d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
953d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
954620e2b28STakashi Iwai 
955f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt1716S_init_verbs);
956f8bfc628STakashi Iwai 	if (err < 0)
957f8bfc628STakashi Iwai 		goto error;
958f8bfc628STakashi Iwai 
959f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
96012daef65STakashi Iwai 	err = via_parse_auto_config(codec);
961fcbdcc1aSTakashi Iwai 	if (err < 0)
962fcbdcc1aSTakashi Iwai 		goto error;
963f3db423dSLydia Wang 
9640e8f9862STakashi Iwai 	if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_vol) ||
9650e8f9862STakashi Iwai 	    !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_sw) ||
9660e8f9862STakashi Iwai 	    !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716S_mono_out_mixer)) {
9670e8f9862STakashi Iwai 		err = -ENOMEM;
9680e8f9862STakashi Iwai 		goto error;
9690e8f9862STakashi Iwai 	}
970f3db423dSLydia Wang 
971f3db423dSLydia Wang 	return 0;
972fcbdcc1aSTakashi Iwai 
973fcbdcc1aSTakashi Iwai  error:
974fcbdcc1aSTakashi Iwai 	via_free(codec);
975fcbdcc1aSTakashi Iwai 	return err;
976f3db423dSLydia Wang }
97725eaba2fSLydia Wang 
97825eaba2fSLydia Wang /* for vt2002P */
97925eaba2fSLydia Wang 
980096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = {
981eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
982eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
983eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
984eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
98525eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
98625eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
98725eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
98825eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
98925eaba2fSLydia Wang 	{ }
99025eaba2fSLydia Wang };
9914a918ffeSTakashi Iwai 
992096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = {
99311890956SLydia Wang 	/* Enable Boost Volume backdoor */
99411890956SLydia Wang 	{0x1, 0xfb9, 0x24},
99511890956SLydia Wang 	/* Enable AOW0 to MW9 */
99611890956SLydia Wang 	{0x1, 0xfb8, 0x88},
99711890956SLydia Wang 	{ }
99811890956SLydia Wang };
99925eaba2fSLydia Wang 
10004b527b65SDavid Henningsson /*
10014b527b65SDavid Henningsson  * pin fix-up
10024b527b65SDavid Henningsson  */
10034b527b65SDavid Henningsson enum {
10044b527b65SDavid Henningsson 	VIA_FIXUP_INTMIC_BOOST,
1005d5266125STakashi Iwai 	VIA_FIXUP_ASUS_G75,
10064bfd6247STakashi Iwai 	VIA_FIXUP_POWER_SAVE,
10074b527b65SDavid Henningsson };
10084b527b65SDavid Henningsson 
10094b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec,
10104b527b65SDavid Henningsson 				  const struct hda_fixup *fix, int action)
10114b527b65SDavid Henningsson {
10124b527b65SDavid Henningsson 	if (action == HDA_FIXUP_ACT_PRE_PROBE)
10134b527b65SDavid Henningsson 		override_mic_boost(codec, 0x30, 0, 2, 40);
10144b527b65SDavid Henningsson }
10154b527b65SDavid Henningsson 
10164bfd6247STakashi Iwai static void via_fixup_power_save(struct hda_codec *codec,
10174bfd6247STakashi Iwai 				 const struct hda_fixup *fix, int action)
10184bfd6247STakashi Iwai {
10194bfd6247STakashi Iwai 	if (action == HDA_FIXUP_ACT_PRE_PROBE)
10204bfd6247STakashi Iwai 		codec->power_save_node = 0;
10214bfd6247STakashi Iwai }
10224bfd6247STakashi Iwai 
10234b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = {
10244b527b65SDavid Henningsson 	[VIA_FIXUP_INTMIC_BOOST] = {
10254b527b65SDavid Henningsson 		.type = HDA_FIXUP_FUNC,
10264b527b65SDavid Henningsson 		.v.func = via_fixup_intmic_boost,
10274b527b65SDavid Henningsson 	},
1028d5266125STakashi Iwai 	[VIA_FIXUP_ASUS_G75] = {
1029d5266125STakashi Iwai 		.type = HDA_FIXUP_PINS,
1030d5266125STakashi Iwai 		.v.pins = (const struct hda_pintbl[]) {
1031d5266125STakashi Iwai 			/* set 0x24 and 0x33 as speakers */
1032d5266125STakashi Iwai 			{ 0x24, 0x991301f0 },
1033d5266125STakashi Iwai 			{ 0x33, 0x991301f1 }, /* subwoofer */
1034d5266125STakashi Iwai 			{ }
1035d5266125STakashi Iwai 		}
1036d5266125STakashi Iwai 	},
10374bfd6247STakashi Iwai 	[VIA_FIXUP_POWER_SAVE] = {
10384bfd6247STakashi Iwai 		.type = HDA_FIXUP_FUNC,
10394bfd6247STakashi Iwai 		.v.func = via_fixup_power_save,
10404bfd6247STakashi Iwai 	},
10414b527b65SDavid Henningsson };
10424b527b65SDavid Henningsson 
10434b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = {
1044d5266125STakashi Iwai 	SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
10454b527b65SDavid Henningsson 	SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
1046*4961167bSTakashi Iwai 	SND_PCI_QUIRK_VENDOR(0x1558, "Clevo", VIA_FIXUP_POWER_SAVE),
10474b527b65SDavid Henningsson 	{}
10484b527b65SDavid Henningsson };
10494b527b65SDavid Henningsson 
1050ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
1051ef4da458STakashi Iwai  * Replace this with mixer NID 0x1c
1052ef4da458STakashi Iwai  */
1053ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec)
1054ef4da458STakashi Iwai {
1055caf3c043SMichał Mirosław 	static const hda_nid_t conn_24[] = { 0x14, 0x1c };
1056caf3c043SMichał Mirosław 	static const hda_nid_t conn_33[] = { 0x1c };
1057ef4da458STakashi Iwai 
1058ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
1059ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
1060ef4da458STakashi Iwai }
1061ef4da458STakashi Iwai 
106225eaba2fSLydia Wang /* patch for vt2002P */
106325eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
106425eaba2fSLydia Wang {
106525eaba2fSLydia Wang 	struct via_spec *spec;
106625eaba2fSLydia Wang 	int err;
106725eaba2fSLydia Wang 
106825eaba2fSLydia Wang 	/* create a codec specific record */
10695b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
107025eaba2fSLydia Wang 	if (spec == NULL)
107125eaba2fSLydia Wang 		return -ENOMEM;
107225eaba2fSLydia Wang 
1073b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1074d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1075d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
1076ef4da458STakashi Iwai 	if (spec->codec_type == VT1802)
1077ef4da458STakashi Iwai 		fix_vt1802_connections(codec);
107830b45033STakashi Iwai 	add_secret_dac_path(codec);
1079620e2b28STakashi Iwai 
10804b527b65SDavid Henningsson 	snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
10814b527b65SDavid Henningsson 	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
10824b527b65SDavid Henningsson 
1083f8bfc628STakashi Iwai 	if (spec->codec_type == VT1802)
1084f8bfc628STakashi Iwai 		err = snd_hda_add_verbs(codec, vt1802_init_verbs);
1085f8bfc628STakashi Iwai 	else
1086f8bfc628STakashi Iwai 		err = snd_hda_add_verbs(codec, vt2002P_init_verbs);
1087f8bfc628STakashi Iwai 	if (err < 0)
1088f8bfc628STakashi Iwai 		goto error;
1089f8bfc628STakashi Iwai 
109025eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
109112daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1092fcbdcc1aSTakashi Iwai 	if (err < 0)
1093fcbdcc1aSTakashi Iwai 		goto error;
109425eaba2fSLydia Wang 
109525eaba2fSLydia Wang 	return 0;
1096fcbdcc1aSTakashi Iwai 
1097fcbdcc1aSTakashi Iwai  error:
1098fcbdcc1aSTakashi Iwai 	via_free(codec);
1099fcbdcc1aSTakashi Iwai 	return err;
110025eaba2fSLydia Wang }
1101ab6734e7SLydia Wang 
1102ab6734e7SLydia Wang /* for vt1812 */
1103ab6734e7SLydia Wang 
1104096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = {
1105ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
1106ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
1107ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
1108ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
1109ab6734e7SLydia Wang 	{ }
1110ab6734e7SLydia Wang };
1111ab6734e7SLydia Wang 
1112ab6734e7SLydia Wang /* patch for vt1812 */
1113ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
1114ab6734e7SLydia Wang {
1115ab6734e7SLydia Wang 	struct via_spec *spec;
1116ab6734e7SLydia Wang 	int err;
1117ab6734e7SLydia Wang 
1118ab6734e7SLydia Wang 	/* create a codec specific record */
11195b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1120ab6734e7SLydia Wang 	if (spec == NULL)
1121ab6734e7SLydia Wang 		return -ENOMEM;
1122ab6734e7SLydia Wang 
1123b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1124d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1125d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
112630b45033STakashi Iwai 	add_secret_dac_path(codec);
1127620e2b28STakashi Iwai 
1128f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt1812_init_verbs);
1129f8bfc628STakashi Iwai 	if (err < 0)
1130f8bfc628STakashi Iwai 		goto error;
1131f8bfc628STakashi Iwai 
1132ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
113312daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1134fcbdcc1aSTakashi Iwai 	if (err < 0)
1135fcbdcc1aSTakashi Iwai 		goto error;
1136ab6734e7SLydia Wang 
1137ab6734e7SLydia Wang 	return 0;
1138fcbdcc1aSTakashi Iwai 
1139fcbdcc1aSTakashi Iwai  error:
1140fcbdcc1aSTakashi Iwai 	via_free(codec);
1141fcbdcc1aSTakashi Iwai 	return err;
1142ab6734e7SLydia Wang }
1143ab6734e7SLydia Wang 
114443737e0aSLydia Wang /* patch for vt3476 */
114543737e0aSLydia Wang 
114643737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = {
114743737e0aSLydia Wang 	/* Enable DMic 8/16/32K */
114843737e0aSLydia Wang 	{0x1, 0xF7B, 0x30},
114943737e0aSLydia Wang 	/* Enable Boost Volume backdoor */
115043737e0aSLydia Wang 	{0x1, 0xFB9, 0x20},
115143737e0aSLydia Wang 	/* Enable AOW-MW9 path */
115243737e0aSLydia Wang 	{0x1, 0xFB8, 0x10},
115343737e0aSLydia Wang 	{ }
115443737e0aSLydia Wang };
115543737e0aSLydia Wang 
115643737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec)
115743737e0aSLydia Wang {
115843737e0aSLydia Wang 	struct via_spec *spec;
115943737e0aSLydia Wang 	int err;
116043737e0aSLydia Wang 
116143737e0aSLydia Wang 	/* create a codec specific record */
116243737e0aSLydia Wang 	spec = via_new_spec(codec);
116343737e0aSLydia Wang 	if (spec == NULL)
116443737e0aSLydia Wang 		return -ENOMEM;
116543737e0aSLydia Wang 
1166b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x3f;
116743737e0aSLydia Wang 	add_secret_dac_path(codec);
116843737e0aSLydia Wang 
1169f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt3476_init_verbs);
1170f8bfc628STakashi Iwai 	if (err < 0)
1171f8bfc628STakashi Iwai 		goto error;
1172f8bfc628STakashi Iwai 
117343737e0aSLydia Wang 	/* automatic parse from the BIOS config */
117443737e0aSLydia Wang 	err = via_parse_auto_config(codec);
1175fcbdcc1aSTakashi Iwai 	if (err < 0)
1176fcbdcc1aSTakashi Iwai 		goto error;
117743737e0aSLydia Wang 
117843737e0aSLydia Wang 	return 0;
1179fcbdcc1aSTakashi Iwai 
1180fcbdcc1aSTakashi Iwai  error:
1181fcbdcc1aSTakashi Iwai 	via_free(codec);
1182fcbdcc1aSTakashi Iwai 	return err;
118343737e0aSLydia Wang }
118443737e0aSLydia Wang 
1185c577b8a1SJoseph Chan /*
1186c577b8a1SJoseph Chan  * patch entries
1187c577b8a1SJoseph Chan  */
1188b9a94a9cSTakashi Iwai static const struct hda_device_id snd_hda_id_via[] = {
1189b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708),
1190b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708),
1191b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708),
1192b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708),
1193b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709),
1194b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709),
1195b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709),
1196b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709),
1197b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709),
1198b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709),
1199b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709),
1200b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709),
1201b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B),
1202b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B),
1203b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B),
1204b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B),
1205b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B),
1206b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B),
1207b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B),
1208b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B),
1209b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S),
1210b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S),
1211b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S),
1212b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S),
1213b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S),
1214b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S),
1215b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S),
1216b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S),
1217b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702),
1218b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702),
1219b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702),
1220b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702),
1221b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702),
1222b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702),
1223b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702),
1224b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702),
1225b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S),
1226b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S),
1227b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S),
1228b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S),
1229b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S),
1230b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S),
1231b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P),
1232b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P),
1233b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812),
1234b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S),
1235b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P),
1236b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P),
1237b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476),
1238b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476),
1239b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476),
1240c577b8a1SJoseph Chan 	{} /* terminator */
1241c577b8a1SJoseph Chan };
1242b9a94a9cSTakashi Iwai MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via);
12431289e9e8STakashi Iwai 
1244d8a766a1STakashi Iwai static struct hda_codec_driver via_driver = {
1245b9a94a9cSTakashi Iwai 	.id = snd_hda_id_via,
12461289e9e8STakashi Iwai };
12471289e9e8STakashi Iwai 
12481289e9e8STakashi Iwai MODULE_LICENSE("GPL");
12491289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
12501289e9e8STakashi Iwai 
1251d8a766a1STakashi Iwai module_hda_codec_driver(via_driver);
1252