xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision be57bfffb7b5ba72e1293643053f8861fcaa5163)
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>
55*be57bfffSPierre-Louis Bossart #include <sound/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 	/* HP mode source */
94f3db423dSLydia Wang 	unsigned int dmic_enabled;
951f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
961f2e99feSLydia Wang 
97e9d010c2STakashi Iwai 	/* analog low-power control */
98e9d010c2STakashi Iwai 	bool alc_mode;
99e9d010c2STakashi Iwai 
1001f2e99feSLydia Wang 	/* work to check hp jack state */
101187d333eSTakashi Iwai 	int hp_work_active;
102e06e5a29STakashi Iwai 	int vt1708_jack_detect;
1031f2e99feSLydia Wang };
1041f2e99feSLydia Wang 
1050341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
106b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
107b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
108b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
109b3f6008fSTakashi Iwai 				  int action);
110b3f6008fSTakashi Iwai 
111225068abSTakashi Iwai static const struct hda_codec_ops via_patch_ops; /* defined below */
112225068abSTakashi Iwai 
1135b0cb1d8SJaroslav Kysela static struct via_spec *via_new_spec(struct hda_codec *codec)
1145b0cb1d8SJaroslav Kysela {
1155b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1165b0cb1d8SJaroslav Kysela 
1175b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1185b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1195b0cb1d8SJaroslav Kysela 		return NULL;
1205b0cb1d8SJaroslav Kysela 
1215b0cb1d8SJaroslav Kysela 	codec->spec = spec;
122b3f6008fSTakashi Iwai 	snd_hda_gen_spec_init(&spec->gen);
1230341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
1240341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
1250341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
1260341ccd7SLydia Wang 		spec->codec_type = VT1708S;
12713961170STakashi Iwai 	spec->gen.indep_hp = 1;
12805909d5cSTakashi Iwai 	spec->gen.keep_eapd_on = 1;
129b3f6008fSTakashi Iwai 	spec->gen.pcm_playback_hook = via_playback_pcm_hook;
13074f14b36STakashi Iwai 	spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
131967b1307STakashi Iwai 	codec->power_save_node = 1;
132688b12ccSTakashi Iwai 	spec->gen.power_down_unused = 1;
133225068abSTakashi Iwai 	codec->patch_ops = via_patch_ops;
1345b0cb1d8SJaroslav Kysela 	return spec;
1355b0cb1d8SJaroslav Kysela }
1365b0cb1d8SJaroslav Kysela 
137744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
138d7426329SHarald Welte {
1397639a06cSTakashi Iwai 	u32 vendor_id = codec->core.vendor_id;
140d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
141d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
142d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
143d7426329SHarald Welte 
144d7426329SHarald Welte 	/* get codec type */
145d7426329SHarald Welte 	if (ven_id != 0x1106)
146d7426329SHarald Welte 		codec_type = UNKNOWN;
147d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
148d7426329SHarald Welte 		codec_type = VT1708;
149d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
150d7426329SHarald Welte 		codec_type = VT1709_10CH;
151d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
152d7426329SHarald Welte 		codec_type = VT1709_6CH;
153518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
154d7426329SHarald Welte 		codec_type = VT1708B_8CH;
155518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
156518bf3baSLydia Wang 			codec_type = VT1708BCE;
157518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
158d7426329SHarald Welte 		codec_type = VT1708B_4CH;
159d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
160d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
161d7426329SHarald Welte 		codec_type = VT1708S;
162d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
163d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
164d7426329SHarald Welte 		codec_type = VT1702;
165eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
166eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
167eb7188caSLydia Wang 		codec_type = VT1718S;
168f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
169f3db423dSLydia Wang 		codec_type = VT1716S;
170bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
171bb3c6bfcSLydia Wang 		codec_type = VT1718S;
17225eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
17325eaba2fSLydia Wang 		codec_type = VT2002P;
174ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
175ab6734e7SLydia Wang 		codec_type = VT1812;
17636dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
17736dd5c4aSLydia Wang 		codec_type = VT1708S;
17811890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
17911890956SLydia Wang 		codec_type = VT1802;
18043737e0aSLydia Wang 	else if (dev_id == 0x4760)
18143737e0aSLydia Wang 		codec_type = VT1705CF;
1826121b84aSLydia Wang 	else if (dev_id == 0x4761 || dev_id == 0x4762)
1836121b84aSLydia Wang 		codec_type = VT1808;
184d7426329SHarald Welte 	else
185d7426329SHarald Welte 		codec_type = UNKNOWN;
186d7426329SHarald Welte 	return codec_type;
187d7426329SHarald Welte };
188d7426329SHarald Welte 
189ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec);
190ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec);
1911f2e99feSLydia Wang 
192187d333eSTakashi Iwai #define hp_detect_with_aa(codec) \
193187d333eSTakashi Iwai 	(snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
194187d333eSTakashi Iwai 	 !is_aa_path_mute(codec))
1951f2e99feSLydia Wang 
196b3f6008fSTakashi Iwai static void vt1708_stop_hp_work(struct hda_codec *codec)
1971f2e99feSLydia Wang {
198b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
199b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
2001f2e99feSLydia Wang 		return;
201187d333eSTakashi Iwai 	if (spec->hp_work_active) {
202b3f6008fSTakashi Iwai 		snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
2037eaa9161SWang Xingchao 		codec->jackpoll_interval = 0;
204b3f6008fSTakashi Iwai 		cancel_delayed_work_sync(&codec->jackpoll_work);
205b3f6008fSTakashi Iwai 		spec->hp_work_active = false;
206187d333eSTakashi Iwai 	}
207187d333eSTakashi Iwai }
208187d333eSTakashi Iwai 
209b3f6008fSTakashi Iwai static void vt1708_update_hp_work(struct hda_codec *codec)
210187d333eSTakashi Iwai {
211b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
212b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
213187d333eSTakashi Iwai 		return;
21405dc0fc9SDavid Henningsson 	if (spec->vt1708_jack_detect) {
215187d333eSTakashi Iwai 		if (!spec->hp_work_active) {
216b3f6008fSTakashi Iwai 			codec->jackpoll_interval = msecs_to_jiffies(100);
217b3f6008fSTakashi Iwai 			snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0);
2182f35c630STakashi Iwai 			schedule_delayed_work(&codec->jackpoll_work, 0);
219b3f6008fSTakashi Iwai 			spec->hp_work_active = true;
220187d333eSTakashi Iwai 		}
221b3f6008fSTakashi Iwai 	} else if (!hp_detect_with_aa(codec))
222b3f6008fSTakashi Iwai 		vt1708_stop_hp_work(codec);
2231f2e99feSLydia Wang }
224f5271101SLydia Wang 
22524088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
22624088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
22724088a58STakashi Iwai {
228dda415d4STakashi Iwai 	return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
22924088a58STakashi Iwai }
23024088a58STakashi Iwai 
23124088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
23224088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
23324088a58STakashi Iwai {
23424088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
235735c75cfSTakashi Iwai 	struct via_spec *spec = codec->spec;
236735c75cfSTakashi Iwai 
237735c75cfSTakashi Iwai 	ucontrol->value.enumerated.item[0] = spec->gen.power_down_unused;
23824088a58STakashi Iwai 	return 0;
23924088a58STakashi Iwai }
24024088a58STakashi Iwai 
24124088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
24224088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
24324088a58STakashi Iwai {
24424088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
24524088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
246688b12ccSTakashi Iwai 	bool val = !!ucontrol->value.enumerated.item[0];
24724088a58STakashi Iwai 
248735c75cfSTakashi Iwai 	if (val == spec->gen.power_down_unused)
24924088a58STakashi Iwai 		return 0;
250735c75cfSTakashi Iwai 	/* codec->power_save_node = val; */ /* widget PM seems yet broken */
251688b12ccSTakashi Iwai 	spec->gen.power_down_unused = val;
252e9d010c2STakashi Iwai 	analog_low_current_mode(codec);
25324088a58STakashi Iwai 	return 1;
25424088a58STakashi Iwai }
25524088a58STakashi Iwai 
2560e8f9862STakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
25724088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
25824088a58STakashi Iwai 	.name = "Dynamic Power-Control",
25924088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
26024088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
26124088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
26224088a58STakashi Iwai };
26324088a58STakashi Iwai 
2644738465cSW. Trevor King #ifdef CONFIG_SND_HDA_INPUT_BEEP
2654738465cSW. Trevor King /* additional beep mixers; the actual parameters are overwritten at build */
2660e8f9862STakashi Iwai static const struct snd_kcontrol_new via_beep_mixer[] = {
2674738465cSW. Trevor King 	HDA_CODEC_VOLUME_MONO("Beep Playback Volume", 0, 1, 0, HDA_OUTPUT),
2684738465cSW. Trevor King 	HDA_CODEC_MUTE_BEEP_MONO("Beep Playback Switch", 0, 1, 0, HDA_OUTPUT),
2694738465cSW. Trevor King };
2704738465cSW. Trevor King 
2710e8f9862STakashi Iwai static int set_beep_amp(struct via_spec *spec, hda_nid_t nid,
2720e8f9862STakashi Iwai 			int idx, int dir)
2734738465cSW. Trevor King {
2740e8f9862STakashi Iwai 	struct snd_kcontrol_new *knew;
2750e8f9862STakashi Iwai 	unsigned int beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir);
2760e8f9862STakashi Iwai 	int i;
2774738465cSW. Trevor King 
2780e8f9862STakashi Iwai 	spec->gen.beep_nid = nid;
2790e8f9862STakashi Iwai 	for (i = 0; i < ARRAY_SIZE(via_beep_mixer); i++) {
2800e8f9862STakashi Iwai 		knew = snd_hda_gen_add_kctl(&spec->gen, NULL,
2810e8f9862STakashi Iwai 					    &via_beep_mixer[i]);
2820e8f9862STakashi Iwai 		if (!knew)
2834738465cSW. Trevor King 			return -ENOMEM;
2840e8f9862STakashi Iwai 		knew->private_value = beep_amp;
2854738465cSW. Trevor King 	}
2864738465cSW. Trevor King 	return 0;
2874738465cSW. Trevor King }
2884738465cSW. Trevor King 
2890e8f9862STakashi Iwai static int auto_parse_beep(struct hda_codec *codec)
2904738465cSW. Trevor King {
2914738465cSW. Trevor King 	struct via_spec *spec = codec->spec;
2924738465cSW. Trevor King 	hda_nid_t nid;
2934738465cSW. Trevor King 
2944738465cSW. Trevor King 	for_each_hda_codec_node(nid, codec)
2950e8f9862STakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, nid)) == AC_WID_BEEP)
2960e8f9862STakashi Iwai 			return set_beep_amp(spec, nid, 0, HDA_OUTPUT);
2970e8f9862STakashi Iwai 	return 0;
2984738465cSW. Trevor King }
2994738465cSW. Trevor King #else
3000e8f9862STakashi Iwai #define auto_parse_beep(codec)	0
3014738465cSW. Trevor King #endif
30224088a58STakashi Iwai 
303f5271101SLydia Wang /* check AA path's mute status */
304ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec)
305ada509ecSTakashi Iwai {
306ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
307ada509ecSTakashi Iwai 	const struct hda_amp_list *p;
3080186f4f4STakashi Iwai 	int ch, v;
309ada509ecSTakashi Iwai 
3100186f4f4STakashi Iwai 	p = spec->gen.loopback.amplist;
3110186f4f4STakashi Iwai 	if (!p)
3120186f4f4STakashi Iwai 		return true;
3130186f4f4STakashi Iwai 	for (; p->nid; p++) {
314ada509ecSTakashi Iwai 		for (ch = 0; ch < 2; ch++) {
315ada509ecSTakashi Iwai 			v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
316ada509ecSTakashi Iwai 						   p->idx);
317ada509ecSTakashi Iwai 			if (!(v & HDA_AMP_MUTE) && v > 0)
318ada509ecSTakashi Iwai 				return false;
319f5271101SLydia Wang 		}
320f5271101SLydia Wang 	}
321ada509ecSTakashi Iwai 	return true;
322f5271101SLydia Wang }
323f5271101SLydia Wang 
324f5271101SLydia Wang /* enter/exit analog low-current mode */
325e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force)
326f5271101SLydia Wang {
327f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
328ada509ecSTakashi Iwai 	bool enable;
329ada509ecSTakashi Iwai 	unsigned int verb, parm;
330f5271101SLydia Wang 
331967b1307STakashi Iwai 	if (!codec->power_save_node)
332e9d010c2STakashi Iwai 		enable = false;
333e9d010c2STakashi Iwai 	else
334b3f6008fSTakashi Iwai 		enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
335e9d010c2STakashi Iwai 	if (enable == spec->alc_mode && !force)
336e9d010c2STakashi Iwai 		return;
337e9d010c2STakashi Iwai 	spec->alc_mode = enable;
338f5271101SLydia Wang 
339f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
340f5271101SLydia Wang 	switch (spec->codec_type) {
341f5271101SLydia Wang 	case VT1708B_8CH:
342f5271101SLydia Wang 	case VT1708B_4CH:
343f5271101SLydia Wang 		verb = 0xf70;
344f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
345f5271101SLydia Wang 		break;
346f5271101SLydia Wang 	case VT1708S:
347eb7188caSLydia Wang 	case VT1718S:
348f3db423dSLydia Wang 	case VT1716S:
349f5271101SLydia Wang 		verb = 0xf73;
350f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
351f5271101SLydia Wang 		break;
352f5271101SLydia Wang 	case VT1702:
353f5271101SLydia Wang 		verb = 0xf73;
354f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
355f5271101SLydia Wang 		break;
35625eaba2fSLydia Wang 	case VT2002P:
357ab6734e7SLydia Wang 	case VT1812:
35811890956SLydia Wang 	case VT1802:
35925eaba2fSLydia Wang 		verb = 0xf93;
36025eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
36125eaba2fSLydia Wang 		break;
36243737e0aSLydia Wang 	case VT1705CF:
3636121b84aSLydia Wang 	case VT1808:
36443737e0aSLydia Wang 		verb = 0xf82;
36543737e0aSLydia Wang 		parm = enable ? 0x00 : 0xe0;  /* 0x00: 4/40x, 0xe0: 1x */
36643737e0aSLydia Wang 		break;
367f5271101SLydia Wang 	default:
368f5271101SLydia Wang 		return;		/* other codecs are not supported */
369f5271101SLydia Wang 	}
370f5271101SLydia Wang 	/* send verb */
3717639a06cSTakashi Iwai 	snd_hda_codec_write(codec, codec->core.afg, 0, verb, parm);
372f5271101SLydia Wang }
373f5271101SLydia Wang 
374e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec)
375e9d010c2STakashi Iwai {
376e9d010c2STakashi Iwai 	return __analog_low_current_mode(codec, false);
377e9d010c2STakashi Iwai }
378e9d010c2STakashi Iwai 
379b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
380b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
381b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
382b3f6008fSTakashi Iwai 				  int action)
383c577b8a1SJoseph Chan {
384b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
385b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
386c577b8a1SJoseph Chan }
387c577b8a1SJoseph Chan 
388c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
389c577b8a1SJoseph Chan {
390b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
391a8dca460STakashi Iwai 	snd_hda_gen_free(codec);
392c577b8a1SJoseph Chan }
393c577b8a1SJoseph Chan 
3942a43952aSTakashi Iwai #ifdef CONFIG_PM
39568cb2b55STakashi Iwai static int via_suspend(struct hda_codec *codec)
3961f2e99feSLydia Wang {
3971f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
398b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
39994c142a1SDavid Henningsson 
40094c142a1SDavid Henningsson 	/* Fix pop noise on headphones */
4012c38d990STakashi Iwai 	if (spec->codec_type == VT1802)
4022c38d990STakashi Iwai 		snd_hda_shutup_pins(codec);
40394c142a1SDavid Henningsson 
4041f2e99feSLydia Wang 	return 0;
4051f2e99feSLydia Wang }
4066b6d0007STakashi Iwai 
4076b6d0007STakashi Iwai static int via_resume(struct hda_codec *codec)
4086b6d0007STakashi Iwai {
4096b6d0007STakashi Iwai 	/* some delay here to make jack detection working (bko#98921) */
4106b6d0007STakashi Iwai 	msleep(10);
4116b6d0007STakashi Iwai 	codec->patch_ops.init(codec);
4126b6d0007STakashi Iwai 	regcache_sync(codec->core.regmap);
4136b6d0007STakashi Iwai 	return 0;
4146b6d0007STakashi Iwai }
4151f2e99feSLydia Wang #endif
4161f2e99feSLydia Wang 
41783012a7cSTakashi Iwai #ifdef CONFIG_PM
418cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
419cb53c626STakashi Iwai {
420cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
421b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
422b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
423b3f6008fSTakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
424cb53c626STakashi Iwai }
425cb53c626STakashi Iwai #endif
426cb53c626STakashi Iwai 
427c577b8a1SJoseph Chan /*
428c577b8a1SJoseph Chan  */
4295d41762aSTakashi Iwai 
4305d41762aSTakashi Iwai static int via_init(struct hda_codec *codec);
4315d41762aSTakashi Iwai 
43290dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
4330e8f9862STakashi Iwai 	.build_controls = snd_hda_gen_build_controls,
434b3f6008fSTakashi Iwai 	.build_pcms = snd_hda_gen_build_pcms,
435c577b8a1SJoseph Chan 	.init = via_init,
436c577b8a1SJoseph Chan 	.free = via_free,
4374e2d16d3SDavid Henningsson 	.unsol_event = snd_hda_jack_unsol_event,
4382a43952aSTakashi Iwai #ifdef CONFIG_PM
4391f2e99feSLydia Wang 	.suspend = via_suspend,
4406b6d0007STakashi Iwai 	.resume = via_resume,
441cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
442cb53c626STakashi Iwai #endif
443c577b8a1SJoseph Chan };
444c577b8a1SJoseph Chan 
4454a79616dSTakashi Iwai 
446b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = {
447b3f6008fSTakashi Iwai 	/* power down jack detect function */
448b3f6008fSTakashi Iwai 	{0x1, 0xf81, 0x1},
449b3f6008fSTakashi Iwai 	{ }
4504a79616dSTakashi Iwai };
45176d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
45276d9b0ddSHarald Welte {
45376d9b0ddSHarald Welte 	unsigned int def_conf;
45476d9b0ddSHarald Welte 	unsigned char seqassoc;
45576d9b0ddSHarald Welte 
4562f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
45776d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
45876d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
45982ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
46082ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
46176d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
4622f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
46376d9b0ddSHarald Welte 	}
46476d9b0ddSHarald Welte 
46576d9b0ddSHarald Welte 	return;
46676d9b0ddSHarald Welte }
46776d9b0ddSHarald Welte 
468e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
4691f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
4701f2e99feSLydia Wang {
4711f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4721f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
4731f2e99feSLydia Wang 
4741f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
4751f2e99feSLydia Wang 		return 0;
476e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
4771f2e99feSLydia Wang 	return 0;
4781f2e99feSLydia Wang }
4791f2e99feSLydia Wang 
480e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
4811f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
4821f2e99feSLydia Wang {
4831f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
4841f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
485187d333eSTakashi Iwai 	int val;
4861f2e99feSLydia Wang 
4871f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
4881f2e99feSLydia Wang 		return 0;
489187d333eSTakashi Iwai 	val = !!ucontrol->value.integer.value[0];
490187d333eSTakashi Iwai 	if (spec->vt1708_jack_detect == val)
491187d333eSTakashi Iwai 		return 0;
492187d333eSTakashi Iwai 	spec->vt1708_jack_detect = val;
493b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
494187d333eSTakashi Iwai 	return 1;
4951f2e99feSLydia Wang }
4961f2e99feSLydia Wang 
4970e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
4981f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
4991f2e99feSLydia Wang 	.name = "Jack Detect",
5001f2e99feSLydia Wang 	.count = 1,
5011f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
502e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
503e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
5041f2e99feSLydia Wang };
5051f2e99feSLydia Wang 
5064abdbd1cSTakashi Iwai static const struct badness_table via_main_out_badness = {
5074abdbd1cSTakashi Iwai 	.no_primary_dac = 0x10000,
5084abdbd1cSTakashi Iwai 	.no_dac = 0x4000,
5094abdbd1cSTakashi Iwai 	.shared_primary = 0x10000,
5104abdbd1cSTakashi Iwai 	.shared_surr = 0x20,
5114abdbd1cSTakashi Iwai 	.shared_clfe = 0x20,
5124abdbd1cSTakashi Iwai 	.shared_surr_main = 0x20,
5134abdbd1cSTakashi Iwai };
5144abdbd1cSTakashi Iwai static const struct badness_table via_extra_out_badness = {
5154abdbd1cSTakashi Iwai 	.no_primary_dac = 0x4000,
5164abdbd1cSTakashi Iwai 	.no_dac = 0x4000,
5174abdbd1cSTakashi Iwai 	.shared_primary = 0x12,
5184abdbd1cSTakashi Iwai 	.shared_surr = 0x20,
5194abdbd1cSTakashi Iwai 	.shared_clfe = 0x20,
5204abdbd1cSTakashi Iwai 	.shared_surr_main = 0x10,
5214abdbd1cSTakashi Iwai };
5224abdbd1cSTakashi Iwai 
523b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec)
524b3f6008fSTakashi Iwai {
525b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
526b3f6008fSTakashi Iwai 	int err;
527b3f6008fSTakashi Iwai 
5284abdbd1cSTakashi Iwai 	spec->gen.main_out_badness = &via_main_out_badness;
5294abdbd1cSTakashi Iwai 	spec->gen.extra_out_badness = &via_extra_out_badness;
5304abdbd1cSTakashi Iwai 
531b3f6008fSTakashi Iwai 	err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
532b3f6008fSTakashi Iwai 	if (err < 0)
533b3f6008fSTakashi Iwai 		return err;
534b3f6008fSTakashi Iwai 
535b3f6008fSTakashi Iwai 	err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
536b3f6008fSTakashi Iwai 	if (err < 0)
537b3f6008fSTakashi Iwai 		return err;
538b3f6008fSTakashi Iwai 
5390e8f9862STakashi Iwai 	err = auto_parse_beep(codec);
5400e8f9862STakashi Iwai 	if (err < 0)
5410e8f9862STakashi Iwai 		return err;
5420e8f9862STakashi Iwai 
5430e8f9862STakashi Iwai 	if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &via_pin_power_ctl_enum))
5440e8f9862STakashi Iwai 		return -ENOMEM;
5450e8f9862STakashi Iwai 
546688b12ccSTakashi Iwai 	/* disable widget PM at start for compatibility */
547967b1307STakashi Iwai 	codec->power_save_node = 0;
548688b12ccSTakashi Iwai 	spec->gen.power_down_unused = 0;
549b3f6008fSTakashi Iwai 	return 0;
5504a918ffeSTakashi Iwai }
5514a918ffeSTakashi Iwai 
5525d41762aSTakashi Iwai static int via_init(struct hda_codec *codec)
5535d41762aSTakashi Iwai {
554e9d010c2STakashi Iwai 	/* init power states */
555e9d010c2STakashi Iwai 	__analog_low_current_mode(codec, true);
556e9d010c2STakashi Iwai 
557b3f6008fSTakashi Iwai 	snd_hda_gen_init(codec);
55811890956SLydia Wang 
559b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
56025eaba2fSLydia Wang 
561c577b8a1SJoseph Chan 	return 0;
562c577b8a1SJoseph Chan }
563c577b8a1SJoseph Chan 
564f672f65aSDavid Henningsson static int vt1708_build_controls(struct hda_codec *codec)
565f672f65aSDavid Henningsson {
566f672f65aSDavid Henningsson 	/* In order not to create "Phantom Jack" controls,
567f672f65aSDavid Henningsson 	   temporary enable jackpoll */
568f672f65aSDavid Henningsson 	int err;
569f672f65aSDavid Henningsson 	int old_interval = codec->jackpoll_interval;
570f672f65aSDavid Henningsson 	codec->jackpoll_interval = msecs_to_jiffies(100);
5710e8f9862STakashi Iwai 	err = snd_hda_gen_build_controls(codec);
572f672f65aSDavid Henningsson 	codec->jackpoll_interval = old_interval;
573f672f65aSDavid Henningsson 	return err;
574f672f65aSDavid Henningsson }
575f672f65aSDavid Henningsson 
576b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec)
577337b9d02STakashi Iwai {
578337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
579b3f6008fSTakashi Iwai 	int i, err;
580337b9d02STakashi Iwai 
581b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_pcms(codec);
5827639a06cSTakashi Iwai 	if (err < 0 || codec->core.vendor_id != 0x11061708)
583b3f6008fSTakashi Iwai 		return err;
584b3f6008fSTakashi Iwai 
585b3f6008fSTakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
586b3f6008fSTakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
587b3f6008fSTakashi Iwai 	 * disable the 24bit format, so far.
588b3f6008fSTakashi Iwai 	 */
589bbbc7e85STakashi Iwai 	for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) {
590bbbc7e85STakashi Iwai 		struct hda_pcm *info = spec->gen.pcm_rec[i];
591bbbc7e85STakashi Iwai 		if (!info)
592bbbc7e85STakashi Iwai 			continue;
593b3f6008fSTakashi Iwai 		if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
594b3f6008fSTakashi Iwai 		    info->pcm_type != HDA_PCM_TYPE_AUDIO)
595b3f6008fSTakashi Iwai 			continue;
596b3f6008fSTakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats =
597b3f6008fSTakashi Iwai 			SNDRV_PCM_FMTBIT_S16_LE;
598337b9d02STakashi Iwai 	}
599b3f6008fSTakashi Iwai 
6001c55d521STakashi Iwai 	return 0;
601337b9d02STakashi Iwai }
602337b9d02STakashi Iwai 
603c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
604c577b8a1SJoseph Chan {
605c577b8a1SJoseph Chan 	struct via_spec *spec;
606c577b8a1SJoseph Chan 	int err;
607c577b8a1SJoseph Chan 
608c577b8a1SJoseph Chan 	/* create a codec specific record */
6095b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
610c577b8a1SJoseph Chan 	if (spec == NULL)
611c577b8a1SJoseph Chan 		return -ENOMEM;
612c577b8a1SJoseph Chan 
613225068abSTakashi Iwai 	/* override some patch_ops */
614225068abSTakashi Iwai 	codec->patch_ops.build_controls = vt1708_build_controls;
615225068abSTakashi Iwai 	codec->patch_ops.build_pcms = vt1708_build_pcms;
616b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x17;
617b3f6008fSTakashi Iwai 
618b3f6008fSTakashi Iwai 	/* set jackpoll_interval while parsing the codec */
619b3f6008fSTakashi Iwai 	codec->jackpoll_interval = msecs_to_jiffies(100);
620b3f6008fSTakashi Iwai 	spec->vt1708_jack_detect = 1;
621b3f6008fSTakashi Iwai 
622b3f6008fSTakashi Iwai 	/* don't support the input jack switching due to lack of unsol event */
623b3f6008fSTakashi Iwai 	/* (it may work with polling, though, but it needs testing) */
624b3f6008fSTakashi Iwai 	spec->gen.suppress_auto_mic = 1;
625eb33ccf7STakashi Iwai 	/* Some machines show the broken speaker mute */
626eb33ccf7STakashi Iwai 	spec->gen.auto_mute_via_amp = 1;
627620e2b28STakashi Iwai 
62812daef65STakashi Iwai 	/* Add HP and CD pin config connect bit re-config action */
62912daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
63012daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
63112daef65STakashi Iwai 
632f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt1708_init_verbs);
633f8bfc628STakashi Iwai 	if (err < 0)
634f8bfc628STakashi Iwai 		goto error;
635f8bfc628STakashi Iwai 
636c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
63712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
638fcbdcc1aSTakashi Iwai 	if (err < 0)
639fcbdcc1aSTakashi Iwai 		goto error;
640c577b8a1SJoseph Chan 
64112daef65STakashi Iwai 	/* add jack detect on/off control */
6420e8f9862STakashi Iwai 	if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1708_jack_detect_ctl)) {
6430e8f9862STakashi Iwai 		err = -ENOMEM;
6440e8f9862STakashi Iwai 		goto error;
6450e8f9862STakashi Iwai 	}
646c577b8a1SJoseph Chan 
647b3f6008fSTakashi Iwai 	/* clear jackpoll_interval again; it's set dynamically */
648b3f6008fSTakashi Iwai 	codec->jackpoll_interval = 0;
649b3f6008fSTakashi Iwai 
650c577b8a1SJoseph Chan 	return 0;
651fcbdcc1aSTakashi Iwai 
652fcbdcc1aSTakashi Iwai  error:
653fcbdcc1aSTakashi Iwai 	via_free(codec);
654fcbdcc1aSTakashi Iwai 	return err;
655c577b8a1SJoseph Chan }
656c577b8a1SJoseph Chan 
657ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec)
658c577b8a1SJoseph Chan {
659c577b8a1SJoseph Chan 	struct via_spec *spec;
660c577b8a1SJoseph Chan 	int err;
661c577b8a1SJoseph Chan 
662c577b8a1SJoseph Chan 	/* create a codec specific record */
6635b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
664c577b8a1SJoseph Chan 	if (spec == NULL)
665c577b8a1SJoseph Chan 		return -ENOMEM;
666c577b8a1SJoseph Chan 
667b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x18;
668620e2b28STakashi Iwai 
66912daef65STakashi Iwai 	err = via_parse_auto_config(codec);
670fcbdcc1aSTakashi Iwai 	if (err < 0)
671fcbdcc1aSTakashi Iwai 		goto error;
672c577b8a1SJoseph Chan 
673f7278fd0SJosepch Chan 	return 0;
674fcbdcc1aSTakashi Iwai 
675fcbdcc1aSTakashi Iwai  error:
676fcbdcc1aSTakashi Iwai 	via_free(codec);
677fcbdcc1aSTakashi Iwai 	return err;
678f7278fd0SJosepch Chan }
679f7278fd0SJosepch Chan 
680518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
681ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec)
682f7278fd0SJosepch Chan {
683f7278fd0SJosepch Chan 	struct via_spec *spec;
684f7278fd0SJosepch Chan 	int err;
685f7278fd0SJosepch Chan 
686518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
687518bf3baSLydia Wang 		return patch_vt1708S(codec);
688ddd304d8STakashi Iwai 
689f7278fd0SJosepch Chan 	/* create a codec specific record */
6905b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
691f7278fd0SJosepch Chan 	if (spec == NULL)
692f7278fd0SJosepch Chan 		return -ENOMEM;
693f7278fd0SJosepch Chan 
694b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
695620e2b28STakashi Iwai 
696f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
69712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
698fcbdcc1aSTakashi Iwai 	if (err < 0)
699fcbdcc1aSTakashi Iwai 		goto error;
700f7278fd0SJosepch Chan 
701f7278fd0SJosepch Chan 	return 0;
702fcbdcc1aSTakashi Iwai 
703fcbdcc1aSTakashi Iwai  error:
704fcbdcc1aSTakashi Iwai 	via_free(codec);
705fcbdcc1aSTakashi Iwai 	return err;
706f7278fd0SJosepch Chan }
707f7278fd0SJosepch Chan 
708d949cac1SHarald Welte /* Patch for VT1708S */
709096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = {
710d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
711d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
712bc7e7e5cSLydia Wang 	/* don't bybass mixer */
713bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
714d949cac1SHarald Welte 	{ }
715d949cac1SHarald Welte };
716d949cac1SHarald Welte 
7176369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
7186369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
7196369bcfcSLydia Wang {
720d045c5dcSTakashi Iwai 	snd_hda_override_wcaps(codec, pin,
721d045c5dcSTakashi Iwai 			       get_wcaps(codec, pin) | AC_WCAP_IN_AMP);
7226369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
7236369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
7246369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
7256369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
7266369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
7276369bcfcSLydia Wang }
7286369bcfcSLydia Wang 
729d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
730d949cac1SHarald Welte {
731d949cac1SHarald Welte 	struct via_spec *spec;
732d949cac1SHarald Welte 	int err;
733d949cac1SHarald Welte 
734d949cac1SHarald Welte 	/* create a codec specific record */
7355b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
736d949cac1SHarald Welte 	if (spec == NULL)
737d949cac1SHarald Welte 		return -ENOMEM;
738d949cac1SHarald Welte 
739b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
740d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
741d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
742620e2b28STakashi Iwai 
743518bf3baSLydia Wang 	/* correct names for VT1708BCE */
744ded255beSTakashi Iwai 	if (get_codec_type(codec) == VT1708BCE)
745ded255beSTakashi Iwai 		snd_hda_codec_set_name(codec, "VT1708BCE");
746bc92df7fSLydia Wang 	/* correct names for VT1705 */
747ded255beSTakashi Iwai 	if (codec->core.vendor_id == 0x11064397)
748ded255beSTakashi Iwai 		snd_hda_codec_set_name(codec, "VT1705");
749b3f6008fSTakashi Iwai 
750f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt1708S_init_verbs);
751f8bfc628STakashi Iwai 	if (err < 0)
752f8bfc628STakashi Iwai 		goto error;
753f8bfc628STakashi Iwai 
754b3f6008fSTakashi Iwai 	/* automatic parse from the BIOS config */
755b3f6008fSTakashi Iwai 	err = via_parse_auto_config(codec);
756fcbdcc1aSTakashi Iwai 	if (err < 0)
757fcbdcc1aSTakashi Iwai 		goto error;
758b3f6008fSTakashi Iwai 
759d949cac1SHarald Welte 	return 0;
760fcbdcc1aSTakashi Iwai 
761fcbdcc1aSTakashi Iwai  error:
762fcbdcc1aSTakashi Iwai 	via_free(codec);
763fcbdcc1aSTakashi Iwai 	return err;
764d949cac1SHarald Welte }
765d949cac1SHarald Welte 
766d949cac1SHarald Welte /* Patch for VT1702 */
767d949cac1SHarald Welte 
768096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = {
769bc7e7e5cSLydia Wang 	/* mixer enable */
770bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
771bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
772bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
773d949cac1SHarald Welte 	{ }
774d949cac1SHarald Welte };
775d949cac1SHarald Welte 
776d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
777d949cac1SHarald Welte {
778d949cac1SHarald Welte 	struct via_spec *spec;
779d949cac1SHarald Welte 	int err;
780d949cac1SHarald Welte 
781d949cac1SHarald Welte 	/* create a codec specific record */
7825b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
783d949cac1SHarald Welte 	if (spec == NULL)
784d949cac1SHarald Welte 		return -ENOMEM;
785d949cac1SHarald Welte 
786b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x1a;
787620e2b28STakashi Iwai 
78812daef65STakashi Iwai 	/* limit AA path volume to 0 dB */
78912daef65STakashi Iwai 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
79012daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
79112daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
79212daef65STakashi Iwai 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
79312daef65STakashi Iwai 				  (1 << AC_AMPCAP_MUTE_SHIFT));
79412daef65STakashi Iwai 
795f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt1702_init_verbs);
796f8bfc628STakashi Iwai 	if (err < 0)
797f8bfc628STakashi Iwai 		goto error;
798f8bfc628STakashi Iwai 
799d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
80012daef65STakashi Iwai 	err = via_parse_auto_config(codec);
801fcbdcc1aSTakashi Iwai 	if (err < 0)
802fcbdcc1aSTakashi Iwai 		goto error;
803d949cac1SHarald Welte 
804d949cac1SHarald Welte 	return 0;
805fcbdcc1aSTakashi Iwai 
806fcbdcc1aSTakashi Iwai  error:
807fcbdcc1aSTakashi Iwai 	via_free(codec);
808fcbdcc1aSTakashi Iwai 	return err;
809d949cac1SHarald Welte }
810d949cac1SHarald Welte 
811eb7188caSLydia Wang /* Patch for VT1718S */
812eb7188caSLydia Wang 
813096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = {
8144ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
8154ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
816eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
817eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
8185d41762aSTakashi Iwai 
819eb7188caSLydia Wang 	{ }
820eb7188caSLydia Wang };
821eb7188caSLydia Wang 
82230b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs
82330b45033STakashi Iwai  * This isn't listed from the raw info, but the chip has a secret connection.
82430b45033STakashi Iwai  */
82530b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec)
82630b45033STakashi Iwai {
82730b45033STakashi Iwai 	struct via_spec *spec = codec->spec;
82830b45033STakashi Iwai 	int i, nums;
82930b45033STakashi Iwai 	hda_nid_t conn[8];
83030b45033STakashi Iwai 	hda_nid_t nid;
83130b45033STakashi Iwai 
832b3f6008fSTakashi Iwai 	if (!spec->gen.mixer_nid)
83330b45033STakashi Iwai 		return 0;
834b3f6008fSTakashi Iwai 	nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
83530b45033STakashi Iwai 				       ARRAY_SIZE(conn) - 1);
83630b45033STakashi Iwai 	for (i = 0; i < nums; i++) {
83730b45033STakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
83830b45033STakashi Iwai 			return 0;
83930b45033STakashi Iwai 	}
84030b45033STakashi Iwai 
84130b45033STakashi Iwai 	/* find the primary DAC and add to the connection list */
8427639a06cSTakashi Iwai 	for_each_hda_codec_node(nid, codec) {
84330b45033STakashi Iwai 		unsigned int caps = get_wcaps(codec, nid);
84430b45033STakashi Iwai 		if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
84530b45033STakashi Iwai 		    !(caps & AC_WCAP_DIGITAL)) {
84630b45033STakashi Iwai 			conn[nums++] = nid;
84730b45033STakashi Iwai 			return snd_hda_override_conn_list(codec,
848b3f6008fSTakashi Iwai 							  spec->gen.mixer_nid,
84930b45033STakashi Iwai 							  nums, conn);
85030b45033STakashi Iwai 		}
85130b45033STakashi Iwai 	}
85230b45033STakashi Iwai 	return 0;
85330b45033STakashi Iwai }
85430b45033STakashi Iwai 
85530b45033STakashi Iwai 
856eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
857eb7188caSLydia Wang {
858eb7188caSLydia Wang 	struct via_spec *spec;
859eb7188caSLydia Wang 	int err;
860eb7188caSLydia Wang 
861eb7188caSLydia Wang 	/* create a codec specific record */
8625b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
863eb7188caSLydia Wang 	if (spec == NULL)
864eb7188caSLydia Wang 		return -ENOMEM;
865eb7188caSLydia Wang 
866b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
867d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
868d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
86930b45033STakashi Iwai 	add_secret_dac_path(codec);
870620e2b28STakashi Iwai 
871f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt1718S_init_verbs);
872f8bfc628STakashi Iwai 	if (err < 0)
873f8bfc628STakashi Iwai 		goto error;
874f8bfc628STakashi Iwai 
875eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
87612daef65STakashi Iwai 	err = via_parse_auto_config(codec);
877fcbdcc1aSTakashi Iwai 	if (err < 0)
878fcbdcc1aSTakashi Iwai 		goto error;
879eb7188caSLydia Wang 
880eb7188caSLydia Wang 	return 0;
881fcbdcc1aSTakashi Iwai 
882fcbdcc1aSTakashi Iwai  error:
883fcbdcc1aSTakashi Iwai 	via_free(codec);
884fcbdcc1aSTakashi Iwai 	return err;
885eb7188caSLydia Wang }
886f3db423dSLydia Wang 
887f3db423dSLydia Wang /* Patch for VT1716S */
888f3db423dSLydia Wang 
889f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
890f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
891f3db423dSLydia Wang {
892f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
893f3db423dSLydia Wang 	uinfo->count = 1;
894f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
895f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
896f3db423dSLydia Wang 	return 0;
897f3db423dSLydia Wang }
898f3db423dSLydia Wang 
899f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
900f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
901f3db423dSLydia Wang {
902f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
903f3db423dSLydia Wang 	int index = 0;
904f3db423dSLydia Wang 
905f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
906f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
907f3db423dSLydia Wang 	if (index != -1)
908f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
909f3db423dSLydia Wang 
910f3db423dSLydia Wang 	return 0;
911f3db423dSLydia Wang }
912f3db423dSLydia Wang 
913f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
914f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
915f3db423dSLydia Wang {
916f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
917f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
918f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
919f3db423dSLydia Wang 
920f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
921f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
922f3db423dSLydia Wang 	spec->dmic_enabled = index;
923f3db423dSLydia Wang 	return 1;
924f3db423dSLydia Wang }
925f3db423dSLydia Wang 
9260e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer_vol =
9270e8f9862STakashi Iwai 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT);
9280e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer_sw = {
929f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
930f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
9315b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
932f3db423dSLydia Wang 	 .count = 1,
933f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
934f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
935f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
936f3db423dSLydia Wang };
937f3db423dSLydia Wang 
938f3db423dSLydia Wang 
939f3db423dSLydia Wang /* mono-out mixer elements */
9400e8f9862STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer =
9410e8f9862STakashi Iwai 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT);
942f3db423dSLydia Wang 
943096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = {
944f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
945f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
946f3db423dSLydia Wang 	/* don't bybass mixer */
947f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
948f3db423dSLydia Wang 	/* Enable mono output */
949f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
950f3db423dSLydia Wang 	{ }
951f3db423dSLydia Wang };
952f3db423dSLydia Wang 
953f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
954f3db423dSLydia Wang {
955f3db423dSLydia Wang 	struct via_spec *spec;
956f3db423dSLydia Wang 	int err;
957f3db423dSLydia Wang 
958f3db423dSLydia Wang 	/* create a codec specific record */
9595b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
960f3db423dSLydia Wang 	if (spec == NULL)
961f3db423dSLydia Wang 		return -ENOMEM;
962f3db423dSLydia Wang 
963b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
964d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
965d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
966620e2b28STakashi Iwai 
967f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt1716S_init_verbs);
968f8bfc628STakashi Iwai 	if (err < 0)
969f8bfc628STakashi Iwai 		goto error;
970f8bfc628STakashi Iwai 
971f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
97212daef65STakashi Iwai 	err = via_parse_auto_config(codec);
973fcbdcc1aSTakashi Iwai 	if (err < 0)
974fcbdcc1aSTakashi Iwai 		goto error;
975f3db423dSLydia Wang 
9760e8f9862STakashi Iwai 	if (!snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_vol) ||
9770e8f9862STakashi Iwai 	    !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716s_dmic_mixer_sw) ||
9780e8f9862STakashi Iwai 	    !snd_hda_gen_add_kctl(&spec->gen, NULL, &vt1716S_mono_out_mixer)) {
9790e8f9862STakashi Iwai 		err = -ENOMEM;
9800e8f9862STakashi Iwai 		goto error;
9810e8f9862STakashi Iwai 	}
982f3db423dSLydia Wang 
983f3db423dSLydia Wang 	return 0;
984fcbdcc1aSTakashi Iwai 
985fcbdcc1aSTakashi Iwai  error:
986fcbdcc1aSTakashi Iwai 	via_free(codec);
987fcbdcc1aSTakashi Iwai 	return err;
988f3db423dSLydia Wang }
98925eaba2fSLydia Wang 
99025eaba2fSLydia Wang /* for vt2002P */
99125eaba2fSLydia Wang 
992096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = {
993eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
994eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
995eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
996eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
99725eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
99825eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
99925eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
100025eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
100125eaba2fSLydia Wang 	{ }
100225eaba2fSLydia Wang };
10034a918ffeSTakashi Iwai 
1004096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = {
100511890956SLydia Wang 	/* Enable Boost Volume backdoor */
100611890956SLydia Wang 	{0x1, 0xfb9, 0x24},
100711890956SLydia Wang 	/* Enable AOW0 to MW9 */
100811890956SLydia Wang 	{0x1, 0xfb8, 0x88},
100911890956SLydia Wang 	{ }
101011890956SLydia Wang };
101125eaba2fSLydia Wang 
10124b527b65SDavid Henningsson /*
10134b527b65SDavid Henningsson  * pin fix-up
10144b527b65SDavid Henningsson  */
10154b527b65SDavid Henningsson enum {
10164b527b65SDavid Henningsson 	VIA_FIXUP_INTMIC_BOOST,
1017d5266125STakashi Iwai 	VIA_FIXUP_ASUS_G75,
10184b527b65SDavid Henningsson };
10194b527b65SDavid Henningsson 
10204b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec,
10214b527b65SDavid Henningsson 				  const struct hda_fixup *fix, int action)
10224b527b65SDavid Henningsson {
10234b527b65SDavid Henningsson 	if (action == HDA_FIXUP_ACT_PRE_PROBE)
10244b527b65SDavid Henningsson 		override_mic_boost(codec, 0x30, 0, 2, 40);
10254b527b65SDavid Henningsson }
10264b527b65SDavid Henningsson 
10274b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = {
10284b527b65SDavid Henningsson 	[VIA_FIXUP_INTMIC_BOOST] = {
10294b527b65SDavid Henningsson 		.type = HDA_FIXUP_FUNC,
10304b527b65SDavid Henningsson 		.v.func = via_fixup_intmic_boost,
10314b527b65SDavid Henningsson 	},
1032d5266125STakashi Iwai 	[VIA_FIXUP_ASUS_G75] = {
1033d5266125STakashi Iwai 		.type = HDA_FIXUP_PINS,
1034d5266125STakashi Iwai 		.v.pins = (const struct hda_pintbl[]) {
1035d5266125STakashi Iwai 			/* set 0x24 and 0x33 as speakers */
1036d5266125STakashi Iwai 			{ 0x24, 0x991301f0 },
1037d5266125STakashi Iwai 			{ 0x33, 0x991301f1 }, /* subwoofer */
1038d5266125STakashi Iwai 			{ }
1039d5266125STakashi Iwai 		}
1040d5266125STakashi Iwai 	},
10414b527b65SDavid Henningsson };
10424b527b65SDavid Henningsson 
10434b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = {
1044d5266125STakashi Iwai 	SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
10454b527b65SDavid Henningsson 	SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
10464b527b65SDavid Henningsson 	{}
10474b527b65SDavid Henningsson };
10484b527b65SDavid Henningsson 
1049ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
1050ef4da458STakashi Iwai  * Replace this with mixer NID 0x1c
1051ef4da458STakashi Iwai  */
1052ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec)
1053ef4da458STakashi Iwai {
1054ef4da458STakashi Iwai 	static hda_nid_t conn_24[] = { 0x14, 0x1c };
1055ef4da458STakashi Iwai 	static hda_nid_t conn_33[] = { 0x1c };
1056ef4da458STakashi Iwai 
1057ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
1058ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
1059ef4da458STakashi Iwai }
1060ef4da458STakashi Iwai 
106125eaba2fSLydia Wang /* patch for vt2002P */
106225eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
106325eaba2fSLydia Wang {
106425eaba2fSLydia Wang 	struct via_spec *spec;
106525eaba2fSLydia Wang 	int err;
106625eaba2fSLydia Wang 
106725eaba2fSLydia Wang 	/* create a codec specific record */
10685b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
106925eaba2fSLydia Wang 	if (spec == NULL)
107025eaba2fSLydia Wang 		return -ENOMEM;
107125eaba2fSLydia Wang 
1072b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1073d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1074d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
1075ef4da458STakashi Iwai 	if (spec->codec_type == VT1802)
1076ef4da458STakashi Iwai 		fix_vt1802_connections(codec);
107730b45033STakashi Iwai 	add_secret_dac_path(codec);
1078620e2b28STakashi Iwai 
10794b527b65SDavid Henningsson 	snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
10804b527b65SDavid Henningsson 	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
10814b527b65SDavid Henningsson 
1082f8bfc628STakashi Iwai 	if (spec->codec_type == VT1802)
1083f8bfc628STakashi Iwai 		err = snd_hda_add_verbs(codec, vt1802_init_verbs);
1084f8bfc628STakashi Iwai 	else
1085f8bfc628STakashi Iwai 		err = snd_hda_add_verbs(codec, vt2002P_init_verbs);
1086f8bfc628STakashi Iwai 	if (err < 0)
1087f8bfc628STakashi Iwai 		goto error;
1088f8bfc628STakashi Iwai 
108925eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
109012daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1091fcbdcc1aSTakashi Iwai 	if (err < 0)
1092fcbdcc1aSTakashi Iwai 		goto error;
109325eaba2fSLydia Wang 
109425eaba2fSLydia Wang 	return 0;
1095fcbdcc1aSTakashi Iwai 
1096fcbdcc1aSTakashi Iwai  error:
1097fcbdcc1aSTakashi Iwai 	via_free(codec);
1098fcbdcc1aSTakashi Iwai 	return err;
109925eaba2fSLydia Wang }
1100ab6734e7SLydia Wang 
1101ab6734e7SLydia Wang /* for vt1812 */
1102ab6734e7SLydia Wang 
1103096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = {
1104ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
1105ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
1106ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
1107ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
1108ab6734e7SLydia Wang 	{ }
1109ab6734e7SLydia Wang };
1110ab6734e7SLydia Wang 
1111ab6734e7SLydia Wang /* patch for vt1812 */
1112ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
1113ab6734e7SLydia Wang {
1114ab6734e7SLydia Wang 	struct via_spec *spec;
1115ab6734e7SLydia Wang 	int err;
1116ab6734e7SLydia Wang 
1117ab6734e7SLydia Wang 	/* create a codec specific record */
11185b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1119ab6734e7SLydia Wang 	if (spec == NULL)
1120ab6734e7SLydia Wang 		return -ENOMEM;
1121ab6734e7SLydia Wang 
1122b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1123d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1124d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
112530b45033STakashi Iwai 	add_secret_dac_path(codec);
1126620e2b28STakashi Iwai 
1127f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt1812_init_verbs);
1128f8bfc628STakashi Iwai 	if (err < 0)
1129f8bfc628STakashi Iwai 		goto error;
1130f8bfc628STakashi Iwai 
1131ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
113212daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1133fcbdcc1aSTakashi Iwai 	if (err < 0)
1134fcbdcc1aSTakashi Iwai 		goto error;
1135ab6734e7SLydia Wang 
1136ab6734e7SLydia Wang 	return 0;
1137fcbdcc1aSTakashi Iwai 
1138fcbdcc1aSTakashi Iwai  error:
1139fcbdcc1aSTakashi Iwai 	via_free(codec);
1140fcbdcc1aSTakashi Iwai 	return err;
1141ab6734e7SLydia Wang }
1142ab6734e7SLydia Wang 
114343737e0aSLydia Wang /* patch for vt3476 */
114443737e0aSLydia Wang 
114543737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = {
114643737e0aSLydia Wang 	/* Enable DMic 8/16/32K */
114743737e0aSLydia Wang 	{0x1, 0xF7B, 0x30},
114843737e0aSLydia Wang 	/* Enable Boost Volume backdoor */
114943737e0aSLydia Wang 	{0x1, 0xFB9, 0x20},
115043737e0aSLydia Wang 	/* Enable AOW-MW9 path */
115143737e0aSLydia Wang 	{0x1, 0xFB8, 0x10},
115243737e0aSLydia Wang 	{ }
115343737e0aSLydia Wang };
115443737e0aSLydia Wang 
115543737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec)
115643737e0aSLydia Wang {
115743737e0aSLydia Wang 	struct via_spec *spec;
115843737e0aSLydia Wang 	int err;
115943737e0aSLydia Wang 
116043737e0aSLydia Wang 	/* create a codec specific record */
116143737e0aSLydia Wang 	spec = via_new_spec(codec);
116243737e0aSLydia Wang 	if (spec == NULL)
116343737e0aSLydia Wang 		return -ENOMEM;
116443737e0aSLydia Wang 
1165b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x3f;
116643737e0aSLydia Wang 	add_secret_dac_path(codec);
116743737e0aSLydia Wang 
1168f8bfc628STakashi Iwai 	err = snd_hda_add_verbs(codec, vt3476_init_verbs);
1169f8bfc628STakashi Iwai 	if (err < 0)
1170f8bfc628STakashi Iwai 		goto error;
1171f8bfc628STakashi Iwai 
117243737e0aSLydia Wang 	/* automatic parse from the BIOS config */
117343737e0aSLydia Wang 	err = via_parse_auto_config(codec);
1174fcbdcc1aSTakashi Iwai 	if (err < 0)
1175fcbdcc1aSTakashi Iwai 		goto error;
117643737e0aSLydia Wang 
117743737e0aSLydia Wang 	return 0;
1178fcbdcc1aSTakashi Iwai 
1179fcbdcc1aSTakashi Iwai  error:
1180fcbdcc1aSTakashi Iwai 	via_free(codec);
1181fcbdcc1aSTakashi Iwai 	return err;
118243737e0aSLydia Wang }
118343737e0aSLydia Wang 
1184c577b8a1SJoseph Chan /*
1185c577b8a1SJoseph Chan  * patch entries
1186c577b8a1SJoseph Chan  */
1187b9a94a9cSTakashi Iwai static const struct hda_device_id snd_hda_id_via[] = {
1188b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708),
1189b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708),
1190b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708),
1191b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708),
1192b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709),
1193b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709),
1194b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709),
1195b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709),
1196b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709),
1197b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709),
1198b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709),
1199b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709),
1200b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B),
1201b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B),
1202b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B),
1203b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B),
1204b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B),
1205b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B),
1206b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B),
1207b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B),
1208b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S),
1209b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S),
1210b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S),
1211b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S),
1212b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S),
1213b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S),
1214b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S),
1215b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S),
1216b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702),
1217b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702),
1218b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702),
1219b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702),
1220b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702),
1221b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702),
1222b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702),
1223b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702),
1224b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S),
1225b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S),
1226b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S),
1227b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S),
1228b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S),
1229b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S),
1230b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P),
1231b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P),
1232b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812),
1233b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S),
1234b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P),
1235b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P),
1236b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476),
1237b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476),
1238b9a94a9cSTakashi Iwai 	HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476),
1239c577b8a1SJoseph Chan 	{} /* terminator */
1240c577b8a1SJoseph Chan };
1241b9a94a9cSTakashi Iwai MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via);
12421289e9e8STakashi Iwai 
1243d8a766a1STakashi Iwai static struct hda_codec_driver via_driver = {
1244b9a94a9cSTakashi Iwai 	.id = snd_hda_id_via,
12451289e9e8STakashi Iwai };
12461289e9e8STakashi Iwai 
12471289e9e8STakashi Iwai MODULE_LICENSE("GPL");
12481289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
12491289e9e8STakashi Iwai 
1250d8a766a1STakashi Iwai module_hda_codec_driver(via_driver);
1251