xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision 688b12cc3ca8a5155b95ce8d01e0e43006813b27)
1c577b8a1SJoseph Chan /*
2c577b8a1SJoseph Chan  * Universal Interface for Intel High Definition Audio Codec
3c577b8a1SJoseph Chan  *
48e86597fSLydia Wang  * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
5c577b8a1SJoseph Chan  *
68e86597fSLydia Wang  *  (C) 2006-2009 VIA Technology, Inc.
78e86597fSLydia Wang  *  (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
8c577b8a1SJoseph Chan  *
9c577b8a1SJoseph Chan  *  This driver is free software; you can redistribute it and/or modify
10c577b8a1SJoseph Chan  *  it under the terms of the GNU General Public License as published by
11c577b8a1SJoseph Chan  *  the Free Software Foundation; either version 2 of the License, or
12c577b8a1SJoseph Chan  *  (at your option) any later version.
13c577b8a1SJoseph Chan  *
14c577b8a1SJoseph Chan  *  This driver is distributed in the hope that it will be useful,
15c577b8a1SJoseph Chan  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16c577b8a1SJoseph Chan  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17c577b8a1SJoseph Chan  *  GNU General Public License for more details.
18c577b8a1SJoseph Chan  *
19c577b8a1SJoseph Chan  *  You should have received a copy of the GNU General Public License
20c577b8a1SJoseph Chan  *  along with this program; if not, write to the Free Software
21c577b8a1SJoseph Chan  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22c577b8a1SJoseph Chan  */
23c577b8a1SJoseph Chan 
24c577b8a1SJoseph Chan /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
25c577b8a1SJoseph Chan /*									     */
26c577b8a1SJoseph Chan /* 2006-03-03  Lydia Wang  Create the basic patch to support VT1708 codec    */
27c577b8a1SJoseph Chan /* 2006-03-14  Lydia Wang  Modify hard code for some pin widget nid	     */
28c577b8a1SJoseph Chan /* 2006-08-02  Lydia Wang  Add support to VT1709 codec			     */
29c577b8a1SJoseph Chan /* 2006-09-08  Lydia Wang  Fix internal loopback recording source select bug */
30f7278fd0SJosepch Chan /* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization	     */
31f7278fd0SJosepch Chan /* 2007-09-17  Lydia Wang  Add VT1708B codec support			    */
3276d9b0ddSHarald Welte /* 2007-11-14  Lydia Wang  Add VT1708A codec HP and CD pin connect config    */
33fb4cb772SHarald Welte /* 2008-02-03  Lydia Wang  Fix Rear channels and Back channels inverse issue */
34d949cac1SHarald Welte /* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support	     */
3569e52a80SHarald Welte /* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin	     */
360aa62aefSHarald Welte /* 2008-04-09  Lydia Wang  Add Independent HP feature			     */
3798aa34c0SHarald Welte /* 2008-05-28  Lydia Wang  Add second S/PDIF Out support for VT1702	     */
38d7426329SHarald Welte /* 2008-09-15  Logan Li	   Add VT1708S Mic Boost workaround/backdoor	     */
398e86597fSLydia Wang /* 2009-02-16  Logan Li	   Add support for VT1718S			     */
408e86597fSLydia Wang /* 2009-03-13  Logan Li	   Add support for VT1716S			     */
418e86597fSLydia Wang /* 2009-04-14  Lydai Wang  Add support for VT1828S and VT2020		     */
428e86597fSLydia Wang /* 2009-07-08  Lydia Wang  Add support for VT2002P			     */
438e86597fSLydia Wang /* 2009-07-21  Lydia Wang  Add support for VT1812			     */
4436dd5c4aSLydia Wang /* 2009-09-19  Lydia Wang  Add support for VT1818S			     */
45c577b8a1SJoseph Chan /*									     */
46c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47c577b8a1SJoseph Chan 
48c577b8a1SJoseph Chan 
49c577b8a1SJoseph Chan #include <linux/init.h>
50c577b8a1SJoseph Chan #include <linux/delay.h>
51c577b8a1SJoseph Chan #include <linux/slab.h>
52da155d5bSPaul Gortmaker #include <linux/module.h>
53c577b8a1SJoseph Chan #include <sound/core.h>
540aa62aefSHarald Welte #include <sound/asoundef.h>
55c577b8a1SJoseph Chan #include "hda_codec.h"
56c577b8a1SJoseph Chan #include "hda_local.h"
57128bc4baSTakashi Iwai #include "hda_auto_parser.h"
581835a0f9STakashi Iwai #include "hda_jack.h"
59b3f6008fSTakashi Iwai #include "hda_generic.h"
60c577b8a1SJoseph Chan 
61c577b8a1SJoseph Chan /* Pin Widget NID */
6276d9b0ddSHarald Welte #define VT1708_HP_PIN_NID	0x20
6376d9b0ddSHarald Welte #define VT1708_CD_PIN_NID	0x24
64c577b8a1SJoseph Chan 
65d7426329SHarald Welte enum VIA_HDA_CODEC {
66d7426329SHarald Welte 	UNKNOWN = -1,
67d7426329SHarald Welte 	VT1708,
68d7426329SHarald Welte 	VT1709_10CH,
69d7426329SHarald Welte 	VT1709_6CH,
70d7426329SHarald Welte 	VT1708B_8CH,
71d7426329SHarald Welte 	VT1708B_4CH,
72d7426329SHarald Welte 	VT1708S,
73518bf3baSLydia Wang 	VT1708BCE,
74d7426329SHarald Welte 	VT1702,
75eb7188caSLydia Wang 	VT1718S,
76f3db423dSLydia Wang 	VT1716S,
7725eaba2fSLydia Wang 	VT2002P,
78ab6734e7SLydia Wang 	VT1812,
7911890956SLydia Wang 	VT1802,
8043737e0aSLydia Wang 	VT1705CF,
816121b84aSLydia Wang 	VT1808,
82d7426329SHarald Welte 	CODEC_TYPES,
83d7426329SHarald Welte };
84d7426329SHarald Welte 
8511890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \
8611890956SLydia Wang 	((spec)->codec_type == VT2002P ||\
8711890956SLydia Wang 	 (spec)->codec_type == VT1812 ||\
8811890956SLydia Wang 	 (spec)->codec_type == VT1802)
8911890956SLydia Wang 
901f2e99feSLydia Wang struct via_spec {
91b3f6008fSTakashi Iwai 	struct hda_gen_spec gen;
92b3f6008fSTakashi Iwai 
931f2e99feSLydia Wang 	/* codec parameterization */
9490dd48a1STakashi Iwai 	const struct snd_kcontrol_new *mixers[6];
951f2e99feSLydia Wang 	unsigned int num_mixers;
961f2e99feSLydia Wang 
9790dd48a1STakashi Iwai 	const struct hda_verb *init_verbs[5];
981f2e99feSLydia Wang 	unsigned int num_iverbs;
991f2e99feSLydia Wang 
1001f2e99feSLydia Wang 	/* HP mode source */
101f3db423dSLydia Wang 	unsigned int dmic_enabled;
1021f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1031f2e99feSLydia Wang 
104e9d010c2STakashi Iwai 	/* analog low-power control */
105e9d010c2STakashi Iwai 	bool alc_mode;
106e9d010c2STakashi Iwai 
1071f2e99feSLydia Wang 	/* work to check hp jack state */
108187d333eSTakashi Iwai 	int hp_work_active;
109e06e5a29STakashi Iwai 	int vt1708_jack_detect;
1101f2e99feSLydia Wang };
1111f2e99feSLydia Wang 
1120341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
113b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
114b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
115b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
116b3f6008fSTakashi Iwai 				  int action);
117b3f6008fSTakashi Iwai 
1185b0cb1d8SJaroslav Kysela static struct via_spec *via_new_spec(struct hda_codec *codec)
1195b0cb1d8SJaroslav Kysela {
1205b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1215b0cb1d8SJaroslav Kysela 
1225b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1235b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1245b0cb1d8SJaroslav Kysela 		return NULL;
1255b0cb1d8SJaroslav Kysela 
1265b0cb1d8SJaroslav Kysela 	codec->spec = spec;
127b3f6008fSTakashi Iwai 	snd_hda_gen_spec_init(&spec->gen);
1280341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
1290341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
1300341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
1310341ccd7SLydia Wang 		spec->codec_type = VT1708S;
13213961170STakashi Iwai 	spec->gen.indep_hp = 1;
13305909d5cSTakashi Iwai 	spec->gen.keep_eapd_on = 1;
134b3f6008fSTakashi Iwai 	spec->gen.pcm_playback_hook = via_playback_pcm_hook;
13574f14b36STakashi Iwai 	spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
136*688b12ccSTakashi Iwai 	codec->power_mgmt = 1;
137*688b12ccSTakashi Iwai 	spec->gen.power_down_unused = 1;
1385b0cb1d8SJaroslav Kysela 	return spec;
1395b0cb1d8SJaroslav Kysela }
1405b0cb1d8SJaroslav Kysela 
141744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
142d7426329SHarald Welte {
143744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
144d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
145d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
146d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
147d7426329SHarald Welte 
148d7426329SHarald Welte 	/* get codec type */
149d7426329SHarald Welte 	if (ven_id != 0x1106)
150d7426329SHarald Welte 		codec_type = UNKNOWN;
151d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
152d7426329SHarald Welte 		codec_type = VT1708;
153d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
154d7426329SHarald Welte 		codec_type = VT1709_10CH;
155d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
156d7426329SHarald Welte 		codec_type = VT1709_6CH;
157518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
158d7426329SHarald Welte 		codec_type = VT1708B_8CH;
159518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
160518bf3baSLydia Wang 			codec_type = VT1708BCE;
161518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
162d7426329SHarald Welte 		codec_type = VT1708B_4CH;
163d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
164d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
165d7426329SHarald Welte 		codec_type = VT1708S;
166d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
167d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
168d7426329SHarald Welte 		codec_type = VT1702;
169eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
170eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
171eb7188caSLydia Wang 		codec_type = VT1718S;
172f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
173f3db423dSLydia Wang 		codec_type = VT1716S;
174bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
175bb3c6bfcSLydia Wang 		codec_type = VT1718S;
17625eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
17725eaba2fSLydia Wang 		codec_type = VT2002P;
178ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
179ab6734e7SLydia Wang 		codec_type = VT1812;
18036dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
18136dd5c4aSLydia Wang 		codec_type = VT1708S;
18211890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
18311890956SLydia Wang 		codec_type = VT1802;
18443737e0aSLydia Wang 	else if (dev_id == 0x4760)
18543737e0aSLydia Wang 		codec_type = VT1705CF;
1866121b84aSLydia Wang 	else if (dev_id == 0x4761 || dev_id == 0x4762)
1876121b84aSLydia Wang 		codec_type = VT1808;
188d7426329SHarald Welte 	else
189d7426329SHarald Welte 		codec_type = UNKNOWN;
190d7426329SHarald Welte 	return codec_type;
191d7426329SHarald Welte };
192d7426329SHarald Welte 
193ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec);
194ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec);
1951f2e99feSLydia Wang 
196187d333eSTakashi Iwai #define hp_detect_with_aa(codec) \
197187d333eSTakashi Iwai 	(snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
198187d333eSTakashi Iwai 	 !is_aa_path_mute(codec))
1991f2e99feSLydia Wang 
200b3f6008fSTakashi Iwai static void vt1708_stop_hp_work(struct hda_codec *codec)
2011f2e99feSLydia Wang {
202b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
203b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
2041f2e99feSLydia Wang 		return;
205187d333eSTakashi Iwai 	if (spec->hp_work_active) {
206b3f6008fSTakashi Iwai 		snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
2077eaa9161SWang Xingchao 		codec->jackpoll_interval = 0;
208b3f6008fSTakashi Iwai 		cancel_delayed_work_sync(&codec->jackpoll_work);
209b3f6008fSTakashi Iwai 		spec->hp_work_active = false;
210187d333eSTakashi Iwai 	}
211187d333eSTakashi Iwai }
212187d333eSTakashi Iwai 
213b3f6008fSTakashi Iwai static void vt1708_update_hp_work(struct hda_codec *codec)
214187d333eSTakashi Iwai {
215b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
216b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
217187d333eSTakashi Iwai 		return;
21805dc0fc9SDavid Henningsson 	if (spec->vt1708_jack_detect) {
219187d333eSTakashi Iwai 		if (!spec->hp_work_active) {
220b3f6008fSTakashi Iwai 			codec->jackpoll_interval = msecs_to_jiffies(100);
221b3f6008fSTakashi Iwai 			snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0);
2222f35c630STakashi Iwai 			schedule_delayed_work(&codec->jackpoll_work, 0);
223b3f6008fSTakashi Iwai 			spec->hp_work_active = true;
224187d333eSTakashi Iwai 		}
225b3f6008fSTakashi Iwai 	} else if (!hp_detect_with_aa(codec))
226b3f6008fSTakashi Iwai 		vt1708_stop_hp_work(codec);
2271f2e99feSLydia Wang }
228f5271101SLydia Wang 
22924088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
23024088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
23124088a58STakashi Iwai {
232dda415d4STakashi Iwai 	return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
23324088a58STakashi Iwai }
23424088a58STakashi Iwai 
23524088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
23624088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
23724088a58STakashi Iwai {
23824088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
239*688b12ccSTakashi Iwai 	ucontrol->value.enumerated.item[0] = codec->power_mgmt;
24024088a58STakashi Iwai 	return 0;
24124088a58STakashi Iwai }
24224088a58STakashi Iwai 
24324088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
24424088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
24524088a58STakashi Iwai {
24624088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
24724088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
248*688b12ccSTakashi Iwai 	bool val = !!ucontrol->value.enumerated.item[0];
24924088a58STakashi Iwai 
250*688b12ccSTakashi Iwai 	if (val == codec->power_mgmt)
25124088a58STakashi Iwai 		return 0;
252*688b12ccSTakashi Iwai 	codec->power_mgmt = val;
253*688b12ccSTakashi Iwai 	spec->gen.power_down_unused = val;
254e9d010c2STakashi Iwai 	analog_low_current_mode(codec);
25524088a58STakashi Iwai 	return 1;
25624088a58STakashi Iwai }
25724088a58STakashi Iwai 
258b3f6008fSTakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = {
259b3f6008fSTakashi Iwai 	{
26024088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
26124088a58STakashi Iwai 	.name = "Dynamic Power-Control",
26224088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
26324088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
26424088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
265b3f6008fSTakashi Iwai 	},
266b3f6008fSTakashi Iwai 	{} /* terminator */
26724088a58STakashi Iwai };
26824088a58STakashi Iwai 
26924088a58STakashi Iwai 
270f5271101SLydia Wang /* check AA path's mute status */
271ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec)
272ada509ecSTakashi Iwai {
273ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
274ada509ecSTakashi Iwai 	const struct hda_amp_list *p;
2750186f4f4STakashi Iwai 	int ch, v;
276ada509ecSTakashi Iwai 
2770186f4f4STakashi Iwai 	p = spec->gen.loopback.amplist;
2780186f4f4STakashi Iwai 	if (!p)
2790186f4f4STakashi Iwai 		return true;
2800186f4f4STakashi Iwai 	for (; p->nid; p++) {
281ada509ecSTakashi Iwai 		for (ch = 0; ch < 2; ch++) {
282ada509ecSTakashi Iwai 			v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
283ada509ecSTakashi Iwai 						   p->idx);
284ada509ecSTakashi Iwai 			if (!(v & HDA_AMP_MUTE) && v > 0)
285ada509ecSTakashi Iwai 				return false;
286f5271101SLydia Wang 		}
287f5271101SLydia Wang 	}
288ada509ecSTakashi Iwai 	return true;
289f5271101SLydia Wang }
290f5271101SLydia Wang 
291f5271101SLydia Wang /* enter/exit analog low-current mode */
292e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force)
293f5271101SLydia Wang {
294f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
295ada509ecSTakashi Iwai 	bool enable;
296ada509ecSTakashi Iwai 	unsigned int verb, parm;
297f5271101SLydia Wang 
298*688b12ccSTakashi Iwai 	if (!codec->power_mgmt)
299e9d010c2STakashi Iwai 		enable = false;
300e9d010c2STakashi Iwai 	else
301b3f6008fSTakashi Iwai 		enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
302e9d010c2STakashi Iwai 	if (enable == spec->alc_mode && !force)
303e9d010c2STakashi Iwai 		return;
304e9d010c2STakashi Iwai 	spec->alc_mode = enable;
305f5271101SLydia Wang 
306f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
307f5271101SLydia Wang 	switch (spec->codec_type) {
308f5271101SLydia Wang 	case VT1708B_8CH:
309f5271101SLydia Wang 	case VT1708B_4CH:
310f5271101SLydia Wang 		verb = 0xf70;
311f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
312f5271101SLydia Wang 		break;
313f5271101SLydia Wang 	case VT1708S:
314eb7188caSLydia Wang 	case VT1718S:
315f3db423dSLydia Wang 	case VT1716S:
316f5271101SLydia Wang 		verb = 0xf73;
317f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
318f5271101SLydia Wang 		break;
319f5271101SLydia Wang 	case VT1702:
320f5271101SLydia Wang 		verb = 0xf73;
321f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
322f5271101SLydia Wang 		break;
32325eaba2fSLydia Wang 	case VT2002P:
324ab6734e7SLydia Wang 	case VT1812:
32511890956SLydia Wang 	case VT1802:
32625eaba2fSLydia Wang 		verb = 0xf93;
32725eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
32825eaba2fSLydia Wang 		break;
32943737e0aSLydia Wang 	case VT1705CF:
3306121b84aSLydia Wang 	case VT1808:
33143737e0aSLydia Wang 		verb = 0xf82;
33243737e0aSLydia Wang 		parm = enable ? 0x00 : 0xe0;  /* 0x00: 4/40x, 0xe0: 1x */
33343737e0aSLydia Wang 		break;
334f5271101SLydia Wang 	default:
335f5271101SLydia Wang 		return;		/* other codecs are not supported */
336f5271101SLydia Wang 	}
337f5271101SLydia Wang 	/* send verb */
338f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
339f5271101SLydia Wang }
340f5271101SLydia Wang 
341e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec)
342e9d010c2STakashi Iwai {
343e9d010c2STakashi Iwai 	return __analog_low_current_mode(codec, false);
344e9d010c2STakashi Iwai }
345e9d010c2STakashi Iwai 
346c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
347c577b8a1SJoseph Chan {
348c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
3495b0cb1d8SJaroslav Kysela 	int err, i;
350c577b8a1SJoseph Chan 
351b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_controls(codec);
352b3f6008fSTakashi Iwai 	if (err < 0)
353b3f6008fSTakashi Iwai 		return err;
354b3f6008fSTakashi Iwai 
355b3f6008fSTakashi Iwai 	spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum;
35624088a58STakashi Iwai 
357c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
358c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
359c577b8a1SJoseph Chan 		if (err < 0)
360c577b8a1SJoseph Chan 			return err;
361c577b8a1SJoseph Chan 	}
362c577b8a1SJoseph Chan 
363c577b8a1SJoseph Chan 	return 0;
364c577b8a1SJoseph Chan }
365c577b8a1SJoseph Chan 
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 }
3931f2e99feSLydia Wang #endif
3941f2e99feSLydia Wang 
39583012a7cSTakashi Iwai #ifdef CONFIG_PM
396cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
397cb53c626STakashi Iwai {
398cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
399b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
400b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
401b3f6008fSTakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
402cb53c626STakashi Iwai }
403cb53c626STakashi Iwai #endif
404cb53c626STakashi Iwai 
405c577b8a1SJoseph Chan /*
406c577b8a1SJoseph Chan  */
4075d41762aSTakashi Iwai 
4085d41762aSTakashi Iwai static int via_init(struct hda_codec *codec);
4095d41762aSTakashi Iwai 
41090dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
411c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
412b3f6008fSTakashi Iwai 	.build_pcms = snd_hda_gen_build_pcms,
413c577b8a1SJoseph Chan 	.init = via_init,
414c577b8a1SJoseph Chan 	.free = via_free,
4154e2d16d3SDavid Henningsson 	.unsol_event = snd_hda_jack_unsol_event,
4162a43952aSTakashi Iwai #ifdef CONFIG_PM
4171f2e99feSLydia Wang 	.suspend = via_suspend,
418cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
419cb53c626STakashi Iwai #endif
420c577b8a1SJoseph Chan };
421c577b8a1SJoseph Chan 
4224a79616dSTakashi Iwai 
423b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = {
424b3f6008fSTakashi Iwai 	/* power down jack detect function */
425b3f6008fSTakashi Iwai 	{0x1, 0xf81, 0x1},
426b3f6008fSTakashi Iwai 	{ }
4274a79616dSTakashi Iwai };
42876d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
42976d9b0ddSHarald Welte {
43076d9b0ddSHarald Welte 	unsigned int def_conf;
43176d9b0ddSHarald Welte 	unsigned char seqassoc;
43276d9b0ddSHarald Welte 
4332f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
43476d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
43576d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
43682ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
43782ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
43876d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
4392f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
44076d9b0ddSHarald Welte 	}
44176d9b0ddSHarald Welte 
44276d9b0ddSHarald Welte 	return;
44376d9b0ddSHarald Welte }
44476d9b0ddSHarald Welte 
445e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
4461f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
4471f2e99feSLydia Wang {
4481f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4491f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
4501f2e99feSLydia Wang 
4511f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
4521f2e99feSLydia Wang 		return 0;
453e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
4541f2e99feSLydia Wang 	return 0;
4551f2e99feSLydia Wang }
4561f2e99feSLydia Wang 
457e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
4581f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
4591f2e99feSLydia Wang {
4601f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4611f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
462187d333eSTakashi Iwai 	int val;
4631f2e99feSLydia Wang 
4641f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
4651f2e99feSLydia Wang 		return 0;
466187d333eSTakashi Iwai 	val = !!ucontrol->value.integer.value[0];
467187d333eSTakashi Iwai 	if (spec->vt1708_jack_detect == val)
468187d333eSTakashi Iwai 		return 0;
469187d333eSTakashi Iwai 	spec->vt1708_jack_detect = val;
470b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
471187d333eSTakashi Iwai 	return 1;
4721f2e99feSLydia Wang }
4731f2e99feSLydia Wang 
474b3f6008fSTakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = {
475b3f6008fSTakashi Iwai 	{
4761f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4771f2e99feSLydia Wang 	.name = "Jack Detect",
4781f2e99feSLydia Wang 	.count = 1,
4791f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
480e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
481e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
482b3f6008fSTakashi Iwai 	},
483b3f6008fSTakashi Iwai 	{} /* terminator */
4841f2e99feSLydia Wang };
4851f2e99feSLydia Wang 
4864abdbd1cSTakashi Iwai static const struct badness_table via_main_out_badness = {
4874abdbd1cSTakashi Iwai 	.no_primary_dac = 0x10000,
4884abdbd1cSTakashi Iwai 	.no_dac = 0x4000,
4894abdbd1cSTakashi Iwai 	.shared_primary = 0x10000,
4904abdbd1cSTakashi Iwai 	.shared_surr = 0x20,
4914abdbd1cSTakashi Iwai 	.shared_clfe = 0x20,
4924abdbd1cSTakashi Iwai 	.shared_surr_main = 0x20,
4934abdbd1cSTakashi Iwai };
4944abdbd1cSTakashi Iwai static const struct badness_table via_extra_out_badness = {
4954abdbd1cSTakashi Iwai 	.no_primary_dac = 0x4000,
4964abdbd1cSTakashi Iwai 	.no_dac = 0x4000,
4974abdbd1cSTakashi Iwai 	.shared_primary = 0x12,
4984abdbd1cSTakashi Iwai 	.shared_surr = 0x20,
4994abdbd1cSTakashi Iwai 	.shared_clfe = 0x20,
5004abdbd1cSTakashi Iwai 	.shared_surr_main = 0x10,
5014abdbd1cSTakashi Iwai };
5024abdbd1cSTakashi Iwai 
503b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec)
504b3f6008fSTakashi Iwai {
505b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
506b3f6008fSTakashi Iwai 	int err;
507b3f6008fSTakashi Iwai 
5084abdbd1cSTakashi Iwai 	spec->gen.main_out_badness = &via_main_out_badness;
5094abdbd1cSTakashi Iwai 	spec->gen.extra_out_badness = &via_extra_out_badness;
5104abdbd1cSTakashi Iwai 
511b3f6008fSTakashi Iwai 	err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
512b3f6008fSTakashi Iwai 	if (err < 0)
513b3f6008fSTakashi Iwai 		return err;
514b3f6008fSTakashi Iwai 
515b3f6008fSTakashi Iwai 	err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
516b3f6008fSTakashi Iwai 	if (err < 0)
517b3f6008fSTakashi Iwai 		return err;
518b3f6008fSTakashi Iwai 
519*688b12ccSTakashi Iwai 	/* disable widget PM at start for compatibility */
520*688b12ccSTakashi Iwai 	codec->power_mgmt = 0;
521*688b12ccSTakashi Iwai 	spec->gen.power_down_unused = 0;
522b3f6008fSTakashi Iwai 	return 0;
5234a918ffeSTakashi Iwai }
5244a918ffeSTakashi Iwai 
5255d41762aSTakashi Iwai static int via_init(struct hda_codec *codec)
5265d41762aSTakashi Iwai {
5275d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
5285d41762aSTakashi Iwai 	int i;
5295d41762aSTakashi Iwai 
5305d41762aSTakashi Iwai 	for (i = 0; i < spec->num_iverbs; i++)
5315d41762aSTakashi Iwai 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
5325d41762aSTakashi Iwai 
533e9d010c2STakashi Iwai 	/* init power states */
534e9d010c2STakashi Iwai 	__analog_low_current_mode(codec, true);
535e9d010c2STakashi Iwai 
536b3f6008fSTakashi Iwai 	snd_hda_gen_init(codec);
53711890956SLydia Wang 
538b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
53925eaba2fSLydia Wang 
540c577b8a1SJoseph Chan 	return 0;
541c577b8a1SJoseph Chan }
542c577b8a1SJoseph Chan 
543f672f65aSDavid Henningsson static int vt1708_build_controls(struct hda_codec *codec)
544f672f65aSDavid Henningsson {
545f672f65aSDavid Henningsson 	/* In order not to create "Phantom Jack" controls,
546f672f65aSDavid Henningsson 	   temporary enable jackpoll */
547f672f65aSDavid Henningsson 	int err;
548f672f65aSDavid Henningsson 	int old_interval = codec->jackpoll_interval;
549f672f65aSDavid Henningsson 	codec->jackpoll_interval = msecs_to_jiffies(100);
550f672f65aSDavid Henningsson 	err = via_build_controls(codec);
551f672f65aSDavid Henningsson 	codec->jackpoll_interval = old_interval;
552f672f65aSDavid Henningsson 	return err;
553f672f65aSDavid Henningsson }
554f672f65aSDavid Henningsson 
555b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec)
556337b9d02STakashi Iwai {
557337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
558b3f6008fSTakashi Iwai 	int i, err;
559337b9d02STakashi Iwai 
560b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_pcms(codec);
561b3f6008fSTakashi Iwai 	if (err < 0 || codec->vendor_id != 0x11061708)
562b3f6008fSTakashi Iwai 		return err;
563b3f6008fSTakashi Iwai 
564b3f6008fSTakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
565b3f6008fSTakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
566b3f6008fSTakashi Iwai 	 * disable the 24bit format, so far.
567b3f6008fSTakashi Iwai 	 */
568bbbc7e85STakashi Iwai 	for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) {
569bbbc7e85STakashi Iwai 		struct hda_pcm *info = spec->gen.pcm_rec[i];
570bbbc7e85STakashi Iwai 		if (!info)
571bbbc7e85STakashi Iwai 			continue;
572b3f6008fSTakashi Iwai 		if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
573b3f6008fSTakashi Iwai 		    info->pcm_type != HDA_PCM_TYPE_AUDIO)
574b3f6008fSTakashi Iwai 			continue;
575b3f6008fSTakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats =
576b3f6008fSTakashi Iwai 			SNDRV_PCM_FMTBIT_S16_LE;
577337b9d02STakashi Iwai 	}
578b3f6008fSTakashi Iwai 
5791c55d521STakashi Iwai 	return 0;
580337b9d02STakashi Iwai }
581337b9d02STakashi Iwai 
582c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
583c577b8a1SJoseph Chan {
584c577b8a1SJoseph Chan 	struct via_spec *spec;
585c577b8a1SJoseph Chan 	int err;
586c577b8a1SJoseph Chan 
587c577b8a1SJoseph Chan 	/* create a codec specific record */
5885b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
589c577b8a1SJoseph Chan 	if (spec == NULL)
590c577b8a1SJoseph Chan 		return -ENOMEM;
591c577b8a1SJoseph Chan 
592b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x17;
593b3f6008fSTakashi Iwai 
594b3f6008fSTakashi Iwai 	/* set jackpoll_interval while parsing the codec */
595b3f6008fSTakashi Iwai 	codec->jackpoll_interval = msecs_to_jiffies(100);
596b3f6008fSTakashi Iwai 	spec->vt1708_jack_detect = 1;
597b3f6008fSTakashi Iwai 
598b3f6008fSTakashi Iwai 	/* don't support the input jack switching due to lack of unsol event */
599b3f6008fSTakashi Iwai 	/* (it may work with polling, though, but it needs testing) */
600b3f6008fSTakashi Iwai 	spec->gen.suppress_auto_mic = 1;
601eb33ccf7STakashi Iwai 	/* Some machines show the broken speaker mute */
602eb33ccf7STakashi Iwai 	spec->gen.auto_mute_via_amp = 1;
603620e2b28STakashi Iwai 
60412daef65STakashi Iwai 	/* Add HP and CD pin config connect bit re-config action */
60512daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
60612daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
60712daef65STakashi Iwai 
608c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
60912daef65STakashi Iwai 	err = via_parse_auto_config(codec);
610c577b8a1SJoseph Chan 	if (err < 0) {
611c577b8a1SJoseph Chan 		via_free(codec);
612c577b8a1SJoseph Chan 		return err;
613c577b8a1SJoseph Chan 	}
614c577b8a1SJoseph Chan 
61512daef65STakashi Iwai 	/* add jack detect on/off control */
616b3f6008fSTakashi Iwai 	spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl;
617c577b8a1SJoseph Chan 
618e322a36dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
619e322a36dSLydia Wang 
620c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
621f672f65aSDavid Henningsson 	codec->patch_ops.build_controls = vt1708_build_controls;
622b3f6008fSTakashi Iwai 	codec->patch_ops.build_pcms = vt1708_build_pcms;
623c577b8a1SJoseph Chan 
624b3f6008fSTakashi Iwai 	/* clear jackpoll_interval again; it's set dynamically */
625b3f6008fSTakashi Iwai 	codec->jackpoll_interval = 0;
626b3f6008fSTakashi Iwai 
627c577b8a1SJoseph Chan 	return 0;
628c577b8a1SJoseph Chan }
629c577b8a1SJoseph Chan 
630ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec)
631c577b8a1SJoseph Chan {
632c577b8a1SJoseph Chan 	struct via_spec *spec;
633c577b8a1SJoseph Chan 	int err;
634c577b8a1SJoseph Chan 
635c577b8a1SJoseph Chan 	/* create a codec specific record */
6365b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
637c577b8a1SJoseph Chan 	if (spec == NULL)
638c577b8a1SJoseph Chan 		return -ENOMEM;
639c577b8a1SJoseph Chan 
640b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x18;
641620e2b28STakashi Iwai 
64212daef65STakashi Iwai 	err = via_parse_auto_config(codec);
643c577b8a1SJoseph Chan 	if (err < 0) {
644c577b8a1SJoseph Chan 		via_free(codec);
645c577b8a1SJoseph Chan 		return err;
646c577b8a1SJoseph Chan 	}
647c577b8a1SJoseph Chan 
648c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
649c577b8a1SJoseph Chan 
650f7278fd0SJosepch Chan 	return 0;
651f7278fd0SJosepch Chan }
652f7278fd0SJosepch Chan 
653518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
654ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec)
655f7278fd0SJosepch Chan {
656f7278fd0SJosepch Chan 	struct via_spec *spec;
657f7278fd0SJosepch Chan 	int err;
658f7278fd0SJosepch Chan 
659518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
660518bf3baSLydia Wang 		return patch_vt1708S(codec);
661ddd304d8STakashi Iwai 
662f7278fd0SJosepch Chan 	/* create a codec specific record */
6635b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
664f7278fd0SJosepch Chan 	if (spec == NULL)
665f7278fd0SJosepch Chan 		return -ENOMEM;
666f7278fd0SJosepch Chan 
667b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
668620e2b28STakashi Iwai 
669f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
67012daef65STakashi Iwai 	err = via_parse_auto_config(codec);
671f7278fd0SJosepch Chan 	if (err < 0) {
672f7278fd0SJosepch Chan 		via_free(codec);
673f7278fd0SJosepch Chan 		return err;
674f7278fd0SJosepch Chan 	}
675f7278fd0SJosepch Chan 
676f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
677f7278fd0SJosepch Chan 	return 0;
678f7278fd0SJosepch Chan }
679f7278fd0SJosepch Chan 
680d949cac1SHarald Welte /* Patch for VT1708S */
681096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = {
682d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
683d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
684bc7e7e5cSLydia Wang 	/* don't bybass mixer */
685bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
686d949cac1SHarald Welte 	{ }
687d949cac1SHarald Welte };
688d949cac1SHarald Welte 
6896369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
6906369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
6916369bcfcSLydia Wang {
692d045c5dcSTakashi Iwai 	snd_hda_override_wcaps(codec, pin,
693d045c5dcSTakashi Iwai 			       get_wcaps(codec, pin) | AC_WCAP_IN_AMP);
6946369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
6956369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
6966369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
6976369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
6986369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
6996369bcfcSLydia Wang }
7006369bcfcSLydia Wang 
701d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
702d949cac1SHarald Welte {
703d949cac1SHarald Welte 	struct via_spec *spec;
704d949cac1SHarald Welte 	int err;
705d949cac1SHarald Welte 
706d949cac1SHarald Welte 	/* create a codec specific record */
7075b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
708d949cac1SHarald Welte 	if (spec == NULL)
709d949cac1SHarald Welte 		return -ENOMEM;
710d949cac1SHarald Welte 
711b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
712d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
713d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
714620e2b28STakashi Iwai 
715518bf3baSLydia Wang 	/* correct names for VT1708BCE */
716518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
717518bf3baSLydia Wang 		kfree(codec->chip_name);
718518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
7196efdd851STakashi Iwai 		snprintf(codec->card->mixername,
7206efdd851STakashi Iwai 			 sizeof(codec->card->mixername),
721518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
722970f630fSLydia Wang 	}
723bc92df7fSLydia Wang 	/* correct names for VT1705 */
724bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)	{
725bc92df7fSLydia Wang 		kfree(codec->chip_name);
726bc92df7fSLydia Wang 		codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
7276efdd851STakashi Iwai 		snprintf(codec->card->mixername,
7286efdd851STakashi Iwai 			 sizeof(codec->card->mixername),
729bc92df7fSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
730bc92df7fSLydia Wang 	}
731b3f6008fSTakashi Iwai 
732b3f6008fSTakashi Iwai 	/* automatic parse from the BIOS config */
733b3f6008fSTakashi Iwai 	err = via_parse_auto_config(codec);
734b3f6008fSTakashi Iwai 	if (err < 0) {
735b3f6008fSTakashi Iwai 		via_free(codec);
736b3f6008fSTakashi Iwai 		return err;
737b3f6008fSTakashi Iwai 	}
738b3f6008fSTakashi Iwai 
739b3f6008fSTakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
740b3f6008fSTakashi Iwai 
741b3f6008fSTakashi Iwai 	codec->patch_ops = via_patch_ops;
742d949cac1SHarald Welte 	return 0;
743d949cac1SHarald Welte }
744d949cac1SHarald Welte 
745d949cac1SHarald Welte /* Patch for VT1702 */
746d949cac1SHarald Welte 
747096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = {
748bc7e7e5cSLydia Wang 	/* mixer enable */
749bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
750bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
751bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
752d949cac1SHarald Welte 	{ }
753d949cac1SHarald Welte };
754d949cac1SHarald Welte 
755d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
756d949cac1SHarald Welte {
757d949cac1SHarald Welte 	struct via_spec *spec;
758d949cac1SHarald Welte 	int err;
759d949cac1SHarald Welte 
760d949cac1SHarald Welte 	/* create a codec specific record */
7615b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
762d949cac1SHarald Welte 	if (spec == NULL)
763d949cac1SHarald Welte 		return -ENOMEM;
764d949cac1SHarald Welte 
765b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x1a;
766620e2b28STakashi Iwai 
76712daef65STakashi Iwai 	/* limit AA path volume to 0 dB */
76812daef65STakashi Iwai 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
76912daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
77012daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
77112daef65STakashi Iwai 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
77212daef65STakashi Iwai 				  (1 << AC_AMPCAP_MUTE_SHIFT));
77312daef65STakashi Iwai 
774d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
77512daef65STakashi Iwai 	err = via_parse_auto_config(codec);
776d949cac1SHarald Welte 	if (err < 0) {
777d949cac1SHarald Welte 		via_free(codec);
778d949cac1SHarald Welte 		return err;
779d949cac1SHarald Welte 	}
780d949cac1SHarald Welte 
781096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
782d949cac1SHarald Welte 
783d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
784d949cac1SHarald Welte 	return 0;
785d949cac1SHarald Welte }
786d949cac1SHarald Welte 
787eb7188caSLydia Wang /* Patch for VT1718S */
788eb7188caSLydia Wang 
789096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = {
7904ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
7914ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
792eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
793eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
7945d41762aSTakashi Iwai 
795eb7188caSLydia Wang 	{ }
796eb7188caSLydia Wang };
797eb7188caSLydia Wang 
79830b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs
79930b45033STakashi Iwai  * This isn't listed from the raw info, but the chip has a secret connection.
80030b45033STakashi Iwai  */
80130b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec)
80230b45033STakashi Iwai {
80330b45033STakashi Iwai 	struct via_spec *spec = codec->spec;
80430b45033STakashi Iwai 	int i, nums;
80530b45033STakashi Iwai 	hda_nid_t conn[8];
80630b45033STakashi Iwai 	hda_nid_t nid;
80730b45033STakashi Iwai 
808b3f6008fSTakashi Iwai 	if (!spec->gen.mixer_nid)
80930b45033STakashi Iwai 		return 0;
810b3f6008fSTakashi Iwai 	nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
81130b45033STakashi Iwai 				       ARRAY_SIZE(conn) - 1);
81230b45033STakashi Iwai 	for (i = 0; i < nums; i++) {
81330b45033STakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
81430b45033STakashi Iwai 			return 0;
81530b45033STakashi Iwai 	}
81630b45033STakashi Iwai 
81730b45033STakashi Iwai 	/* find the primary DAC and add to the connection list */
81830b45033STakashi Iwai 	nid = codec->start_nid;
81930b45033STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, nid++) {
82030b45033STakashi Iwai 		unsigned int caps = get_wcaps(codec, nid);
82130b45033STakashi Iwai 		if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
82230b45033STakashi Iwai 		    !(caps & AC_WCAP_DIGITAL)) {
82330b45033STakashi Iwai 			conn[nums++] = nid;
82430b45033STakashi Iwai 			return snd_hda_override_conn_list(codec,
825b3f6008fSTakashi Iwai 							  spec->gen.mixer_nid,
82630b45033STakashi Iwai 							  nums, conn);
82730b45033STakashi Iwai 		}
82830b45033STakashi Iwai 	}
82930b45033STakashi Iwai 	return 0;
83030b45033STakashi Iwai }
83130b45033STakashi Iwai 
83230b45033STakashi Iwai 
833eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
834eb7188caSLydia Wang {
835eb7188caSLydia Wang 	struct via_spec *spec;
836eb7188caSLydia Wang 	int err;
837eb7188caSLydia Wang 
838eb7188caSLydia Wang 	/* create a codec specific record */
8395b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
840eb7188caSLydia Wang 	if (spec == NULL)
841eb7188caSLydia Wang 		return -ENOMEM;
842eb7188caSLydia Wang 
843b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
844d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
845d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
84630b45033STakashi Iwai 	add_secret_dac_path(codec);
847620e2b28STakashi Iwai 
848eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
84912daef65STakashi Iwai 	err = via_parse_auto_config(codec);
850eb7188caSLydia Wang 	if (err < 0) {
851eb7188caSLydia Wang 		via_free(codec);
852eb7188caSLydia Wang 		return err;
853eb7188caSLydia Wang 	}
854eb7188caSLydia Wang 
855096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
856eb7188caSLydia Wang 
857eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
858eb7188caSLydia Wang 	return 0;
859eb7188caSLydia Wang }
860f3db423dSLydia Wang 
861f3db423dSLydia Wang /* Patch for VT1716S */
862f3db423dSLydia Wang 
863f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
864f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
865f3db423dSLydia Wang {
866f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
867f3db423dSLydia Wang 	uinfo->count = 1;
868f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
869f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
870f3db423dSLydia Wang 	return 0;
871f3db423dSLydia Wang }
872f3db423dSLydia Wang 
873f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
874f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
875f3db423dSLydia Wang {
876f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
877f3db423dSLydia Wang 	int index = 0;
878f3db423dSLydia Wang 
879f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
880f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
881f3db423dSLydia Wang 	if (index != -1)
882f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
883f3db423dSLydia Wang 
884f3db423dSLydia Wang 	return 0;
885f3db423dSLydia Wang }
886f3db423dSLydia Wang 
887f3db423dSLydia Wang static int vt1716s_dmic_put(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 	struct via_spec *spec = codec->spec;
892f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
893f3db423dSLydia Wang 
894f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
895f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
896f3db423dSLydia Wang 	spec->dmic_enabled = index;
897f3db423dSLydia Wang 	return 1;
898f3db423dSLydia Wang }
899f3db423dSLydia Wang 
90090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
901f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
902f3db423dSLydia Wang 	{
903f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
904f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
9055b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
906f3db423dSLydia Wang 	 .count = 1,
907f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
908f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
909f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
910f3db423dSLydia Wang 	 },
911f3db423dSLydia Wang 	{}			/* end */
912f3db423dSLydia Wang };
913f3db423dSLydia Wang 
914f3db423dSLydia Wang 
915f3db423dSLydia Wang /* mono-out mixer elements */
91690dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
917f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
918f3db423dSLydia Wang 	{ } /* end */
919f3db423dSLydia Wang };
920f3db423dSLydia Wang 
921096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = {
922f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
923f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
924f3db423dSLydia Wang 	/* don't bybass mixer */
925f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
926f3db423dSLydia Wang 	/* Enable mono output */
927f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
928f3db423dSLydia Wang 	{ }
929f3db423dSLydia Wang };
930f3db423dSLydia Wang 
931f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
932f3db423dSLydia Wang {
933f3db423dSLydia Wang 	struct via_spec *spec;
934f3db423dSLydia Wang 	int err;
935f3db423dSLydia Wang 
936f3db423dSLydia Wang 	/* create a codec specific record */
9375b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
938f3db423dSLydia Wang 	if (spec == NULL)
939f3db423dSLydia Wang 		return -ENOMEM;
940f3db423dSLydia Wang 
941b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
942d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
943d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
944620e2b28STakashi Iwai 
945f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
94612daef65STakashi Iwai 	err = via_parse_auto_config(codec);
947f3db423dSLydia Wang 	if (err < 0) {
948f3db423dSLydia Wang 		via_free(codec);
949f3db423dSLydia Wang 		return err;
950f3db423dSLydia Wang 	}
951f3db423dSLydia Wang 
952096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_init_verbs;
953f3db423dSLydia Wang 
954b3f6008fSTakashi Iwai 	spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer;
955f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
956f3db423dSLydia Wang 
957f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
958f3db423dSLydia Wang 	return 0;
959f3db423dSLydia Wang }
96025eaba2fSLydia Wang 
96125eaba2fSLydia Wang /* for vt2002P */
96225eaba2fSLydia Wang 
963096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = {
964eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
965eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
966eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
967eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
96825eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
96925eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
97025eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
97125eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
97225eaba2fSLydia Wang 	{ }
97325eaba2fSLydia Wang };
9744a918ffeSTakashi Iwai 
975096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = {
97611890956SLydia Wang 	/* Enable Boost Volume backdoor */
97711890956SLydia Wang 	{0x1, 0xfb9, 0x24},
97811890956SLydia Wang 	/* Enable AOW0 to MW9 */
97911890956SLydia Wang 	{0x1, 0xfb8, 0x88},
98011890956SLydia Wang 	{ }
98111890956SLydia Wang };
98225eaba2fSLydia Wang 
9834b527b65SDavid Henningsson /*
9844b527b65SDavid Henningsson  * pin fix-up
9854b527b65SDavid Henningsson  */
9864b527b65SDavid Henningsson enum {
9874b527b65SDavid Henningsson 	VIA_FIXUP_INTMIC_BOOST,
988d5266125STakashi Iwai 	VIA_FIXUP_ASUS_G75,
9894b527b65SDavid Henningsson };
9904b527b65SDavid Henningsson 
9914b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec,
9924b527b65SDavid Henningsson 				  const struct hda_fixup *fix, int action)
9934b527b65SDavid Henningsson {
9944b527b65SDavid Henningsson 	if (action == HDA_FIXUP_ACT_PRE_PROBE)
9954b527b65SDavid Henningsson 		override_mic_boost(codec, 0x30, 0, 2, 40);
9964b527b65SDavid Henningsson }
9974b527b65SDavid Henningsson 
9984b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = {
9994b527b65SDavid Henningsson 	[VIA_FIXUP_INTMIC_BOOST] = {
10004b527b65SDavid Henningsson 		.type = HDA_FIXUP_FUNC,
10014b527b65SDavid Henningsson 		.v.func = via_fixup_intmic_boost,
10024b527b65SDavid Henningsson 	},
1003d5266125STakashi Iwai 	[VIA_FIXUP_ASUS_G75] = {
1004d5266125STakashi Iwai 		.type = HDA_FIXUP_PINS,
1005d5266125STakashi Iwai 		.v.pins = (const struct hda_pintbl[]) {
1006d5266125STakashi Iwai 			/* set 0x24 and 0x33 as speakers */
1007d5266125STakashi Iwai 			{ 0x24, 0x991301f0 },
1008d5266125STakashi Iwai 			{ 0x33, 0x991301f1 }, /* subwoofer */
1009d5266125STakashi Iwai 			{ }
1010d5266125STakashi Iwai 		}
1011d5266125STakashi Iwai 	},
10124b527b65SDavid Henningsson };
10134b527b65SDavid Henningsson 
10144b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = {
1015d5266125STakashi Iwai 	SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
10164b527b65SDavid Henningsson 	SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
10174b527b65SDavid Henningsson 	{}
10184b527b65SDavid Henningsson };
10194b527b65SDavid Henningsson 
1020ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
1021ef4da458STakashi Iwai  * Replace this with mixer NID 0x1c
1022ef4da458STakashi Iwai  */
1023ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec)
1024ef4da458STakashi Iwai {
1025ef4da458STakashi Iwai 	static hda_nid_t conn_24[] = { 0x14, 0x1c };
1026ef4da458STakashi Iwai 	static hda_nid_t conn_33[] = { 0x1c };
1027ef4da458STakashi Iwai 
1028ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
1029ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
1030ef4da458STakashi Iwai }
1031ef4da458STakashi Iwai 
103225eaba2fSLydia Wang /* patch for vt2002P */
103325eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
103425eaba2fSLydia Wang {
103525eaba2fSLydia Wang 	struct via_spec *spec;
103625eaba2fSLydia Wang 	int err;
103725eaba2fSLydia Wang 
103825eaba2fSLydia Wang 	/* create a codec specific record */
10395b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
104025eaba2fSLydia Wang 	if (spec == NULL)
104125eaba2fSLydia Wang 		return -ENOMEM;
104225eaba2fSLydia Wang 
1043b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1044d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1045d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
1046ef4da458STakashi Iwai 	if (spec->codec_type == VT1802)
1047ef4da458STakashi Iwai 		fix_vt1802_connections(codec);
104830b45033STakashi Iwai 	add_secret_dac_path(codec);
1049620e2b28STakashi Iwai 
10504b527b65SDavid Henningsson 	snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
10514b527b65SDavid Henningsson 	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
10524b527b65SDavid Henningsson 
105325eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
105412daef65STakashi Iwai 	err = via_parse_auto_config(codec);
105525eaba2fSLydia Wang 	if (err < 0) {
105625eaba2fSLydia Wang 		via_free(codec);
105725eaba2fSLydia Wang 		return err;
105825eaba2fSLydia Wang 	}
105925eaba2fSLydia Wang 
106011890956SLydia Wang 	if (spec->codec_type == VT1802)
10614a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
106211890956SLydia Wang 	else
10634a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
106411890956SLydia Wang 
106525eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
106625eaba2fSLydia Wang 	return 0;
106725eaba2fSLydia Wang }
1068ab6734e7SLydia Wang 
1069ab6734e7SLydia Wang /* for vt1812 */
1070ab6734e7SLydia Wang 
1071096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = {
1072ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
1073ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
1074ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
1075ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
1076ab6734e7SLydia Wang 	{ }
1077ab6734e7SLydia Wang };
1078ab6734e7SLydia Wang 
1079ab6734e7SLydia Wang /* patch for vt1812 */
1080ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
1081ab6734e7SLydia Wang {
1082ab6734e7SLydia Wang 	struct via_spec *spec;
1083ab6734e7SLydia Wang 	int err;
1084ab6734e7SLydia Wang 
1085ab6734e7SLydia Wang 	/* create a codec specific record */
10865b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1087ab6734e7SLydia Wang 	if (spec == NULL)
1088ab6734e7SLydia Wang 		return -ENOMEM;
1089ab6734e7SLydia Wang 
1090b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1091d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1092d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
109330b45033STakashi Iwai 	add_secret_dac_path(codec);
1094620e2b28STakashi Iwai 
1095ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
109612daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1097ab6734e7SLydia Wang 	if (err < 0) {
1098ab6734e7SLydia Wang 		via_free(codec);
1099ab6734e7SLydia Wang 		return err;
1100ab6734e7SLydia Wang 	}
1101ab6734e7SLydia Wang 
1102096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1812_init_verbs;
1103ab6734e7SLydia Wang 
1104ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
1105ab6734e7SLydia Wang 	return 0;
1106ab6734e7SLydia Wang }
1107ab6734e7SLydia Wang 
110843737e0aSLydia Wang /* patch for vt3476 */
110943737e0aSLydia Wang 
111043737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = {
111143737e0aSLydia Wang 	/* Enable DMic 8/16/32K */
111243737e0aSLydia Wang 	{0x1, 0xF7B, 0x30},
111343737e0aSLydia Wang 	/* Enable Boost Volume backdoor */
111443737e0aSLydia Wang 	{0x1, 0xFB9, 0x20},
111543737e0aSLydia Wang 	/* Enable AOW-MW9 path */
111643737e0aSLydia Wang 	{0x1, 0xFB8, 0x10},
111743737e0aSLydia Wang 	{ }
111843737e0aSLydia Wang };
111943737e0aSLydia Wang 
112043737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec)
112143737e0aSLydia Wang {
112243737e0aSLydia Wang 	struct via_spec *spec;
112343737e0aSLydia Wang 	int err;
112443737e0aSLydia Wang 
112543737e0aSLydia Wang 	/* create a codec specific record */
112643737e0aSLydia Wang 	spec = via_new_spec(codec);
112743737e0aSLydia Wang 	if (spec == NULL)
112843737e0aSLydia Wang 		return -ENOMEM;
112943737e0aSLydia Wang 
1130b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x3f;
113143737e0aSLydia Wang 	add_secret_dac_path(codec);
113243737e0aSLydia Wang 
113343737e0aSLydia Wang 	/* automatic parse from the BIOS config */
113443737e0aSLydia Wang 	err = via_parse_auto_config(codec);
113543737e0aSLydia Wang 	if (err < 0) {
113643737e0aSLydia Wang 		via_free(codec);
113743737e0aSLydia Wang 		return err;
113843737e0aSLydia Wang 	}
113943737e0aSLydia Wang 
114043737e0aSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs;
114143737e0aSLydia Wang 
114243737e0aSLydia Wang 	codec->patch_ops = via_patch_ops;
114343737e0aSLydia Wang 	return 0;
114443737e0aSLydia Wang }
114543737e0aSLydia Wang 
1146c577b8a1SJoseph Chan /*
1147c577b8a1SJoseph Chan  * patch entries
1148c577b8a1SJoseph Chan  */
114990dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = {
11503218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
11513218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
11523218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
11533218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
11543218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
1155ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
11563218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
1157ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
11583218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
1159ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
11603218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
1161ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
11623218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
1163ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
11643218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
1165ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
11663218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
1167ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
11683218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
1169ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
11703218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
1171ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
11723218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
1173ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
11743218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
1175ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
11763218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
1177ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
11783218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
1179ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
11803218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
1181ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
11823218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
1183ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
11843218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
1185ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
11863218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
1187d949cac1SHarald Welte 	  .patch = patch_vt1708S},
11883218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
1189d949cac1SHarald Welte 	  .patch = patch_vt1708S},
11903218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
1191d949cac1SHarald Welte 	  .patch = patch_vt1708S},
11923218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
1193d949cac1SHarald Welte 	  .patch = patch_vt1708S},
1194bc92df7fSLydia Wang 	{ .id = 0x11064397, .name = "VT1705",
1195d949cac1SHarald Welte 	  .patch = patch_vt1708S},
11963218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
1197d949cac1SHarald Welte 	  .patch = patch_vt1708S},
11983218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
1199d949cac1SHarald Welte 	  .patch = patch_vt1708S},
12003218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
1201d949cac1SHarald Welte 	  .patch = patch_vt1708S},
12023218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
1203d949cac1SHarald Welte 	  .patch = patch_vt1702},
12043218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
1205d949cac1SHarald Welte 	  .patch = patch_vt1702},
12063218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
1207d949cac1SHarald Welte 	  .patch = patch_vt1702},
12083218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
1209d949cac1SHarald Welte 	  .patch = patch_vt1702},
12103218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
1211d949cac1SHarald Welte 	  .patch = patch_vt1702},
12123218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
1213d949cac1SHarald Welte 	  .patch = patch_vt1702},
12143218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
1215d949cac1SHarald Welte 	  .patch = patch_vt1702},
12163218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
1217d949cac1SHarald Welte 	  .patch = patch_vt1702},
1218eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
1219eb7188caSLydia Wang 	  .patch = patch_vt1718S},
1220eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
1221eb7188caSLydia Wang 	  .patch = patch_vt1718S},
1222bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
1223bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
1224bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
1225bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
1226f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
1227f3db423dSLydia Wang 	  .patch = patch_vt1716S},
1228f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
1229f3db423dSLydia Wang 	  .patch = patch_vt1716S},
123025eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
123125eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
1232ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
123336dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
123436dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
123511890956SLydia Wang 	{ .id = 0x11060446, .name = "VT1802",
123611890956SLydia Wang 		.patch = patch_vt2002P},
123711890956SLydia Wang 	{ .id = 0x11068446, .name = "VT1802",
123811890956SLydia Wang 		.patch = patch_vt2002P},
123943737e0aSLydia Wang 	{ .id = 0x11064760, .name = "VT1705CF",
124043737e0aSLydia Wang 		.patch = patch_vt3476},
12416121b84aSLydia Wang 	{ .id = 0x11064761, .name = "VT1708SCE",
12426121b84aSLydia Wang 		.patch = patch_vt3476},
12436121b84aSLydia Wang 	{ .id = 0x11064762, .name = "VT1808",
12446121b84aSLydia Wang 		.patch = patch_vt3476},
1245c577b8a1SJoseph Chan 	{} /* terminator */
1246c577b8a1SJoseph Chan };
12471289e9e8STakashi Iwai 
12481289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
12491289e9e8STakashi Iwai 
1250d8a766a1STakashi Iwai static struct hda_codec_driver via_driver = {
12511289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
12521289e9e8STakashi Iwai };
12531289e9e8STakashi Iwai 
12541289e9e8STakashi Iwai MODULE_LICENSE("GPL");
12551289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
12561289e9e8STakashi Iwai 
1257d8a766a1STakashi Iwai module_hda_codec_driver(via_driver);
1258