xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision caf3c0437aaf2e63624c4aaf94c0dd38d1f897e3)
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;
116b3f6008fSTakashi Iwai 	spec->gen.pcm_playback_hook = via_playback_pcm_hook;
11774f14b36STakashi Iwai 	spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
118967b1307STakashi Iwai 	codec->power_save_node = 1;
119688b12ccSTakashi Iwai 	spec->gen.power_down_unused = 1;
120225068abSTakashi Iwai 	codec->patch_ops = via_patch_ops;
1215b0cb1d8SJaroslav Kysela 	return spec;
1225b0cb1d8SJaroslav Kysela }
1235b0cb1d8SJaroslav Kysela 
124744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
125d7426329SHarald Welte {
1267639a06cSTakashi Iwai 	u32 vendor_id = codec->core.vendor_id;
127d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
128d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
129d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
130d7426329SHarald Welte 
131d7426329SHarald Welte 	/* get codec type */
132d7426329SHarald Welte 	if (ven_id != 0x1106)
133d7426329SHarald Welte 		codec_type = UNKNOWN;
134d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
135d7426329SHarald Welte 		codec_type = VT1708;
136d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
137d7426329SHarald Welte 		codec_type = VT1709_10CH;
138d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
139d7426329SHarald Welte 		codec_type = VT1709_6CH;
140518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
141d7426329SHarald Welte 		codec_type = VT1708B_8CH;
142518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
143518bf3baSLydia Wang 			codec_type = VT1708BCE;
144518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
145d7426329SHarald Welte 		codec_type = VT1708B_4CH;
146d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
147d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
148d7426329SHarald Welte 		codec_type = VT1708S;
149d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
150d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
151d7426329SHarald Welte 		codec_type = VT1702;
152eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
153eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
154eb7188caSLydia Wang 		codec_type = VT1718S;
155f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
156f3db423dSLydia Wang 		codec_type = VT1716S;
157bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
158bb3c6bfcSLydia Wang 		codec_type = VT1718S;
15925eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
16025eaba2fSLydia Wang 		codec_type = VT2002P;
161ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
162ab6734e7SLydia Wang 		codec_type = VT1812;
16336dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
16436dd5c4aSLydia Wang 		codec_type = VT1708S;
16511890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
16611890956SLydia Wang 		codec_type = VT1802;
16743737e0aSLydia Wang 	else if (dev_id == 0x4760)
16843737e0aSLydia Wang 		codec_type = VT1705CF;
1696121b84aSLydia Wang 	else if (dev_id == 0x4761 || dev_id == 0x4762)
1706121b84aSLydia Wang 		codec_type = VT1808;
171d7426329SHarald Welte 	else
172d7426329SHarald Welte 		codec_type = UNKNOWN;
173d7426329SHarald Welte 	return codec_type;
174d7426329SHarald Welte };
175d7426329SHarald Welte 
176ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec);
177ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec);
1781f2e99feSLydia Wang 
179187d333eSTakashi Iwai #define hp_detect_with_aa(codec) \
180187d333eSTakashi Iwai 	(snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
181187d333eSTakashi Iwai 	 !is_aa_path_mute(codec))
1821f2e99feSLydia Wang 
183b3f6008fSTakashi Iwai static void vt1708_stop_hp_work(struct hda_codec *codec)
1841f2e99feSLydia Wang {
185b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
186b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
1871f2e99feSLydia Wang 		return;
188187d333eSTakashi Iwai 	if (spec->hp_work_active) {
189b3f6008fSTakashi Iwai 		snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
1907eaa9161SWang Xingchao 		codec->jackpoll_interval = 0;
191b3f6008fSTakashi Iwai 		cancel_delayed_work_sync(&codec->jackpoll_work);
192b3f6008fSTakashi Iwai 		spec->hp_work_active = false;
193187d333eSTakashi Iwai 	}
194187d333eSTakashi Iwai }
195187d333eSTakashi Iwai 
196b3f6008fSTakashi Iwai static void vt1708_update_hp_work(struct hda_codec *codec)
197187d333eSTakashi Iwai {
198b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
199b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
200187d333eSTakashi Iwai 		return;
20105dc0fc9SDavid Henningsson 	if (spec->vt1708_jack_detect) {
202187d333eSTakashi Iwai 		if (!spec->hp_work_active) {
203b3f6008fSTakashi Iwai 			codec->jackpoll_interval = msecs_to_jiffies(100);
204b3f6008fSTakashi Iwai 			snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0);
2052f35c630STakashi Iwai 			schedule_delayed_work(&codec->jackpoll_work, 0);
206b3f6008fSTakashi Iwai 			spec->hp_work_active = true;
207187d333eSTakashi Iwai 		}
208b3f6008fSTakashi Iwai 	} else if (!hp_detect_with_aa(codec))
209b3f6008fSTakashi Iwai 		vt1708_stop_hp_work(codec);
2101f2e99feSLydia Wang }
211f5271101SLydia Wang 
21224088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
21324088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
21424088a58STakashi Iwai {
215dda415d4STakashi Iwai 	return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
21624088a58STakashi Iwai }
21724088a58STakashi Iwai 
21824088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
21924088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
22024088a58STakashi Iwai {
22124088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
222735c75cfSTakashi Iwai 	struct via_spec *spec = codec->spec;
223735c75cfSTakashi Iwai 
224735c75cfSTakashi Iwai 	ucontrol->value.enumerated.item[0] = spec->gen.power_down_unused;
22524088a58STakashi Iwai 	return 0;
22624088a58STakashi Iwai }
22724088a58STakashi Iwai 
22824088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
22924088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
23024088a58STakashi Iwai {
23124088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
23224088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
233688b12ccSTakashi Iwai 	bool val = !!ucontrol->value.enumerated.item[0];
23424088a58STakashi Iwai 
235735c75cfSTakashi Iwai 	if (val == spec->gen.power_down_unused)
23624088a58STakashi Iwai 		return 0;
237735c75cfSTakashi Iwai 	/* codec->power_save_node = val; */ /* widget PM seems yet broken */
238688b12ccSTakashi Iwai 	spec->gen.power_down_unused = val;
239e9d010c2STakashi Iwai 	analog_low_current_mode(codec);
24024088a58STakashi Iwai 	return 1;
24124088a58STakashi Iwai }
24224088a58STakashi Iwai 
2430e8f9862STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
24424088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
24524088a58STakashi Iwai 	.name = "Dynamic Power-Control",
24624088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
24724088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
24824088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
24924088a58STakashi Iwai };
25024088a58STakashi Iwai 
2514738465cSW. Trevor King #ifdef CONFIG_SND_HDA_INPUT_BEEP
2524738465cSW. Trevor King /* additional beep mixers; the actual parameters are overwritten at build */
2530e8f9862STakashi Iwai static const struct snd_kcontrol_new via_beep_mixer[] = {
2544738465cSW. Trevor King 	HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
2554738465cSW. Trevor King 	HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
2564738465cSW. Trevor King };
2574738465cSW. Trevor King 
2580e8f9862STakashi Iwai static int set_beep_amp(struct via_spec *spec, hda_nid_t nid,
2590e8f9862STakashi Iwai 			int idx, int dir)
2604738465cSW. Trevor King {
2610e8f9862STakashi Iwai 	struct snd_kcontrol_new *knew;
2620e8f9862STakashi Iwai 	unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
2630e8f9862STakashi Iwai 	int i;
2644738465cSW. Trevor King 
2650e8f9862STakashi Iwai 	spec->gen.beep_nid = nid;
2660e8f9862STakashi Iwai 	for (i = 0; i < ARRAY_SIZE(via_beep_mixer); i++) {
2670e8f9862STakashi Iwai 		knew = snd_hda_gen_add_kctl(&spec->gen, NULL,
2680e8f9862STakashi Iwai 					    &via_beep_mixer[i]);
2690e8f9862STakashi Iwai 		if (!knew)
2704738465cSW. Trevor King 			return -ENOMEM;
2710e8f9862STakashi Iwai 		knew->private_value = beep_amp;
2724738465cSW. Trevor King 	}
2734738465cSW. Trevor King 	return 0;
2744738465cSW. Trevor King }
2754738465cSW. Trevor King 
2760e8f9862STakashi Iwai static int auto_parse_beep(struct hda_codec *codec)
2774738465cSW. Trevor King {
2784738465cSW. Trevor King 	struct via_spec *spec = codec->spec;
2794738465cSW. Trevor King 	hda_nid_t nid;
2804738465cSW. Trevor King 
2814738465cSW. Trevor King 	for_each_hda_codec_node(nid, codec)
2820e8f9862STakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP)
2830e8f9862STakashi Iwai 			return set_beep_amp(spec, nid, 0, HDA_OUTPUT);
2840e8f9862STakashi Iwai 	return 0;
2854738465cSW. Trevor King }
2864738465cSW. Trevor King #else
2870e8f9862STakashi Iwai #define auto_parse_beep(codec)	0
2884738465cSW. Trevor King #endif
28924088a58STakashi Iwai 
290f5271101SLydia Wang /* check AA path's mute status */
291ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec)
292ada509ecSTakashi Iwai {
293ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
294ada509ecSTakashi Iwai 	const struct hda_amp_list *p;
2950186f4f4STakashi Iwai 	int ch, v;
296ada509ecSTakashi Iwai 
2970186f4f4STakashi Iwai 	p = spec->gen.loopback.amplist;
2980186f4f4STakashi Iwai 	if (!p)
2990186f4f4STakashi Iwai 		return true;
3000186f4f4STakashi Iwai 	for (; p->nid; p++) {
301ada509ecSTakashi Iwai 		for (ch = 0; ch < 2; ch++) {
302ada509ecSTakashi Iwai 			v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
303ada509ecSTakashi Iwai 						   p->idx);
304ada509ecSTakashi Iwai 			if (!(v & HDA_AMP_MUTE) && v > 0)
305ada509ecSTakashi Iwai 				return false;
306f5271101SLydia Wang 		}
307f5271101SLydia Wang 	}
308ada509ecSTakashi Iwai 	return true;
309f5271101SLydia Wang }
310f5271101SLydia Wang 
311f5271101SLydia Wang /* enter/exit analog low-current mode */
312e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force)
313f5271101SLydia Wang {
314f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
315ada509ecSTakashi Iwai 	bool enable;
316ada509ecSTakashi Iwai 	unsigned int verb, parm;
317f5271101SLydia Wang 
318967b1307STakashi Iwai 	if (!codec->power_save_node)
319e9d010c2STakashi Iwai 		enable = false;
320e9d010c2STakashi Iwai 	else
321b3f6008fSTakashi Iwai 		enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
322e9d010c2STakashi Iwai 	if (enable == spec->alc_mode && !force)
323e9d010c2STakashi Iwai 		return;
324e9d010c2STakashi Iwai 	spec->alc_mode = enable;
325f5271101SLydia Wang 
326f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
327f5271101SLydia Wang 	switch (spec->codec_type) {
328f5271101SLydia Wang 	case VT1708B_8CH:
329f5271101SLydia Wang 	case VT1708B_4CH:
330f5271101SLydia Wang 		verb = 0xf70;
331f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
332f5271101SLydia Wang 		break;
333f5271101SLydia Wang 	case VT1708S:
334eb7188caSLydia Wang 	case VT1718S:
335f3db423dSLydia Wang 	case VT1716S:
336f5271101SLydia Wang 		verb = 0xf73;
337f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
338f5271101SLydia Wang 		break;
339f5271101SLydia Wang 	case VT1702:
340f5271101SLydia Wang 		verb = 0xf73;
341f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
342f5271101SLydia Wang 		break;
34325eaba2fSLydia Wang 	case VT2002P:
344ab6734e7SLydia Wang 	case VT1812:
34511890956SLydia Wang 	case VT1802:
34625eaba2fSLydia Wang 		verb = 0xf93;
34725eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
34825eaba2fSLydia Wang 		break;
34943737e0aSLydia Wang 	case VT1705CF:
3506121b84aSLydia Wang 	case VT1808:
35143737e0aSLydia Wang 		verb = 0xf82;
35243737e0aSLydia Wang 		parm = enable ? 0x00 : 0xe0;  /* 0x00: 4/40x, 0xe0: 1x */
35343737e0aSLydia Wang 		break;
354f5271101SLydia Wang 	default:
355f5271101SLydia Wang 		return;		/* other codecs are not supported */
356f5271101SLydia Wang 	}
357f5271101SLydia Wang 	/* send verb */
3587639a06cSTakashi Iwai 	snd_hda_codec_write(codec, codec->core.afg, 0, verb, parm);
359f5271101SLydia Wang }
360f5271101SLydia Wang 
361e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec)
362e9d010c2STakashi Iwai {
363e9d010c2STakashi Iwai 	return __analog_low_current_mode(codec, false);
364e9d010c2STakashi Iwai }
365e9d010c2STakashi Iwai 
366b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
367b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
368b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
369b3f6008fSTakashi Iwai 				  int action)
370c577b8a1SJoseph Chan {
371b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
372b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
373c577b8a1SJoseph Chan }
374c577b8a1SJoseph Chan 
375c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
376c577b8a1SJoseph Chan {
377b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
378a8dca460STakashi Iwai 	snd_hda_gen_free(codec);
379c577b8a1SJoseph Chan }
380c577b8a1SJoseph Chan 
3812a43952aSTakashi Iwai #ifdef CONFIG_PM
38268cb2b55STakashi Iwai static int via_suspend(struct hda_codec *codec)
3831f2e99feSLydia Wang {
3841f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
385b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
38694c142a1SDavid Henningsson 
38794c142a1SDavid Henningsson 	/* Fix pop noise on headphones */
3882c38d990STakashi Iwai 	if (spec->codec_type == VT1802)
3892c38d990STakashi Iwai 		snd_hda_shutup_pins(codec);
39094c142a1SDavid Henningsson 
3911f2e99feSLydia Wang 	return 0;
3921f2e99feSLydia Wang }
3936b6d0007STakashi Iwai 
3946b6d0007STakashi Iwai static int via_resume(struct hda_codec *codec)
3956b6d0007STakashi Iwai {
3966b6d0007STakashi Iwai 	/* some delay here to make jack detection working (bko#98921) */
3976b6d0007STakashi Iwai 	msleep(10);
3986b6d0007STakashi Iwai 	codec->patch_ops.init(codec);
3996b6d0007STakashi Iwai 	regcache_sync(codec->core.regmap);
4006b6d0007STakashi Iwai 	return 0;
4016b6d0007STakashi Iwai }
4021f2e99feSLydia Wang #endif
4031f2e99feSLydia Wang 
40483012a7cSTakashi Iwai #ifdef CONFIG_PM
405cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
406cb53c626STakashi Iwai {
407cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
408b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
409b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
410b3f6008fSTakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
411cb53c626STakashi Iwai }
412cb53c626STakashi Iwai #endif
413cb53c626STakashi Iwai 
414c577b8a1SJoseph Chan /*
415c577b8a1SJoseph Chan  */
4165d41762aSTakashi Iwai 
4175d41762aSTakashi Iwai static int via_init(struct hda_codec *codec);
4185d41762aSTakashi Iwai 
41990dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
4200e8f9862STakashi Iwai 	.build_controls = snd_hda_gen_build_controls,
421b3f6008fSTakashi Iwai 	.build_pcms = snd_hda_gen_build_pcms,
422c577b8a1SJoseph Chan 	.init = via_init,
423c577b8a1SJoseph Chan 	.free = via_free,
4244e2d16d3SDavid Henningsson 	.unsol_event = snd_hda_jack_unsol_event,
4252a43952aSTakashi Iwai #ifdef CONFIG_PM
4261f2e99feSLydia Wang 	.suspend = via_suspend,
4276b6d0007STakashi Iwai 	.resume = via_resume,
428cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
429cb53c626STakashi Iwai #endif
430c577b8a1SJoseph Chan };
431c577b8a1SJoseph Chan 
4324a79616dSTakashi Iwai 
433b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = {
434b3f6008fSTakashi Iwai 	/* power down jack detect function */
435b3f6008fSTakashi Iwai 	{0x1, 0xf81, 0x1},
436b3f6008fSTakashi Iwai 	{ }
4374a79616dSTakashi Iwai };
43876d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
43976d9b0ddSHarald Welte {
44076d9b0ddSHarald Welte 	unsigned int def_conf;
44176d9b0ddSHarald Welte 	unsigned char seqassoc;
44276d9b0ddSHarald Welte 
4432f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
44476d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
44576d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
44682ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
44782ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
44876d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
4492f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
45076d9b0ddSHarald Welte 	}
45176d9b0ddSHarald Welte 
45276d9b0ddSHarald Welte 	return;
45376d9b0ddSHarald Welte }
45476d9b0ddSHarald Welte 
455e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
4561f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
4571f2e99feSLydia Wang {
4581f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4591f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
4601f2e99feSLydia Wang 
4611f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
4621f2e99feSLydia Wang 		return 0;
463e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
4641f2e99feSLydia Wang 	return 0;
4651f2e99feSLydia Wang }
4661f2e99feSLydia Wang 
467e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
4681f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
4691f2e99feSLydia Wang {
4701f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4711f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
472187d333eSTakashi Iwai 	int val;
4731f2e99feSLydia Wang 
4741f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
4751f2e99feSLydia Wang 		return 0;
476187d333eSTakashi Iwai 	val = !!ucontrol->value.integer.value[0];
477187d333eSTakashi Iwai 	if (spec->vt1708_jack_detect == val)
478187d333eSTakashi Iwai 		return 0;
479187d333eSTakashi Iwai 	spec->vt1708_jack_detect = val;
480b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
481187d333eSTakashi Iwai 	return 1;
4821f2e99feSLydia Wang }
4831f2e99feSLydia Wang 
4840e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
4851f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4861f2e99feSLydia Wang 	.name = "Jack Detect",
4871f2e99feSLydia Wang 	.count = 1,
4881f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
489e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
490e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
4911f2e99feSLydia Wang };
4921f2e99feSLydia Wang 
4934abdbd1cSTakashi Iwai static const struct badness_table via_main_out_badness = {
4944abdbd1cSTakashi Iwai 	.no_primary_dac = 0x10000,
4954abdbd1cSTakashi Iwai 	.no_dac = 0x4000,
4964abdbd1cSTakashi Iwai 	.shared_primary = 0x10000,
4974abdbd1cSTakashi Iwai 	.shared_surr = 0x20,
4984abdbd1cSTakashi Iwai 	.shared_clfe = 0x20,
4994abdbd1cSTakashi Iwai 	.shared_surr_main = 0x20,
5004abdbd1cSTakashi Iwai };
5014abdbd1cSTakashi Iwai static const struct badness_table via_extra_out_badness = {
5024abdbd1cSTakashi Iwai 	.no_primary_dac = 0x4000,
5034abdbd1cSTakashi Iwai 	.no_dac = 0x4000,
5044abdbd1cSTakashi Iwai 	.shared_primary = 0x12,
5054abdbd1cSTakashi Iwai 	.shared_surr = 0x20,
5064abdbd1cSTakashi Iwai 	.shared_clfe = 0x20,
5074abdbd1cSTakashi Iwai 	.shared_surr_main = 0x10,
5084abdbd1cSTakashi Iwai };
5094abdbd1cSTakashi Iwai 
510b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec)
511b3f6008fSTakashi Iwai {
512b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
513b3f6008fSTakashi Iwai 	int err;
514b3f6008fSTakashi Iwai 
5154abdbd1cSTakashi Iwai 	spec->gen.main_out_badness = &via_main_out_badness;
5164abdbd1cSTakashi Iwai 	spec->gen.extra_out_badness = &via_extra_out_badness;
5174abdbd1cSTakashi Iwai 
518b3f6008fSTakashi Iwai 	err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
519b3f6008fSTakashi Iwai 	if (err < 0)
520b3f6008fSTakashi Iwai 		return err;
521b3f6008fSTakashi Iwai 
522b3f6008fSTakashi Iwai 	err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
523b3f6008fSTakashi Iwai 	if (err < 0)
524b3f6008fSTakashi Iwai 		return err;
525b3f6008fSTakashi Iwai 
5260e8f9862STakashi Iwai 	err = auto_parse_beep(codec);
5270e8f9862STakashi Iwai 	if (err < 0)
5280e8f9862STakashi Iwai 		return err;
5290e8f9862STakashi Iwai 
5300e8f9862STakashi Iwai 	if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &via_pin_power_ctl_enum))
5310e8f9862STakashi Iwai 		return -ENOMEM;
5320e8f9862STakashi Iwai 
533688b12ccSTakashi Iwai 	/* disable widget PM at start for compatibility */
534967b1307STakashi Iwai 	codec->power_save_node = 0;
535688b12ccSTakashi Iwai 	spec->gen.power_down_unused = 0;
536b3f6008fSTakashi Iwai 	return 0;
5374a918ffeSTakashi Iwai }
5384a918ffeSTakashi Iwai 
5395d41762aSTakashi Iwai static int via_init(struct hda_codec *codec)
5405d41762aSTakashi Iwai {
541e9d010c2STakashi Iwai 	/* init power states */
542e9d010c2STakashi Iwai 	__analog_low_current_mode(codec, true);
543e9d010c2STakashi Iwai 
544b3f6008fSTakashi Iwai 	snd_hda_gen_init(codec);
54511890956SLydia Wang 
546b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
54725eaba2fSLydia Wang 
548c577b8a1SJoseph Chan 	return 0;
549c577b8a1SJoseph Chan }
550c577b8a1SJoseph Chan 
551f672f65aSDavid Henningsson static int vt1708_build_controls(struct hda_codec *codec)
552f672f65aSDavid Henningsson {
553f672f65aSDavid Henningsson 	/* In order not to create "Phantom Jack" controls,
554f672f65aSDavid Henningsson 	   temporary enable jackpoll */
555f672f65aSDavid Henningsson 	int err;
556f672f65aSDavid Henningsson 	int old_interval = codec->jackpoll_interval;
557f672f65aSDavid Henningsson 	codec->jackpoll_interval = msecs_to_jiffies(100);
5580e8f9862STakashi Iwai 	err = snd_hda_gen_build_controls(codec);
559f672f65aSDavid Henningsson 	codec->jackpoll_interval = old_interval;
560f672f65aSDavid Henningsson 	return err;
561f672f65aSDavid Henningsson }
562f672f65aSDavid Henningsson 
563b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec)
564337b9d02STakashi Iwai {
565337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
566b3f6008fSTakashi Iwai 	int i, err;
567337b9d02STakashi Iwai 
568b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_pcms(codec);
5697639a06cSTakashi Iwai 	if (err < 0 || codec->core.vendor_id != 0x11061708)
570b3f6008fSTakashi Iwai 		return err;
571b3f6008fSTakashi Iwai 
572b3f6008fSTakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
573b3f6008fSTakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
574b3f6008fSTakashi Iwai 	 * disable the 24bit format, so far.
575b3f6008fSTakashi Iwai 	 */
576bbbc7e85STakashi Iwai 	for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) {
577bbbc7e85STakashi Iwai 		struct hda_pcm *info = spec->gen.pcm_rec[i];
578bbbc7e85STakashi Iwai 		if (!info)
579bbbc7e85STakashi Iwai 			continue;
580b3f6008fSTakashi Iwai 		if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
581b3f6008fSTakashi Iwai 		    info->pcm_type != HDA_PCM_TYPE_AUDIO)
582b3f6008fSTakashi Iwai 			continue;
583b3f6008fSTakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats =
584b3f6008fSTakashi Iwai 			SNDRV_PCM_FMTBIT_S16_LE;
585337b9d02STakashi Iwai 	}
586b3f6008fSTakashi Iwai 
5871c55d521STakashi Iwai 	return 0;
588337b9d02STakashi Iwai }
589337b9d02STakashi Iwai 
590c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
591c577b8a1SJoseph Chan {
592c577b8a1SJoseph Chan 	struct via_spec *spec;
593c577b8a1SJoseph Chan 	int err;
594c577b8a1SJoseph Chan 
595c577b8a1SJoseph Chan 	/* create a codec specific record */
5965b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
597c577b8a1SJoseph Chan 	if (spec == NULL)
598c577b8a1SJoseph Chan 		return -ENOMEM;
599c577b8a1SJoseph Chan 
600225068abSTakashi Iwai 	/* override some patch_ops */
601225068abSTakashi Iwai 	codec->patch_ops.build_controls = vt1708_build_controls;
602225068abSTakashi Iwai 	codec->patch_ops.build_pcms = vt1708_build_pcms;
603b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x17;
604b3f6008fSTakashi Iwai 
605b3f6008fSTakashi Iwai 	/* set jackpoll_interval while parsing the codec */
606b3f6008fSTakashi Iwai 	codec->jackpoll_interval = msecs_to_jiffies(100);
607b3f6008fSTakashi Iwai 	spec->vt1708_jack_detect = 1;
608b3f6008fSTakashi Iwai 
609b3f6008fSTakashi Iwai 	/* don't support the input jack switching due to lack of unsol event */
610b3f6008fSTakashi Iwai 	/* (it may work with polling, though, but it needs testing) */
611b3f6008fSTakashi Iwai 	spec->gen.suppress_auto_mic = 1;
612eb33ccf7STakashi Iwai 	/* Some machines show the broken speaker mute */
613eb33ccf7STakashi Iwai 	spec->gen.auto_mute_via_amp = 1;
614620e2b28STakashi Iwai 
61512daef65STakashi Iwai 	/* Add HP and CD pin config connect bit re-config action */
61612daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
61712daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
61812daef65STakashi Iwai 
619f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt1708_init_verbs);
620f8bfc628STakashi Iwai 	if (err < 0)
621f8bfc628STakashi Iwai 		goto error;
622f8bfc628STakashi Iwai 
623c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
62412daef65STakashi Iwai 	err = via_parse_auto_config(codec);
625fcbdcc1aSTakashi Iwai 	if (err < 0)
626fcbdcc1aSTakashi Iwai 		goto error;
627c577b8a1SJoseph Chan 
62812daef65STakashi Iwai 	/* add jack detect on/off control */
6290e8f9862STakashi Iwai 	if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1708_jack_detect_ctl)) {
6300e8f9862STakashi Iwai 		err = -ENOMEM;
6310e8f9862STakashi Iwai 		goto error;
6320e8f9862STakashi Iwai 	}
633c577b8a1SJoseph Chan 
634b3f6008fSTakashi Iwai 	/* clear jackpoll_interval again; it's set dynamically */
635b3f6008fSTakashi Iwai 	codec->jackpoll_interval = 0;
636b3f6008fSTakashi Iwai 
637c577b8a1SJoseph Chan 	return 0;
638fcbdcc1aSTakashi Iwai 
639fcbdcc1aSTakashi Iwai  error:
640fcbdcc1aSTakashi Iwai 	via_free(codec);
641fcbdcc1aSTakashi Iwai 	return err;
642c577b8a1SJoseph Chan }
643c577b8a1SJoseph Chan 
644ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec)
645c577b8a1SJoseph Chan {
646c577b8a1SJoseph Chan 	struct via_spec *spec;
647c577b8a1SJoseph Chan 	int err;
648c577b8a1SJoseph Chan 
649c577b8a1SJoseph Chan 	/* create a codec specific record */
6505b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
651c577b8a1SJoseph Chan 	if (spec == NULL)
652c577b8a1SJoseph Chan 		return -ENOMEM;
653c577b8a1SJoseph Chan 
654b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x18;
655620e2b28STakashi Iwai 
65612daef65STakashi Iwai 	err = via_parse_auto_config(codec);
657fcbdcc1aSTakashi Iwai 	if (err < 0)
658fcbdcc1aSTakashi Iwai 		goto error;
659c577b8a1SJoseph Chan 
660f7278fd0SJosepch Chan 	return 0;
661fcbdcc1aSTakashi Iwai 
662fcbdcc1aSTakashi Iwai  error:
663fcbdcc1aSTakashi Iwai 	via_free(codec);
664fcbdcc1aSTakashi Iwai 	return err;
665f7278fd0SJosepch Chan }
666f7278fd0SJosepch Chan 
667518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
668ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec)
669f7278fd0SJosepch Chan {
670f7278fd0SJosepch Chan 	struct via_spec *spec;
671f7278fd0SJosepch Chan 	int err;
672f7278fd0SJosepch Chan 
673518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
674518bf3baSLydia Wang 		return patch_vt1708S(codec);
675ddd304d8STakashi Iwai 
676f7278fd0SJosepch Chan 	/* create a codec specific record */
6775b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
678f7278fd0SJosepch Chan 	if (spec == NULL)
679f7278fd0SJosepch Chan 		return -ENOMEM;
680f7278fd0SJosepch Chan 
681b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
682620e2b28STakashi Iwai 
683f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
68412daef65STakashi Iwai 	err = via_parse_auto_config(codec);
685fcbdcc1aSTakashi Iwai 	if (err < 0)
686fcbdcc1aSTakashi Iwai 		goto error;
687f7278fd0SJosepch Chan 
688f7278fd0SJosepch Chan 	return 0;
689fcbdcc1aSTakashi Iwai 
690fcbdcc1aSTakashi Iwai  error:
691fcbdcc1aSTakashi Iwai 	via_free(codec);
692fcbdcc1aSTakashi Iwai 	return err;
693f7278fd0SJosepch Chan }
694f7278fd0SJosepch Chan 
695d949cac1SHarald Welte /* Patch for VT1708S */
696096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = {
697d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
698d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
699bc7e7e5cSLydia Wang 	/* don't bybass mixer */
700bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
701d949cac1SHarald Welte 	{ }
702d949cac1SHarald Welte };
703d949cac1SHarald Welte 
7046369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
7056369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
7066369bcfcSLydia Wang {
707d045c5dcSTakashi Iwai 	snd_hda_override_wcaps(codec, pin,
708d045c5dcSTakashi Iwai 			       get_wcaps(codec, pin) | AC_WCAP_IN_AMP);
7096369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
7106369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
7116369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
7126369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
7136369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
7146369bcfcSLydia Wang }
7156369bcfcSLydia Wang 
716d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
717d949cac1SHarald Welte {
718d949cac1SHarald Welte 	struct via_spec *spec;
719d949cac1SHarald Welte 	int err;
720d949cac1SHarald Welte 
721d949cac1SHarald Welte 	/* create a codec specific record */
7225b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
723d949cac1SHarald Welte 	if (spec == NULL)
724d949cac1SHarald Welte 		return -ENOMEM;
725d949cac1SHarald Welte 
726b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
727d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
728d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
729620e2b28STakashi Iwai 
730518bf3baSLydia Wang 	/* correct names for VT1708BCE */
731ded255beSTakashi Iwai 	if (get_codec_type(codec) == VT1708BCE)
732ded255beSTakashi Iwai 		snd_hda_codec_set_name(codec, "VT1708BCE");
733bc92df7fSLydia Wang 	/* correct names for VT1705 */
734ded255beSTakashi Iwai 	if (codec->core.vendor_id == 0x11064397)
735ded255beSTakashi Iwai 		snd_hda_codec_set_name(codec, "VT1705");
736b3f6008fSTakashi Iwai 
737f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt1708S_init_verbs);
738f8bfc628STakashi Iwai 	if (err < 0)
739f8bfc628STakashi Iwai 		goto error;
740f8bfc628STakashi Iwai 
741b3f6008fSTakashi Iwai 	/* automatic parse from the BIOS config */
742b3f6008fSTakashi Iwai 	err = via_parse_auto_config(codec);
743fcbdcc1aSTakashi Iwai 	if (err < 0)
744fcbdcc1aSTakashi Iwai 		goto error;
745b3f6008fSTakashi Iwai 
746d949cac1SHarald Welte 	return 0;
747fcbdcc1aSTakashi Iwai 
748fcbdcc1aSTakashi Iwai  error:
749fcbdcc1aSTakashi Iwai 	via_free(codec);
750fcbdcc1aSTakashi Iwai 	return err;
751d949cac1SHarald Welte }
752d949cac1SHarald Welte 
753d949cac1SHarald Welte /* Patch for VT1702 */
754d949cac1SHarald Welte 
755096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = {
756bc7e7e5cSLydia Wang 	/* mixer enable */
757bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
758bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
759bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
760d949cac1SHarald Welte 	{ }
761d949cac1SHarald Welte };
762d949cac1SHarald Welte 
763d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
764d949cac1SHarald Welte {
765d949cac1SHarald Welte 	struct via_spec *spec;
766d949cac1SHarald Welte 	int err;
767d949cac1SHarald Welte 
768d949cac1SHarald Welte 	/* create a codec specific record */
7695b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
770d949cac1SHarald Welte 	if (spec == NULL)
771d949cac1SHarald Welte 		return -ENOMEM;
772d949cac1SHarald Welte 
773b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x1a;
774620e2b28STakashi Iwai 
77512daef65STakashi Iwai 	/* limit AA path volume to 0 dB */
77612daef65STakashi Iwai 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
77712daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
77812daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
77912daef65STakashi Iwai 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
78012daef65STakashi Iwai 				  (1 << AC_AMPCAP_MUTE_SHIFT));
78112daef65STakashi Iwai 
782f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt1702_init_verbs);
783f8bfc628STakashi Iwai 	if (err < 0)
784f8bfc628STakashi Iwai 		goto error;
785f8bfc628STakashi Iwai 
786d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
78712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
788fcbdcc1aSTakashi Iwai 	if (err < 0)
789fcbdcc1aSTakashi Iwai 		goto error;
790d949cac1SHarald Welte 
791d949cac1SHarald Welte 	return 0;
792fcbdcc1aSTakashi Iwai 
793fcbdcc1aSTakashi Iwai  error:
794fcbdcc1aSTakashi Iwai 	via_free(codec);
795fcbdcc1aSTakashi Iwai 	return err;
796d949cac1SHarald Welte }
797d949cac1SHarald Welte 
798eb7188caSLydia Wang /* Patch for VT1718S */
799eb7188caSLydia Wang 
800096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = {
8014ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
8024ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
803eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
804eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
8055d41762aSTakashi Iwai 
806eb7188caSLydia Wang 	{ }
807eb7188caSLydia Wang };
808eb7188caSLydia Wang 
80930b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs
81030b45033STakashi Iwai  * This isn't listed from the raw info, but the chip has a secret connection.
81130b45033STakashi Iwai  */
81230b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec)
81330b45033STakashi Iwai {
81430b45033STakashi Iwai 	struct via_spec *spec = codec->spec;
81530b45033STakashi Iwai 	int i, nums;
81630b45033STakashi Iwai 	hda_nid_t conn[8];
81730b45033STakashi Iwai 	hda_nid_t nid;
81830b45033STakashi Iwai 
819b3f6008fSTakashi Iwai 	if (!spec->gen.mixer_nid)
82030b45033STakashi Iwai 		return 0;
821b3f6008fSTakashi Iwai 	nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
82230b45033STakashi Iwai 				       ARRAY_SIZE(conn) - 1);
82330b45033STakashi Iwai 	for (i = 0; i < nums; i++) {
82430b45033STakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
82530b45033STakashi Iwai 			return 0;
82630b45033STakashi Iwai 	}
82730b45033STakashi Iwai 
82830b45033STakashi Iwai 	/* find the primary DAC and add to the connection list */
8297639a06cSTakashi Iwai 	for_each_hda_codec_node(nid, codec) {
83030b45033STakashi Iwai 		unsigned int caps = get_wcaps(codec, nid);
83130b45033STakashi Iwai 		if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
83230b45033STakashi Iwai 		    !(caps & AC_WCAP_DIGITAL)) {
83330b45033STakashi Iwai 			conn[nums++] = nid;
83430b45033STakashi Iwai 			return snd_hda_override_conn_list(codec,
835b3f6008fSTakashi Iwai 							  spec->gen.mixer_nid,
83630b45033STakashi Iwai 							  nums, conn);
83730b45033STakashi Iwai 		}
83830b45033STakashi Iwai 	}
83930b45033STakashi Iwai 	return 0;
84030b45033STakashi Iwai }
84130b45033STakashi Iwai 
84230b45033STakashi Iwai 
843eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
844eb7188caSLydia Wang {
845eb7188caSLydia Wang 	struct via_spec *spec;
846eb7188caSLydia Wang 	int err;
847eb7188caSLydia Wang 
848eb7188caSLydia Wang 	/* create a codec specific record */
8495b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
850eb7188caSLydia Wang 	if (spec == NULL)
851eb7188caSLydia Wang 		return -ENOMEM;
852eb7188caSLydia Wang 
853b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
854d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
855d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
85630b45033STakashi Iwai 	add_secret_dac_path(codec);
857620e2b28STakashi Iwai 
858f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt1718S_init_verbs);
859f8bfc628STakashi Iwai 	if (err < 0)
860f8bfc628STakashi Iwai 		goto error;
861f8bfc628STakashi Iwai 
862eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
86312daef65STakashi Iwai 	err = via_parse_auto_config(codec);
864fcbdcc1aSTakashi Iwai 	if (err < 0)
865fcbdcc1aSTakashi Iwai 		goto error;
866eb7188caSLydia Wang 
867eb7188caSLydia Wang 	return 0;
868fcbdcc1aSTakashi Iwai 
869fcbdcc1aSTakashi Iwai  error:
870fcbdcc1aSTakashi Iwai 	via_free(codec);
871fcbdcc1aSTakashi Iwai 	return err;
872eb7188caSLydia Wang }
873f3db423dSLydia Wang 
874f3db423dSLydia Wang /* Patch for VT1716S */
875f3db423dSLydia Wang 
876f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
877f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
878f3db423dSLydia Wang {
879f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
880f3db423dSLydia Wang 	uinfo->count = 1;
881f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
882f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
883f3db423dSLydia Wang 	return 0;
884f3db423dSLydia Wang }
885f3db423dSLydia Wang 
886f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
887f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
888f3db423dSLydia Wang {
889f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
890f3db423dSLydia Wang 	int index = 0;
891f3db423dSLydia Wang 
892f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
893f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
894f3db423dSLydia Wang 	if (index != -1)
895f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
896f3db423dSLydia Wang 
897f3db423dSLydia Wang 	return 0;
898f3db423dSLydia Wang }
899f3db423dSLydia Wang 
900f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
901f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
902f3db423dSLydia Wang {
903f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
904f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
905f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
906f3db423dSLydia Wang 
907f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
908f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
909f3db423dSLydia Wang 	spec->dmic_enabled = index;
910f3db423dSLydia Wang 	return 1;
911f3db423dSLydia Wang }
912f3db423dSLydia Wang 
9130e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer_vol =
9140e8f9862STakashi Iwai 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT);
9150e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer_sw = {
916f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
917f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
9185b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
919f3db423dSLydia Wang 	 .count = 1,
920f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
921f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
922f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
923f3db423dSLydia Wang };
924f3db423dSLydia Wang 
925f3db423dSLydia Wang 
926f3db423dSLydia Wang /* mono-out mixer elements */
9270e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer =
9280e8f9862STakashi Iwai 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT);
929f3db423dSLydia Wang 
930096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = {
931f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
932f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
933f3db423dSLydia Wang 	/* don't bybass mixer */
934f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
935f3db423dSLydia Wang 	/* Enable mono output */
936f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
937f3db423dSLydia Wang 	{ }
938f3db423dSLydia Wang };
939f3db423dSLydia Wang 
940f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
941f3db423dSLydia Wang {
942f3db423dSLydia Wang 	struct via_spec *spec;
943f3db423dSLydia Wang 	int err;
944f3db423dSLydia Wang 
945f3db423dSLydia Wang 	/* create a codec specific record */
9465b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
947f3db423dSLydia Wang 	if (spec == NULL)
948f3db423dSLydia Wang 		return -ENOMEM;
949f3db423dSLydia Wang 
950b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
951d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
952d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
953620e2b28STakashi Iwai 
954f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt1716S_init_verbs);
955f8bfc628STakashi Iwai 	if (err < 0)
956f8bfc628STakashi Iwai 		goto error;
957f8bfc628STakashi Iwai 
958f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
95912daef65STakashi Iwai 	err = via_parse_auto_config(codec);
960fcbdcc1aSTakashi Iwai 	if (err < 0)
961fcbdcc1aSTakashi Iwai 		goto error;
962f3db423dSLydia Wang 
9630e8f9862STakashi Iwai 	if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_vol) ||
9640e8f9862STakashi Iwai 	    !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_sw) ||
9650e8f9862STakashi Iwai 	    !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716S_mono_out_mixer)) {
9660e8f9862STakashi Iwai 		err = -ENOMEM;
9670e8f9862STakashi Iwai 		goto error;
9680e8f9862STakashi Iwai 	}
969f3db423dSLydia Wang 
970f3db423dSLydia Wang 	return 0;
971fcbdcc1aSTakashi Iwai 
972fcbdcc1aSTakashi Iwai  error:
973fcbdcc1aSTakashi Iwai 	via_free(codec);
974fcbdcc1aSTakashi Iwai 	return err;
975f3db423dSLydia Wang }
97625eaba2fSLydia Wang 
97725eaba2fSLydia Wang /* for vt2002P */
97825eaba2fSLydia Wang 
979096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = {
980eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
981eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
982eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
983eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
98425eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
98525eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
98625eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
98725eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
98825eaba2fSLydia Wang 	{ }
98925eaba2fSLydia Wang };
9904a918ffeSTakashi Iwai 
991096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = {
99211890956SLydia Wang 	/* Enable Boost Volume backdoor */
99311890956SLydia Wang 	{0x1, 0xfb9, 0x24},
99411890956SLydia Wang 	/* Enable AOW0 to MW9 */
99511890956SLydia Wang 	{0x1, 0xfb8, 0x88},
99611890956SLydia Wang 	{ }
99711890956SLydia Wang };
99825eaba2fSLydia Wang 
9994b527b65SDavid Henningsson /*
10004b527b65SDavid Henningsson  * pin fix-up
10014b527b65SDavid Henningsson  */
10024b527b65SDavid Henningsson enum {
10034b527b65SDavid Henningsson 	VIA_FIXUP_INTMIC_BOOST,
1004d5266125STakashi Iwai 	VIA_FIXUP_ASUS_G75,
10054b527b65SDavid Henningsson };
10064b527b65SDavid Henningsson 
10074b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec,
10084b527b65SDavid Henningsson 				  const struct hda_fixup *fix, int action)
10094b527b65SDavid Henningsson {
10104b527b65SDavid Henningsson 	if (action == HDA_FIXUP_ACT_PRE_PROBE)
10114b527b65SDavid Henningsson 		override_mic_boost(codec, 0x30, 0, 2, 40);
10124b527b65SDavid Henningsson }
10134b527b65SDavid Henningsson 
10144b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = {
10154b527b65SDavid Henningsson 	[VIA_FIXUP_INTMIC_BOOST] = {
10164b527b65SDavid Henningsson 		.type = HDA_FIXUP_FUNC,
10174b527b65SDavid Henningsson 		.v.func = via_fixup_intmic_boost,
10184b527b65SDavid Henningsson 	},
1019d5266125STakashi Iwai 	[VIA_FIXUP_ASUS_G75] = {
1020d5266125STakashi Iwai 		.type = HDA_FIXUP_PINS,
1021d5266125STakashi Iwai 		.v.pins = (const struct hda_pintbl[]) {
1022d5266125STakashi Iwai 			/* set 0x24 and 0x33 as speakers */
1023d5266125STakashi Iwai 			{ 0x24, 0x991301f0 },
1024d5266125STakashi Iwai 			{ 0x33, 0x991301f1 }, /* subwoofer */
1025d5266125STakashi Iwai 			{ }
1026d5266125STakashi Iwai 		}
1027d5266125STakashi Iwai 	},
10284b527b65SDavid Henningsson };
10294b527b65SDavid Henningsson 
10304b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = {
1031d5266125STakashi Iwai 	SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
10324b527b65SDavid Henningsson 	SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
10334b527b65SDavid Henningsson 	{}
10344b527b65SDavid Henningsson };
10354b527b65SDavid Henningsson 
1036ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
1037ef4da458STakashi Iwai  * Replace this with mixer NID 0x1c
1038ef4da458STakashi Iwai  */
1039ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec)
1040ef4da458STakashi Iwai {
1041*caf3c043SMichał Mirosław 	static const hda_nid_t conn_24[] = { 0x14, 0x1c };
1042*caf3c043SMichał Mirosław 	static const hda_nid_t conn_33[] = { 0x1c };
1043ef4da458STakashi Iwai 
1044ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
1045ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
1046ef4da458STakashi Iwai }
1047ef4da458STakashi Iwai 
104825eaba2fSLydia Wang /* patch for vt2002P */
104925eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
105025eaba2fSLydia Wang {
105125eaba2fSLydia Wang 	struct via_spec *spec;
105225eaba2fSLydia Wang 	int err;
105325eaba2fSLydia Wang 
105425eaba2fSLydia Wang 	/* create a codec specific record */
10555b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
105625eaba2fSLydia Wang 	if (spec == NULL)
105725eaba2fSLydia Wang 		return -ENOMEM;
105825eaba2fSLydia Wang 
1059b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1060d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1061d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
1062ef4da458STakashi Iwai 	if (spec->codec_type == VT1802)
1063ef4da458STakashi Iwai 		fix_vt1802_connections(codec);
106430b45033STakashi Iwai 	add_secret_dac_path(codec);
1065620e2b28STakashi Iwai 
10664b527b65SDavid Henningsson 	snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
10674b527b65SDavid Henningsson 	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
10684b527b65SDavid Henningsson 
1069f8bfc628STakashi Iwai 	if (spec->codec_type == VT1802)
1070f8bfc628STakashi Iwai 		err = snd_hda_add_verbs(codec, vt1802_init_verbs);
1071f8bfc628STakashi Iwai 	else
1072f8bfc628STakashi Iwai 		err = snd_hda_add_verbs(codec, vt2002P_init_verbs);
1073f8bfc628STakashi Iwai 	if (err < 0)
1074f8bfc628STakashi Iwai 		goto error;
1075f8bfc628STakashi Iwai 
107625eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
107712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1078fcbdcc1aSTakashi Iwai 	if (err < 0)
1079fcbdcc1aSTakashi Iwai 		goto error;
108025eaba2fSLydia Wang 
108125eaba2fSLydia Wang 	return 0;
1082fcbdcc1aSTakashi Iwai 
1083fcbdcc1aSTakashi Iwai  error:
1084fcbdcc1aSTakashi Iwai 	via_free(codec);
1085fcbdcc1aSTakashi Iwai 	return err;
108625eaba2fSLydia Wang }
1087ab6734e7SLydia Wang 
1088ab6734e7SLydia Wang /* for vt1812 */
1089ab6734e7SLydia Wang 
1090096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = {
1091ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
1092ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
1093ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
1094ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
1095ab6734e7SLydia Wang 	{ }
1096ab6734e7SLydia Wang };
1097ab6734e7SLydia Wang 
1098ab6734e7SLydia Wang /* patch for vt1812 */
1099ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
1100ab6734e7SLydia Wang {
1101ab6734e7SLydia Wang 	struct via_spec *spec;
1102ab6734e7SLydia Wang 	int err;
1103ab6734e7SLydia Wang 
1104ab6734e7SLydia Wang 	/* create a codec specific record */
11055b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1106ab6734e7SLydia Wang 	if (spec == NULL)
1107ab6734e7SLydia Wang 		return -ENOMEM;
1108ab6734e7SLydia Wang 
1109b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1110d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1111d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
111230b45033STakashi Iwai 	add_secret_dac_path(codec);
1113620e2b28STakashi Iwai 
1114f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt1812_init_verbs);
1115f8bfc628STakashi Iwai 	if (err < 0)
1116f8bfc628STakashi Iwai 		goto error;
1117f8bfc628STakashi Iwai 
1118ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
111912daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1120fcbdcc1aSTakashi Iwai 	if (err < 0)
1121fcbdcc1aSTakashi Iwai 		goto error;
1122ab6734e7SLydia Wang 
1123ab6734e7SLydia Wang 	return 0;
1124fcbdcc1aSTakashi Iwai 
1125fcbdcc1aSTakashi Iwai  error:
1126fcbdcc1aSTakashi Iwai 	via_free(codec);
1127fcbdcc1aSTakashi Iwai 	return err;
1128ab6734e7SLydia Wang }
1129ab6734e7SLydia Wang 
113043737e0aSLydia Wang /* patch for vt3476 */
113143737e0aSLydia Wang 
113243737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = {
113343737e0aSLydia Wang 	/* Enable DMic 8/16/32K */
113443737e0aSLydia Wang 	{0x1, 0xF7B, 0x30},
113543737e0aSLydia Wang 	/* Enable Boost Volume backdoor */
113643737e0aSLydia Wang 	{0x1, 0xFB9, 0x20},
113743737e0aSLydia Wang 	/* Enable AOW-MW9 path */
113843737e0aSLydia Wang 	{0x1, 0xFB8, 0x10},
113943737e0aSLydia Wang 	{ }
114043737e0aSLydia Wang };
114143737e0aSLydia Wang 
114243737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec)
114343737e0aSLydia Wang {
114443737e0aSLydia Wang 	struct via_spec *spec;
114543737e0aSLydia Wang 	int err;
114643737e0aSLydia Wang 
114743737e0aSLydia Wang 	/* create a codec specific record */
114843737e0aSLydia Wang 	spec = via_new_spec(codec);
114943737e0aSLydia Wang 	if (spec == NULL)
115043737e0aSLydia Wang 		return -ENOMEM;
115143737e0aSLydia Wang 
1152b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x3f;
115343737e0aSLydia Wang 	add_secret_dac_path(codec);
115443737e0aSLydia Wang 
1155f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt3476_init_verbs);
1156f8bfc628STakashi Iwai 	if (err < 0)
1157f8bfc628STakashi Iwai 		goto error;
1158f8bfc628STakashi Iwai 
115943737e0aSLydia Wang 	/* automatic parse from the BIOS config */
116043737e0aSLydia Wang 	err = via_parse_auto_config(codec);
1161fcbdcc1aSTakashi Iwai 	if (err < 0)
1162fcbdcc1aSTakashi Iwai 		goto error;
116343737e0aSLydia Wang 
116443737e0aSLydia Wang 	return 0;
1165fcbdcc1aSTakashi Iwai 
1166fcbdcc1aSTakashi Iwai  error:
1167fcbdcc1aSTakashi Iwai 	via_free(codec);
1168fcbdcc1aSTakashi Iwai 	return err;
116943737e0aSLydia Wang }
117043737e0aSLydia Wang 
1171c577b8a1SJoseph Chan /*
1172c577b8a1SJoseph Chan  * patch entries
1173c577b8a1SJoseph Chan  */
1174b9a94a9cSTakashi Iwai static const struct hda_device_id snd_hda_id_via[] = {
1175b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708),
1176b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708),
1177b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708),
1178b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708),
1179b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709),
1180b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709),
1181b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709),
1182b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709),
1183b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709),
1184b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709),
1185b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709),
1186b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709),
1187b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B),
1188b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B),
1189b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B),
1190b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B),
1191b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B),
1192b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B),
1193b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B),
1194b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B),
1195b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S),
1196b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S),
1197b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S),
1198b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S),
1199b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S),
1200b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S),
1201b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S),
1202b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S),
1203b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702),
1204b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702),
1205b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702),
1206b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702),
1207b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702),
1208b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702),
1209b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702),
1210b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702),
1211b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S),
1212b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S),
1213b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S),
1214b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S),
1215b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S),
1216b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S),
1217b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P),
1218b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P),
1219b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812),
1220b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S),
1221b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P),
1222b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P),
1223b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476),
1224b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476),
1225b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476),
1226c577b8a1SJoseph Chan 	{} /* terminator */
1227c577b8a1SJoseph Chan };
1228b9a94a9cSTakashi Iwai MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via);
12291289e9e8STakashi Iwai 
1230d8a766a1STakashi Iwai static struct hda_codec_driver via_driver = {
1231b9a94a9cSTakashi Iwai 	.id = snd_hda_id_via,
12321289e9e8STakashi Iwai };
12331289e9e8STakashi Iwai 
12341289e9e8STakashi Iwai MODULE_LICENSE("GPL");
12351289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
12361289e9e8STakashi Iwai 
1237d8a766a1STakashi Iwai module_hda_codec_driver(via_driver);
1238