xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision 05dc0fc9d01537a66d9a0cffe2e96296d8f4c7ac)
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;
10224088a58STakashi Iwai 	unsigned int no_pin_power_ctl;
1031f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1041f2e99feSLydia Wang 
105e9d010c2STakashi Iwai 	/* analog low-power control */
106e9d010c2STakashi Iwai 	bool alc_mode;
107e9d010c2STakashi Iwai 
1081f2e99feSLydia Wang 	/* work to check hp jack state */
109187d333eSTakashi Iwai 	int hp_work_active;
110e06e5a29STakashi Iwai 	int vt1708_jack_detect;
1113e95b9abSLydia Wang 
1123e95b9abSLydia Wang 	void (*set_widgets_power_state)(struct hda_codec *codec);
11343737e0aSLydia Wang 	unsigned int dac_stream_tag[4];
1141f2e99feSLydia Wang };
1151f2e99feSLydia Wang 
1160341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
117b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
118b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
119b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
120b3f6008fSTakashi Iwai 				  int action);
121b3f6008fSTakashi Iwai static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl);
122b3f6008fSTakashi Iwai 
1235b0cb1d8SJaroslav Kysela static struct via_spec *via_new_spec(struct hda_codec *codec)
1245b0cb1d8SJaroslav Kysela {
1255b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1265b0cb1d8SJaroslav Kysela 
1275b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1285b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1295b0cb1d8SJaroslav Kysela 		return NULL;
1305b0cb1d8SJaroslav Kysela 
1315b0cb1d8SJaroslav Kysela 	codec->spec = spec;
132b3f6008fSTakashi Iwai 	snd_hda_gen_spec_init(&spec->gen);
1330341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
1340341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
1350341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
1360341ccd7SLydia Wang 		spec->codec_type = VT1708S;
137b3f6008fSTakashi Iwai 	spec->no_pin_power_ctl = 1;
13813961170STakashi Iwai 	spec->gen.indep_hp = 1;
139b3f6008fSTakashi Iwai 	spec->gen.pcm_playback_hook = via_playback_pcm_hook;
1405b0cb1d8SJaroslav Kysela 	return spec;
1415b0cb1d8SJaroslav Kysela }
1425b0cb1d8SJaroslav Kysela 
143744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
144d7426329SHarald Welte {
145744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
146d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
147d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
148d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
149d7426329SHarald Welte 
150d7426329SHarald Welte 	/* get codec type */
151d7426329SHarald Welte 	if (ven_id != 0x1106)
152d7426329SHarald Welte 		codec_type = UNKNOWN;
153d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
154d7426329SHarald Welte 		codec_type = VT1708;
155d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
156d7426329SHarald Welte 		codec_type = VT1709_10CH;
157d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
158d7426329SHarald Welte 		codec_type = VT1709_6CH;
159518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
160d7426329SHarald Welte 		codec_type = VT1708B_8CH;
161518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
162518bf3baSLydia Wang 			codec_type = VT1708BCE;
163518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
164d7426329SHarald Welte 		codec_type = VT1708B_4CH;
165d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
166d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
167d7426329SHarald Welte 		codec_type = VT1708S;
168d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
169d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
170d7426329SHarald Welte 		codec_type = VT1702;
171eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
172eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
173eb7188caSLydia Wang 		codec_type = VT1718S;
174f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
175f3db423dSLydia Wang 		codec_type = VT1716S;
176bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
177bb3c6bfcSLydia Wang 		codec_type = VT1718S;
17825eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
17925eaba2fSLydia Wang 		codec_type = VT2002P;
180ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
181ab6734e7SLydia Wang 		codec_type = VT1812;
18236dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
18336dd5c4aSLydia Wang 		codec_type = VT1708S;
18411890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
18511890956SLydia Wang 		codec_type = VT1802;
18643737e0aSLydia Wang 	else if (dev_id == 0x4760)
18743737e0aSLydia Wang 		codec_type = VT1705CF;
1886121b84aSLydia Wang 	else if (dev_id == 0x4761 || dev_id == 0x4762)
1896121b84aSLydia Wang 		codec_type = VT1808;
190d7426329SHarald Welte 	else
191d7426329SHarald Welte 		codec_type = UNKNOWN;
192d7426329SHarald Welte 	return codec_type;
193d7426329SHarald Welte };
194d7426329SHarald Welte 
195ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec);
196ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec);
1971f2e99feSLydia Wang 
198187d333eSTakashi Iwai #define hp_detect_with_aa(codec) \
199187d333eSTakashi Iwai 	(snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
200187d333eSTakashi Iwai 	 !is_aa_path_mute(codec))
2011f2e99feSLydia Wang 
202b3f6008fSTakashi Iwai static void vt1708_stop_hp_work(struct hda_codec *codec)
2031f2e99feSLydia Wang {
204b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
205b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
2061f2e99feSLydia Wang 		return;
207187d333eSTakashi Iwai 	if (spec->hp_work_active) {
208b3f6008fSTakashi Iwai 		snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
209b3f6008fSTakashi Iwai 		cancel_delayed_work_sync(&codec->jackpoll_work);
210b3f6008fSTakashi Iwai 		spec->hp_work_active = false;
211b3f6008fSTakashi Iwai 		codec->jackpoll_interval = 0;
212187d333eSTakashi Iwai 	}
213187d333eSTakashi Iwai }
214187d333eSTakashi Iwai 
215b3f6008fSTakashi Iwai static void vt1708_update_hp_work(struct hda_codec *codec)
216187d333eSTakashi Iwai {
217b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
218b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
219187d333eSTakashi Iwai 		return;
220*05dc0fc9SDavid Henningsson 	if (spec->vt1708_jack_detect) {
221187d333eSTakashi Iwai 		if (!spec->hp_work_active) {
222b3f6008fSTakashi Iwai 			codec->jackpoll_interval = msecs_to_jiffies(100);
223b3f6008fSTakashi Iwai 			snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0);
224b3f6008fSTakashi Iwai 			queue_delayed_work(codec->bus->workq,
225b3f6008fSTakashi Iwai 					   &codec->jackpoll_work, 0);
226b3f6008fSTakashi Iwai 			spec->hp_work_active = true;
227187d333eSTakashi Iwai 		}
228b3f6008fSTakashi Iwai 	} else if (!hp_detect_with_aa(codec))
229b3f6008fSTakashi Iwai 		vt1708_stop_hp_work(codec);
2301f2e99feSLydia Wang }
231f5271101SLydia Wang 
2323e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec)
2333e95b9abSLydia Wang {
2343e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
2353e95b9abSLydia Wang 	if (spec->set_widgets_power_state)
2363e95b9abSLydia Wang 		spec->set_widgets_power_state(codec);
2373e95b9abSLydia Wang }
23825eaba2fSLydia Wang 
239054d867eSTakashi Iwai static void update_power_state(struct hda_codec *codec, hda_nid_t nid,
240054d867eSTakashi Iwai 			       unsigned int parm)
241054d867eSTakashi Iwai {
2429040d102STakashi Iwai 	if (snd_hda_check_power_state(codec, nid, parm))
243054d867eSTakashi Iwai 		return;
244054d867eSTakashi Iwai 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
245054d867eSTakashi Iwai }
246054d867eSTakashi Iwai 
24743737e0aSLydia Wang static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid,
24843737e0aSLydia Wang 			       unsigned int parm, unsigned int index)
24943737e0aSLydia Wang {
25043737e0aSLydia Wang 	struct via_spec *spec = codec->spec;
25143737e0aSLydia Wang 	unsigned int format;
2529040d102STakashi Iwai 
2539040d102STakashi Iwai 	if (snd_hda_check_power_state(codec, nid, parm))
25443737e0aSLydia Wang 		return;
25543737e0aSLydia Wang 	format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
25643737e0aSLydia Wang 	if (format && (spec->dac_stream_tag[index] != format))
25743737e0aSLydia Wang 		spec->dac_stream_tag[index] = format;
25843737e0aSLydia Wang 
25943737e0aSLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
26043737e0aSLydia Wang 	if (parm == AC_PWRST_D0) {
26143737e0aSLydia Wang 		format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
26243737e0aSLydia Wang 		if (!format && (spec->dac_stream_tag[index] != format))
26343737e0aSLydia Wang 			snd_hda_codec_write(codec, nid, 0,
26443737e0aSLydia Wang 						  AC_VERB_SET_CHANNEL_STREAMID,
26543737e0aSLydia Wang 						  spec->dac_stream_tag[index]);
26643737e0aSLydia Wang 	}
26743737e0aSLydia Wang }
26843737e0aSLydia Wang 
269b3f6008fSTakashi Iwai static bool smart51_enabled(struct hda_codec *codec)
270b3f6008fSTakashi Iwai {
271b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
272b3f6008fSTakashi Iwai 	return spec->gen.ext_channel_count > 2;
273b3f6008fSTakashi Iwai }
274b3f6008fSTakashi Iwai 
275b3f6008fSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
276b3f6008fSTakashi Iwai {
277b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
278b3f6008fSTakashi Iwai 	int i;
279b3f6008fSTakashi Iwai 
280b3f6008fSTakashi Iwai 	for (i = 0; i < spec->gen.multi_ios; i++)
281b3f6008fSTakashi Iwai 		if (spec->gen.multi_io[i].pin == pin)
282b3f6008fSTakashi Iwai 			return true;
283b3f6008fSTakashi Iwai 	return false;
284b3f6008fSTakashi Iwai }
285b3f6008fSTakashi Iwai 
286f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
287f5271101SLydia Wang 				unsigned int *affected_parm)
288f5271101SLydia Wang {
289f5271101SLydia Wang 	unsigned parm;
290f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
291f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
292f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
293f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
2941564b287SLydia Wang 	struct via_spec *spec = codec->spec;
29524088a58STakashi Iwai 	unsigned present = 0;
29624088a58STakashi Iwai 
29724088a58STakashi Iwai 	no_presence |= spec->no_pin_power_ctl;
29824088a58STakashi Iwai 	if (!no_presence)
29924088a58STakashi Iwai 		present = snd_hda_jack_detect(codec, nid);
300b3f6008fSTakashi Iwai 	if ((smart51_enabled(codec) && is_smart51_pins(codec, nid))
3011564b287SLydia Wang 	    || ((no_presence || present)
3021564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
303f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
304f5271101SLydia Wang 		parm = AC_PWRST_D0;
305f5271101SLydia Wang 	} else
306f5271101SLydia Wang 		parm = AC_PWRST_D3;
307f5271101SLydia Wang 
308054d867eSTakashi Iwai 	update_power_state(codec, nid, parm);
309f5271101SLydia Wang }
310f5271101SLydia Wang 
31124088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
31224088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
31324088a58STakashi Iwai {
314dda415d4STakashi Iwai 	return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
31524088a58STakashi Iwai }
31624088a58STakashi Iwai 
31724088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
31824088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
31924088a58STakashi Iwai {
32024088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
32124088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
32224088a58STakashi Iwai 	ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
32324088a58STakashi Iwai 	return 0;
32424088a58STakashi Iwai }
32524088a58STakashi Iwai 
32624088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
32724088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
32824088a58STakashi Iwai {
32924088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
33024088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
33124088a58STakashi Iwai 	unsigned int val = !ucontrol->value.enumerated.item[0];
33224088a58STakashi Iwai 
33324088a58STakashi Iwai 	if (val == spec->no_pin_power_ctl)
33424088a58STakashi Iwai 		return 0;
33524088a58STakashi Iwai 	spec->no_pin_power_ctl = val;
33624088a58STakashi Iwai 	set_widgets_power_state(codec);
337e9d010c2STakashi Iwai 	analog_low_current_mode(codec);
33824088a58STakashi Iwai 	return 1;
33924088a58STakashi Iwai }
34024088a58STakashi Iwai 
341b3f6008fSTakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = {
342b3f6008fSTakashi Iwai 	{
34324088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
34424088a58STakashi Iwai 	.name = "Dynamic Power-Control",
34524088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
34624088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
34724088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
348b3f6008fSTakashi Iwai 	},
349b3f6008fSTakashi Iwai 	{} /* terminator */
35024088a58STakashi Iwai };
35124088a58STakashi Iwai 
35224088a58STakashi Iwai 
353f5271101SLydia Wang /* check AA path's mute status */
354ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec)
355ada509ecSTakashi Iwai {
356ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
357ada509ecSTakashi Iwai 	const struct hda_amp_list *p;
358ada509ecSTakashi Iwai 	int i, ch, v;
359ada509ecSTakashi Iwai 
360b3f6008fSTakashi Iwai 	for (i = 0; i < spec->gen.num_loopbacks; i++) {
361b3f6008fSTakashi Iwai 		p = &spec->gen.loopback_list[i];
362ada509ecSTakashi Iwai 		for (ch = 0; ch < 2; ch++) {
363ada509ecSTakashi Iwai 			v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
364ada509ecSTakashi Iwai 						   p->idx);
365ada509ecSTakashi Iwai 			if (!(v & HDA_AMP_MUTE) && v > 0)
366ada509ecSTakashi Iwai 				return false;
367f5271101SLydia Wang 		}
368f5271101SLydia Wang 	}
369ada509ecSTakashi Iwai 	return true;
370f5271101SLydia Wang }
371f5271101SLydia Wang 
372f5271101SLydia Wang /* enter/exit analog low-current mode */
373e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force)
374f5271101SLydia Wang {
375f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
376ada509ecSTakashi Iwai 	bool enable;
377ada509ecSTakashi Iwai 	unsigned int verb, parm;
378f5271101SLydia Wang 
379e9d010c2STakashi Iwai 	if (spec->no_pin_power_ctl)
380e9d010c2STakashi Iwai 		enable = false;
381e9d010c2STakashi Iwai 	else
382b3f6008fSTakashi Iwai 		enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
383e9d010c2STakashi Iwai 	if (enable == spec->alc_mode && !force)
384e9d010c2STakashi Iwai 		return;
385e9d010c2STakashi Iwai 	spec->alc_mode = enable;
386f5271101SLydia Wang 
387f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
388f5271101SLydia Wang 	switch (spec->codec_type) {
389f5271101SLydia Wang 	case VT1708B_8CH:
390f5271101SLydia Wang 	case VT1708B_4CH:
391f5271101SLydia Wang 		verb = 0xf70;
392f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
393f5271101SLydia Wang 		break;
394f5271101SLydia Wang 	case VT1708S:
395eb7188caSLydia Wang 	case VT1718S:
396f3db423dSLydia Wang 	case VT1716S:
397f5271101SLydia Wang 		verb = 0xf73;
398f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
399f5271101SLydia Wang 		break;
400f5271101SLydia Wang 	case VT1702:
401f5271101SLydia Wang 		verb = 0xf73;
402f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
403f5271101SLydia Wang 		break;
40425eaba2fSLydia Wang 	case VT2002P:
405ab6734e7SLydia Wang 	case VT1812:
40611890956SLydia Wang 	case VT1802:
40725eaba2fSLydia Wang 		verb = 0xf93;
40825eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
40925eaba2fSLydia Wang 		break;
41043737e0aSLydia Wang 	case VT1705CF:
4116121b84aSLydia Wang 	case VT1808:
41243737e0aSLydia Wang 		verb = 0xf82;
41343737e0aSLydia Wang 		parm = enable ? 0x00 : 0xe0;  /* 0x00: 4/40x, 0xe0: 1x */
41443737e0aSLydia Wang 		break;
415f5271101SLydia Wang 	default:
416f5271101SLydia Wang 		return;		/* other codecs are not supported */
417f5271101SLydia Wang 	}
418f5271101SLydia Wang 	/* send verb */
419f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
420f5271101SLydia Wang }
421f5271101SLydia Wang 
422e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec)
423e9d010c2STakashi Iwai {
424e9d010c2STakashi Iwai 	return __analog_low_current_mode(codec, false);
425e9d010c2STakashi Iwai }
426e9d010c2STakashi Iwai 
427c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
428c577b8a1SJoseph Chan {
429c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
4305b0cb1d8SJaroslav Kysela 	int err, i;
431c577b8a1SJoseph Chan 
432b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_controls(codec);
433b3f6008fSTakashi Iwai 	if (err < 0)
434b3f6008fSTakashi Iwai 		return err;
435b3f6008fSTakashi Iwai 
43624088a58STakashi Iwai 	if (spec->set_widgets_power_state)
437b3f6008fSTakashi Iwai 		spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum;
43824088a58STakashi Iwai 
439c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
440c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
441c577b8a1SJoseph Chan 		if (err < 0)
442c577b8a1SJoseph Chan 			return err;
443c577b8a1SJoseph Chan 	}
444c577b8a1SJoseph Chan 
445c577b8a1SJoseph Chan 	return 0;
446c577b8a1SJoseph Chan }
447c577b8a1SJoseph Chan 
448b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
449b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
450b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
451b3f6008fSTakashi Iwai 				  int action)
452c577b8a1SJoseph Chan {
453b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
454b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
455c577b8a1SJoseph Chan }
456c577b8a1SJoseph Chan 
457c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
458c577b8a1SJoseph Chan {
459c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
460c577b8a1SJoseph Chan 
461c577b8a1SJoseph Chan 	if (!spec)
462c577b8a1SJoseph Chan 		return;
463c577b8a1SJoseph Chan 
464b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
465b3f6008fSTakashi Iwai 	snd_hda_gen_spec_free(&spec->gen);
466a86a88eaSTakashi Iwai 	kfree(spec);
467c577b8a1SJoseph Chan }
468c577b8a1SJoseph Chan 
4692a43952aSTakashi Iwai #ifdef CONFIG_PM
47068cb2b55STakashi Iwai static int via_suspend(struct hda_codec *codec)
4711f2e99feSLydia Wang {
4721f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
473b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
47494c142a1SDavid Henningsson 
47594c142a1SDavid Henningsson 	if (spec->codec_type == VT1802) {
47694c142a1SDavid Henningsson 		/* Fix pop noise on headphones */
47794c142a1SDavid Henningsson 		int i;
478b3f6008fSTakashi Iwai 		for (i = 0; i < spec->gen.autocfg.hp_outs; i++)
479b3f6008fSTakashi Iwai 			snd_hda_set_pin_ctl(codec, spec->gen.autocfg.hp_pins[i], 0);
48094c142a1SDavid Henningsson 	}
48194c142a1SDavid Henningsson 
4821f2e99feSLydia Wang 	return 0;
4831f2e99feSLydia Wang }
4841f2e99feSLydia Wang #endif
4851f2e99feSLydia Wang 
48683012a7cSTakashi Iwai #ifdef CONFIG_PM
487cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
488cb53c626STakashi Iwai {
489cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
490b3f6008fSTakashi Iwai 	set_widgets_power_state(codec);
491b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
492b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
493b3f6008fSTakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
494cb53c626STakashi Iwai }
495cb53c626STakashi Iwai #endif
496cb53c626STakashi Iwai 
497c577b8a1SJoseph Chan /*
498c577b8a1SJoseph Chan  */
4995d41762aSTakashi Iwai 
5005d41762aSTakashi Iwai static int via_init(struct hda_codec *codec);
5015d41762aSTakashi Iwai 
50290dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
503c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
504b3f6008fSTakashi Iwai 	.build_pcms = snd_hda_gen_build_pcms,
505c577b8a1SJoseph Chan 	.init = via_init,
506c577b8a1SJoseph Chan 	.free = via_free,
5074e2d16d3SDavid Henningsson 	.unsol_event = snd_hda_jack_unsol_event,
5082a43952aSTakashi Iwai #ifdef CONFIG_PM
5091f2e99feSLydia Wang 	.suspend = via_suspend,
510cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
511cb53c626STakashi Iwai #endif
512c577b8a1SJoseph Chan };
513c577b8a1SJoseph Chan 
5144a79616dSTakashi Iwai 
515b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = {
516b3f6008fSTakashi Iwai 	/* power down jack detect function */
517b3f6008fSTakashi Iwai 	{0x1, 0xf81, 0x1},
518b3f6008fSTakashi Iwai 	{ }
5194a79616dSTakashi Iwai };
52076d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
52176d9b0ddSHarald Welte {
52276d9b0ddSHarald Welte 	unsigned int def_conf;
52376d9b0ddSHarald Welte 	unsigned char seqassoc;
52476d9b0ddSHarald Welte 
5252f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
52676d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
52776d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
52882ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
52982ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
53076d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
5312f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
53276d9b0ddSHarald Welte 	}
53376d9b0ddSHarald Welte 
53476d9b0ddSHarald Welte 	return;
53576d9b0ddSHarald Welte }
53676d9b0ddSHarald Welte 
537e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
5381f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
5391f2e99feSLydia Wang {
5401f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
5411f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
5421f2e99feSLydia Wang 
5431f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
5441f2e99feSLydia Wang 		return 0;
545e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
5461f2e99feSLydia Wang 	return 0;
5471f2e99feSLydia Wang }
5481f2e99feSLydia Wang 
549e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
5501f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
5511f2e99feSLydia Wang {
5521f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
5531f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
554187d333eSTakashi Iwai 	int val;
5551f2e99feSLydia Wang 
5561f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
5571f2e99feSLydia Wang 		return 0;
558187d333eSTakashi Iwai 	val = !!ucontrol->value.integer.value[0];
559187d333eSTakashi Iwai 	if (spec->vt1708_jack_detect == val)
560187d333eSTakashi Iwai 		return 0;
561187d333eSTakashi Iwai 	spec->vt1708_jack_detect = val;
562b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
563187d333eSTakashi Iwai 	return 1;
5641f2e99feSLydia Wang }
5651f2e99feSLydia Wang 
566b3f6008fSTakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = {
567b3f6008fSTakashi Iwai 	{
5681f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5691f2e99feSLydia Wang 	.name = "Jack Detect",
5701f2e99feSLydia Wang 	.count = 1,
5711f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
572e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
573e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
574b3f6008fSTakashi Iwai 	},
575b3f6008fSTakashi Iwai 	{} /* terminator */
5761f2e99feSLydia Wang };
5771f2e99feSLydia Wang 
578b3f6008fSTakashi Iwai static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl)
5794e2d16d3SDavid Henningsson {
5804e2d16d3SDavid Henningsson 	set_widgets_power_state(codec);
581b3f6008fSTakashi Iwai 	snd_hda_gen_hp_automute(codec, tbl);
582b3f6008fSTakashi Iwai }
583b3f6008fSTakashi Iwai 
584b3f6008fSTakashi Iwai static void via_line_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl)
585b3f6008fSTakashi Iwai {
586b3f6008fSTakashi Iwai 	set_widgets_power_state(codec);
587b3f6008fSTakashi Iwai 	snd_hda_gen_line_automute(codec, tbl);
5884e2d16d3SDavid Henningsson }
5894e2d16d3SDavid Henningsson 
5904e2d16d3SDavid Henningsson static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_tbl *tbl)
5914e2d16d3SDavid Henningsson {
5924e2d16d3SDavid Henningsson 	set_widgets_power_state(codec);
5934e2d16d3SDavid Henningsson }
5944e2d16d3SDavid Henningsson 
595b3f6008fSTakashi Iwai #define VIA_JACK_EVENT	(HDA_GEN_LAST_EVENT + 1)
596b3f6008fSTakashi Iwai 
597b3f6008fSTakashi Iwai static void via_set_jack_unsol_events(struct hda_codec *codec)
5984a918ffeSTakashi Iwai {
5994a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
600b3f6008fSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->gen.autocfg;
601b3f6008fSTakashi Iwai 	hda_nid_t pin;
6024a918ffeSTakashi Iwai 	int i;
6034a918ffeSTakashi Iwai 
604b3f6008fSTakashi Iwai 	spec->gen.hp_automute_hook = via_hp_automute;
6054a918ffeSTakashi Iwai 	if (cfg->speaker_pins[0])
606b3f6008fSTakashi Iwai 		spec->gen.line_automute_hook = via_line_automute;
6074e2d16d3SDavid Henningsson 
6084a918ffeSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
609b3f6008fSTakashi Iwai 		pin = cfg->line_out_pins[i];
610b3f6008fSTakashi Iwai 		if (pin && !snd_hda_jack_tbl_get(codec, pin) &&
611b3f6008fSTakashi Iwai 		    is_jack_detectable(codec, pin))
612b3f6008fSTakashi Iwai 			snd_hda_jack_detect_enable_callback(codec, pin,
6134e2d16d3SDavid Henningsson 							    VIA_JACK_EVENT,
6144e2d16d3SDavid Henningsson 							    via_jack_powerstate_event);
6154a918ffeSTakashi Iwai 	}
616b3f6008fSTakashi Iwai 
617b3f6008fSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
618b3f6008fSTakashi Iwai 		pin = cfg->line_out_pins[i];
619b3f6008fSTakashi Iwai 		if (pin && !snd_hda_jack_tbl_get(codec, pin) &&
620b3f6008fSTakashi Iwai 		    is_jack_detectable(codec, pin))
621b3f6008fSTakashi Iwai 			snd_hda_jack_detect_enable_callback(codec, pin,
622b3f6008fSTakashi Iwai 							    VIA_JACK_EVENT,
623b3f6008fSTakashi Iwai 							    via_jack_powerstate_event);
624b3f6008fSTakashi Iwai 	}
625b3f6008fSTakashi Iwai }
626b3f6008fSTakashi Iwai 
627b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec)
628b3f6008fSTakashi Iwai {
629b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
630b3f6008fSTakashi Iwai 	int err;
631b3f6008fSTakashi Iwai 
632b3f6008fSTakashi Iwai 	err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
633b3f6008fSTakashi Iwai 	if (err < 0)
634b3f6008fSTakashi Iwai 		return err;
635b3f6008fSTakashi Iwai 
636b3f6008fSTakashi Iwai 	err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
637b3f6008fSTakashi Iwai 	if (err < 0)
638b3f6008fSTakashi Iwai 		return err;
639b3f6008fSTakashi Iwai 
640b3f6008fSTakashi Iwai 	via_set_jack_unsol_events(codec);
641b3f6008fSTakashi Iwai 	return 0;
6424a918ffeSTakashi Iwai }
6434a918ffeSTakashi Iwai 
6445d41762aSTakashi Iwai static int via_init(struct hda_codec *codec)
6455d41762aSTakashi Iwai {
6465d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
6475d41762aSTakashi Iwai 	int i;
6485d41762aSTakashi Iwai 
6495d41762aSTakashi Iwai 	for (i = 0; i < spec->num_iverbs; i++)
6505d41762aSTakashi Iwai 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
6515d41762aSTakashi Iwai 
652e9d010c2STakashi Iwai 	/* init power states */
653e9d010c2STakashi Iwai 	set_widgets_power_state(codec);
654e9d010c2STakashi Iwai 	__analog_low_current_mode(codec, true);
655e9d010c2STakashi Iwai 
656b3f6008fSTakashi Iwai 	snd_hda_gen_init(codec);
65711890956SLydia Wang 
658b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
65925eaba2fSLydia Wang 
660c577b8a1SJoseph Chan 	return 0;
661c577b8a1SJoseph Chan }
662c577b8a1SJoseph Chan 
663f672f65aSDavid Henningsson static int vt1708_build_controls(struct hda_codec *codec)
664f672f65aSDavid Henningsson {
665f672f65aSDavid Henningsson 	/* In order not to create "Phantom Jack" controls,
666f672f65aSDavid Henningsson 	   temporary enable jackpoll */
667f672f65aSDavid Henningsson 	int err;
668f672f65aSDavid Henningsson 	int old_interval = codec->jackpoll_interval;
669f672f65aSDavid Henningsson 	codec->jackpoll_interval = msecs_to_jiffies(100);
670f672f65aSDavid Henningsson 	err = via_build_controls(codec);
671f672f65aSDavid Henningsson 	codec->jackpoll_interval = old_interval;
672f672f65aSDavid Henningsson 	return err;
673f672f65aSDavid Henningsson }
674f672f65aSDavid Henningsson 
675b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec)
676337b9d02STakashi Iwai {
677337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
678b3f6008fSTakashi Iwai 	int i, err;
679337b9d02STakashi Iwai 
680b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_pcms(codec);
681b3f6008fSTakashi Iwai 	if (err < 0 || codec->vendor_id != 0x11061708)
682b3f6008fSTakashi Iwai 		return err;
683b3f6008fSTakashi Iwai 
684b3f6008fSTakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
685b3f6008fSTakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
686b3f6008fSTakashi Iwai 	 * disable the 24bit format, so far.
687b3f6008fSTakashi Iwai 	 */
688b3f6008fSTakashi Iwai 	for (i = 0; i < codec->num_pcms; i++) {
689b3f6008fSTakashi Iwai 		struct hda_pcm *info = &spec->gen.pcm_rec[i];
690b3f6008fSTakashi Iwai 		if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
691b3f6008fSTakashi Iwai 		    info->pcm_type != HDA_PCM_TYPE_AUDIO)
692b3f6008fSTakashi Iwai 			continue;
693b3f6008fSTakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats =
694b3f6008fSTakashi Iwai 			SNDRV_PCM_FMTBIT_S16_LE;
695337b9d02STakashi Iwai 	}
696b3f6008fSTakashi Iwai 
6971c55d521STakashi Iwai 	return 0;
698337b9d02STakashi Iwai }
699337b9d02STakashi Iwai 
700c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
701c577b8a1SJoseph Chan {
702c577b8a1SJoseph Chan 	struct via_spec *spec;
703c577b8a1SJoseph Chan 	int err;
704c577b8a1SJoseph Chan 
705c577b8a1SJoseph Chan 	/* create a codec specific record */
7065b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
707c577b8a1SJoseph Chan 	if (spec == NULL)
708c577b8a1SJoseph Chan 		return -ENOMEM;
709c577b8a1SJoseph Chan 
710b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x17;
711b3f6008fSTakashi Iwai 
712b3f6008fSTakashi Iwai 	/* set jackpoll_interval while parsing the codec */
713b3f6008fSTakashi Iwai 	codec->jackpoll_interval = msecs_to_jiffies(100);
714b3f6008fSTakashi Iwai 	spec->vt1708_jack_detect = 1;
715b3f6008fSTakashi Iwai 
716b3f6008fSTakashi Iwai 	/* don't support the input jack switching due to lack of unsol event */
717b3f6008fSTakashi Iwai 	/* (it may work with polling, though, but it needs testing) */
718b3f6008fSTakashi Iwai 	spec->gen.suppress_auto_mic = 1;
719620e2b28STakashi Iwai 
72012daef65STakashi Iwai 	/* Add HP and CD pin config connect bit re-config action */
72112daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
72212daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
72312daef65STakashi Iwai 
724c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
72512daef65STakashi Iwai 	err = via_parse_auto_config(codec);
726c577b8a1SJoseph Chan 	if (err < 0) {
727c577b8a1SJoseph Chan 		via_free(codec);
728c577b8a1SJoseph Chan 		return err;
729c577b8a1SJoseph Chan 	}
730c577b8a1SJoseph Chan 
73112daef65STakashi Iwai 	/* add jack detect on/off control */
732b3f6008fSTakashi Iwai 	spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl;
733c577b8a1SJoseph Chan 
734e322a36dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
735e322a36dSLydia Wang 
736c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
737f672f65aSDavid Henningsson 	codec->patch_ops.build_controls = vt1708_build_controls;
738b3f6008fSTakashi Iwai 	codec->patch_ops.build_pcms = vt1708_build_pcms;
739c577b8a1SJoseph Chan 
740b3f6008fSTakashi Iwai 	/* clear jackpoll_interval again; it's set dynamically */
741b3f6008fSTakashi Iwai 	codec->jackpoll_interval = 0;
742b3f6008fSTakashi Iwai 
743c577b8a1SJoseph Chan 	return 0;
744c577b8a1SJoseph Chan }
745c577b8a1SJoseph Chan 
746ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec)
747c577b8a1SJoseph Chan {
748c577b8a1SJoseph Chan 	struct via_spec *spec;
749c577b8a1SJoseph Chan 	int err;
750c577b8a1SJoseph Chan 
751c577b8a1SJoseph Chan 	/* create a codec specific record */
7525b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
753c577b8a1SJoseph Chan 	if (spec == NULL)
754c577b8a1SJoseph Chan 		return -ENOMEM;
755c577b8a1SJoseph Chan 
756b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x18;
757620e2b28STakashi Iwai 
75812daef65STakashi Iwai 	err = via_parse_auto_config(codec);
759c577b8a1SJoseph Chan 	if (err < 0) {
760c577b8a1SJoseph Chan 		via_free(codec);
761c577b8a1SJoseph Chan 		return err;
762c577b8a1SJoseph Chan 	}
763c577b8a1SJoseph Chan 
764c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
765c577b8a1SJoseph Chan 
766f7278fd0SJosepch Chan 	return 0;
767f7278fd0SJosepch Chan }
768f7278fd0SJosepch Chan 
7693e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
7703e95b9abSLydia Wang {
7713e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
7723e95b9abSLydia Wang 	int imux_is_smixer;
7733e95b9abSLydia Wang 	unsigned int parm;
7743e95b9abSLydia Wang 	int is_8ch = 0;
775bc92df7fSLydia Wang 	if ((spec->codec_type != VT1708B_4CH) &&
776bc92df7fSLydia Wang 	    (codec->vendor_id != 0x11064397))
7773e95b9abSLydia Wang 		is_8ch = 1;
7783e95b9abSLydia Wang 
7793e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
7803e95b9abSLydia Wang 	imux_is_smixer =
7813e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
7823e95b9abSLydia Wang 	 == ((spec->codec_type == VT1708S) ? 5 : 0));
7833e95b9abSLydia Wang 	/* inputs */
7843e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
7853e95b9abSLydia Wang 	parm = AC_PWRST_D3;
7863e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
7873e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
7883e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
7893e95b9abSLydia Wang 	if (imux_is_smixer)
7903e95b9abSLydia Wang 		parm = AC_PWRST_D0;
7913e95b9abSLydia Wang 	/* SW0 (17h), AIW 0/1 (13h/14h) */
792054d867eSTakashi Iwai 	update_power_state(codec, 0x17, parm);
793054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
794054d867eSTakashi Iwai 	update_power_state(codec, 0x14, parm);
7953e95b9abSLydia Wang 
7963e95b9abSLydia Wang 	/* outputs */
7973e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
7983e95b9abSLydia Wang 	parm = AC_PWRST_D3;
7993e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
800b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
8013e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
802054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
803054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
8043e95b9abSLydia Wang 
8053e95b9abSLydia Wang 	/* PW6 (22h), SW2 (26h), AOW2 (24h) */
8063e95b9abSLydia Wang 	if (is_8ch) {
8073e95b9abSLydia Wang 		parm = AC_PWRST_D3;
8083e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
809b3f6008fSTakashi Iwai 		if (smart51_enabled(codec))
8103e95b9abSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
811054d867eSTakashi Iwai 		update_power_state(codec, 0x26, parm);
812054d867eSTakashi Iwai 		update_power_state(codec, 0x24, parm);
813bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397) {
814bc92df7fSLydia Wang 		/* PW7(23h), SW2(27h), AOW2(25h) */
815bc92df7fSLydia Wang 		parm = AC_PWRST_D3;
816bc92df7fSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
817b3f6008fSTakashi Iwai 		if (smart51_enabled(codec))
818bc92df7fSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
819054d867eSTakashi Iwai 		update_power_state(codec, 0x27, parm);
820054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
8213e95b9abSLydia Wang 	}
8223e95b9abSLydia Wang 
8233e95b9abSLydia Wang 	/* PW 3/4/7 (1ch/1dh/23h) */
8243e95b9abSLydia Wang 	parm = AC_PWRST_D3;
8253e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
8263e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
8273e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
8283e95b9abSLydia Wang 	if (is_8ch)
8293e95b9abSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
8303e95b9abSLydia Wang 
8313e95b9abSLydia Wang 	/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
832054d867eSTakashi Iwai 	update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
833054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
8343e95b9abSLydia Wang 	if (is_8ch) {
835054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
836054d867eSTakashi Iwai 		update_power_state(codec, 0x27, parm);
837b3f6008fSTakashi Iwai 	} else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled)
838054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
8393e95b9abSLydia Wang }
8403e95b9abSLydia Wang 
841518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
842ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec)
843f7278fd0SJosepch Chan {
844f7278fd0SJosepch Chan 	struct via_spec *spec;
845f7278fd0SJosepch Chan 	int err;
846f7278fd0SJosepch Chan 
847518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
848518bf3baSLydia Wang 		return patch_vt1708S(codec);
849ddd304d8STakashi Iwai 
850f7278fd0SJosepch Chan 	/* create a codec specific record */
8515b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
852f7278fd0SJosepch Chan 	if (spec == NULL)
853f7278fd0SJosepch Chan 		return -ENOMEM;
854f7278fd0SJosepch Chan 
855b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
856620e2b28STakashi Iwai 
857f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
85812daef65STakashi Iwai 	err = via_parse_auto_config(codec);
859f7278fd0SJosepch Chan 	if (err < 0) {
860f7278fd0SJosepch Chan 		via_free(codec);
861f7278fd0SJosepch Chan 		return err;
862f7278fd0SJosepch Chan 	}
863f7278fd0SJosepch Chan 
864f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
865f7278fd0SJosepch Chan 
8663e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
8673e95b9abSLydia Wang 
868f7278fd0SJosepch Chan 	return 0;
869f7278fd0SJosepch Chan }
870f7278fd0SJosepch Chan 
871d949cac1SHarald Welte /* Patch for VT1708S */
872096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = {
873d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
874d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
875bc7e7e5cSLydia Wang 	/* don't bybass mixer */
876bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
877d949cac1SHarald Welte 	{ }
878d949cac1SHarald Welte };
879d949cac1SHarald Welte 
8806369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
8816369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
8826369bcfcSLydia Wang {
8836369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
8846369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
8856369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
8866369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
8876369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
8886369bcfcSLydia Wang }
8896369bcfcSLydia Wang 
890d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
891d949cac1SHarald Welte {
892d949cac1SHarald Welte 	struct via_spec *spec;
893d949cac1SHarald Welte 	int err;
894d949cac1SHarald Welte 
895d949cac1SHarald Welte 	/* create a codec specific record */
8965b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
897d949cac1SHarald Welte 	if (spec == NULL)
898d949cac1SHarald Welte 		return -ENOMEM;
899d949cac1SHarald Welte 
900b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
901d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
902d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
903620e2b28STakashi Iwai 
904518bf3baSLydia Wang 	/* correct names for VT1708BCE */
905518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
906518bf3baSLydia Wang 		kfree(codec->chip_name);
907518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
908518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
909518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
910518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
911970f630fSLydia Wang 	}
912bc92df7fSLydia Wang 	/* correct names for VT1705 */
913bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)	{
914bc92df7fSLydia Wang 		kfree(codec->chip_name);
915bc92df7fSLydia Wang 		codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
916bc92df7fSLydia Wang 		snprintf(codec->bus->card->mixername,
917bc92df7fSLydia Wang 			 sizeof(codec->bus->card->mixername),
918bc92df7fSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
919bc92df7fSLydia Wang 	}
920b3f6008fSTakashi Iwai 
921b3f6008fSTakashi Iwai 	/* automatic parse from the BIOS config */
922b3f6008fSTakashi Iwai 	err = via_parse_auto_config(codec);
923b3f6008fSTakashi Iwai 	if (err < 0) {
924b3f6008fSTakashi Iwai 		via_free(codec);
925b3f6008fSTakashi Iwai 		return err;
926b3f6008fSTakashi Iwai 	}
927b3f6008fSTakashi Iwai 
928b3f6008fSTakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
929b3f6008fSTakashi Iwai 
930b3f6008fSTakashi Iwai 	codec->patch_ops = via_patch_ops;
931b3f6008fSTakashi Iwai 
9323e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
933d949cac1SHarald Welte 	return 0;
934d949cac1SHarald Welte }
935d949cac1SHarald Welte 
936d949cac1SHarald Welte /* Patch for VT1702 */
937d949cac1SHarald Welte 
938096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = {
939bc7e7e5cSLydia Wang 	/* mixer enable */
940bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
941bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
942bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
943d949cac1SHarald Welte 	{ }
944d949cac1SHarald Welte };
945d949cac1SHarald Welte 
9463e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec)
9473e95b9abSLydia Wang {
9483e95b9abSLydia Wang 	int imux_is_smixer =
9493e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
9503e95b9abSLydia Wang 	unsigned int parm;
9513e95b9abSLydia Wang 	/* inputs */
9523e95b9abSLydia Wang 	/* PW 1/2/5 (14h/15h/18h) */
9533e95b9abSLydia Wang 	parm = AC_PWRST_D3;
9543e95b9abSLydia Wang 	set_pin_power_state(codec, 0x14, &parm);
9553e95b9abSLydia Wang 	set_pin_power_state(codec, 0x15, &parm);
9563e95b9abSLydia Wang 	set_pin_power_state(codec, 0x18, &parm);
9573e95b9abSLydia Wang 	if (imux_is_smixer)
9583e95b9abSLydia Wang 		parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
9593e95b9abSLydia Wang 	/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
960054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
961054d867eSTakashi Iwai 	update_power_state(codec, 0x12, parm);
962054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
963054d867eSTakashi Iwai 	update_power_state(codec, 0x20, parm);
9643e95b9abSLydia Wang 
9653e95b9abSLydia Wang 	/* outputs */
9663e95b9abSLydia Wang 	/* PW 3/4 (16h/17h) */
9673e95b9abSLydia Wang 	parm = AC_PWRST_D3;
9683e95b9abSLydia Wang 	set_pin_power_state(codec, 0x17, &parm);
9693e95b9abSLydia Wang 	set_pin_power_state(codec, 0x16, &parm);
9703e95b9abSLydia Wang 	/* MW0 (1ah), AOW 0/1 (10h/1dh) */
971054d867eSTakashi Iwai 	update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm);
972054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
973054d867eSTakashi Iwai 	update_power_state(codec, 0x1d, parm);
9743e95b9abSLydia Wang }
9753e95b9abSLydia Wang 
976d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
977d949cac1SHarald Welte {
978d949cac1SHarald Welte 	struct via_spec *spec;
979d949cac1SHarald Welte 	int err;
980d949cac1SHarald Welte 
981d949cac1SHarald Welte 	/* create a codec specific record */
9825b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
983d949cac1SHarald Welte 	if (spec == NULL)
984d949cac1SHarald Welte 		return -ENOMEM;
985d949cac1SHarald Welte 
986b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x1a;
987620e2b28STakashi Iwai 
98812daef65STakashi Iwai 	/* limit AA path volume to 0 dB */
98912daef65STakashi Iwai 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
99012daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
99112daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
99212daef65STakashi Iwai 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
99312daef65STakashi Iwai 				  (1 << AC_AMPCAP_MUTE_SHIFT));
99412daef65STakashi Iwai 
995d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
99612daef65STakashi Iwai 	err = via_parse_auto_config(codec);
997d949cac1SHarald Welte 	if (err < 0) {
998d949cac1SHarald Welte 		via_free(codec);
999d949cac1SHarald Welte 		return err;
1000d949cac1SHarald Welte 	}
1001d949cac1SHarald Welte 
1002096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
1003d949cac1SHarald Welte 
1004d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
1005d949cac1SHarald Welte 
10063e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
1007d949cac1SHarald Welte 	return 0;
1008d949cac1SHarald Welte }
1009d949cac1SHarald Welte 
1010eb7188caSLydia Wang /* Patch for VT1718S */
1011eb7188caSLydia Wang 
1012096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = {
10134ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
10144ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
1015eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
1016eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
10175d41762aSTakashi Iwai 
1018eb7188caSLydia Wang 	{ }
1019eb7188caSLydia Wang };
1020eb7188caSLydia Wang 
10213e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
10223e95b9abSLydia Wang {
10233e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
10243e95b9abSLydia Wang 	int imux_is_smixer;
10256162552bSTakashi Iwai 	unsigned int parm, parm2;
10263e95b9abSLydia Wang 	/* MUX6 (1eh) = stereo mixer */
10273e95b9abSLydia Wang 	imux_is_smixer =
10283e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
10293e95b9abSLydia Wang 	/* inputs */
10303e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
10313e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10323e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
10333e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
10343e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
10353e95b9abSLydia Wang 	if (imux_is_smixer)
10363e95b9abSLydia Wang 		parm = AC_PWRST_D0;
10373e95b9abSLydia Wang 	/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
1038054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
1039054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
1040054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1041054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
10423e95b9abSLydia Wang 
10433e95b9abSLydia Wang 	/* outputs */
10443e95b9abSLydia Wang 	/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
10453e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10463e95b9abSLydia Wang 	set_pin_power_state(codec, 0x27, &parm);
1047054d867eSTakashi Iwai 	update_power_state(codec, 0x1a, parm);
10486162552bSTakashi Iwai 	parm2 = parm; /* for pin 0x0b */
10493e95b9abSLydia Wang 
10503e95b9abSLydia Wang 	/* PW2 (26h), AOW2 (ah) */
10513e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10523e95b9abSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
1053b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
10543e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
1055054d867eSTakashi Iwai 	update_power_state(codec, 0xa, parm);
10563e95b9abSLydia Wang 
10573e95b9abSLydia Wang 	/* PW0 (24h), AOW0 (8h) */
10583e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10593e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
1060b3f6008fSTakashi Iwai 	if (!spec->gen.indep_hp_enabled) /* check for redirected HP */
10613e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
1062054d867eSTakashi Iwai 	update_power_state(codec, 0x8, parm);
1063b3f6008fSTakashi Iwai 	if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3)
10646162552bSTakashi Iwai 		parm = parm2;
10656162552bSTakashi Iwai 	update_power_state(codec, 0xb, parm);
10663e95b9abSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
1067054d867eSTakashi Iwai 	update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm);
10683e95b9abSLydia Wang 
10693e95b9abSLydia Wang 	/* PW1 (25h), AOW1 (9h) */
10703e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10713e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
1072b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
10733e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
1074054d867eSTakashi Iwai 	update_power_state(codec, 0x9, parm);
10753e95b9abSLydia Wang 
1076b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled) {
10773e95b9abSLydia Wang 		/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
10783e95b9abSLydia Wang 		parm = AC_PWRST_D3;
10793e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
1080054d867eSTakashi Iwai 		update_power_state(codec, 0x1b, parm);
1081054d867eSTakashi Iwai 		update_power_state(codec, 0x34, parm);
1082054d867eSTakashi Iwai 		update_power_state(codec, 0xc, parm);
10833e95b9abSLydia Wang 	}
10843e95b9abSLydia Wang }
10853e95b9abSLydia Wang 
108630b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs
108730b45033STakashi Iwai  * This isn't listed from the raw info, but the chip has a secret connection.
108830b45033STakashi Iwai  */
108930b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec)
109030b45033STakashi Iwai {
109130b45033STakashi Iwai 	struct via_spec *spec = codec->spec;
109230b45033STakashi Iwai 	int i, nums;
109330b45033STakashi Iwai 	hda_nid_t conn[8];
109430b45033STakashi Iwai 	hda_nid_t nid;
109530b45033STakashi Iwai 
1096b3f6008fSTakashi Iwai 	if (!spec->gen.mixer_nid)
109730b45033STakashi Iwai 		return 0;
1098b3f6008fSTakashi Iwai 	nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
109930b45033STakashi Iwai 				       ARRAY_SIZE(conn) - 1);
110030b45033STakashi Iwai 	for (i = 0; i < nums; i++) {
110130b45033STakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
110230b45033STakashi Iwai 			return 0;
110330b45033STakashi Iwai 	}
110430b45033STakashi Iwai 
110530b45033STakashi Iwai 	/* find the primary DAC and add to the connection list */
110630b45033STakashi Iwai 	nid = codec->start_nid;
110730b45033STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, nid++) {
110830b45033STakashi Iwai 		unsigned int caps = get_wcaps(codec, nid);
110930b45033STakashi Iwai 		if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
111030b45033STakashi Iwai 		    !(caps & AC_WCAP_DIGITAL)) {
111130b45033STakashi Iwai 			conn[nums++] = nid;
111230b45033STakashi Iwai 			return snd_hda_override_conn_list(codec,
1113b3f6008fSTakashi Iwai 							  spec->gen.mixer_nid,
111430b45033STakashi Iwai 							  nums, conn);
111530b45033STakashi Iwai 		}
111630b45033STakashi Iwai 	}
111730b45033STakashi Iwai 	return 0;
111830b45033STakashi Iwai }
111930b45033STakashi Iwai 
112030b45033STakashi Iwai 
1121eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
1122eb7188caSLydia Wang {
1123eb7188caSLydia Wang 	struct via_spec *spec;
1124eb7188caSLydia Wang 	int err;
1125eb7188caSLydia Wang 
1126eb7188caSLydia Wang 	/* create a codec specific record */
11275b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1128eb7188caSLydia Wang 	if (spec == NULL)
1129eb7188caSLydia Wang 		return -ENOMEM;
1130eb7188caSLydia Wang 
1131b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1132d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1133d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
113430b45033STakashi Iwai 	add_secret_dac_path(codec);
1135620e2b28STakashi Iwai 
1136eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
113712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1138eb7188caSLydia Wang 	if (err < 0) {
1139eb7188caSLydia Wang 		via_free(codec);
1140eb7188caSLydia Wang 		return err;
1141eb7188caSLydia Wang 	}
1142eb7188caSLydia Wang 
1143096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
1144eb7188caSLydia Wang 
1145eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
1146eb7188caSLydia Wang 
11473e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1718S;
11483e95b9abSLydia Wang 
1149eb7188caSLydia Wang 	return 0;
1150eb7188caSLydia Wang }
1151f3db423dSLydia Wang 
1152f3db423dSLydia Wang /* Patch for VT1716S */
1153f3db423dSLydia Wang 
1154f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
1155f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
1156f3db423dSLydia Wang {
1157f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
1158f3db423dSLydia Wang 	uinfo->count = 1;
1159f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
1160f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
1161f3db423dSLydia Wang 	return 0;
1162f3db423dSLydia Wang }
1163f3db423dSLydia Wang 
1164f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
1165f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
1166f3db423dSLydia Wang {
1167f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1168f3db423dSLydia Wang 	int index = 0;
1169f3db423dSLydia Wang 
1170f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
1171f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
1172f3db423dSLydia Wang 	if (index != -1)
1173f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
1174f3db423dSLydia Wang 
1175f3db423dSLydia Wang 	return 0;
1176f3db423dSLydia Wang }
1177f3db423dSLydia Wang 
1178f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
1179f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
1180f3db423dSLydia Wang {
1181f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1182f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
1183f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
1184f3db423dSLydia Wang 
1185f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
1186f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
1187f3db423dSLydia Wang 	spec->dmic_enabled = index;
11883e95b9abSLydia Wang 	set_widgets_power_state(codec);
1189f3db423dSLydia Wang 	return 1;
1190f3db423dSLydia Wang }
1191f3db423dSLydia Wang 
119290dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
1193f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
1194f3db423dSLydia Wang 	{
1195f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1196f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
11975b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
1198f3db423dSLydia Wang 	 .count = 1,
1199f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
1200f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
1201f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
1202f3db423dSLydia Wang 	 },
1203f3db423dSLydia Wang 	{}			/* end */
1204f3db423dSLydia Wang };
1205f3db423dSLydia Wang 
1206f3db423dSLydia Wang 
1207f3db423dSLydia Wang /* mono-out mixer elements */
120890dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
1209f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
1210f3db423dSLydia Wang 	{ } /* end */
1211f3db423dSLydia Wang };
1212f3db423dSLydia Wang 
1213096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = {
1214f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
1215f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
1216f3db423dSLydia Wang 	/* don't bybass mixer */
1217f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
1218f3db423dSLydia Wang 	/* Enable mono output */
1219f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
1220f3db423dSLydia Wang 	{ }
1221f3db423dSLydia Wang };
1222f3db423dSLydia Wang 
12233e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
12243e95b9abSLydia Wang {
12253e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
12263e95b9abSLydia Wang 	int imux_is_smixer;
12273e95b9abSLydia Wang 	unsigned int parm;
12283e95b9abSLydia Wang 	unsigned int mono_out, present;
12293e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
12303e95b9abSLydia Wang 	imux_is_smixer =
12313e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0,
12323e95b9abSLydia Wang 			    AC_VERB_GET_CONNECT_SEL, 0x00) ==  5);
12333e95b9abSLydia Wang 	/* inputs */
12343e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
12353e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12363e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
12373e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
12383e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
12393e95b9abSLydia Wang 	if (imux_is_smixer)
12403e95b9abSLydia Wang 		parm = AC_PWRST_D0;
12413e95b9abSLydia Wang 	/* SW0 (17h), AIW0(13h) */
1242054d867eSTakashi Iwai 	update_power_state(codec, 0x17, parm);
1243054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
12443e95b9abSLydia Wang 
12453e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12463e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
12473e95b9abSLydia Wang 	/* PW11 (22h) */
12483e95b9abSLydia Wang 	if (spec->dmic_enabled)
12493e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
12503e95b9abSLydia Wang 	else
1251054d867eSTakashi Iwai 		update_power_state(codec, 0x22, AC_PWRST_D3);
12523e95b9abSLydia Wang 
12533e95b9abSLydia Wang 	/* SW2(26h), AIW1(14h) */
1254054d867eSTakashi Iwai 	update_power_state(codec, 0x26, parm);
1255054d867eSTakashi Iwai 	update_power_state(codec, 0x14, parm);
12563e95b9abSLydia Wang 
12573e95b9abSLydia Wang 	/* outputs */
12583e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
12593e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12603e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
12613e95b9abSLydia Wang 	/* Smart 5.1 PW2(1bh) */
1262b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
12633e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
1264054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
1265054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
12663e95b9abSLydia Wang 
12673e95b9abSLydia Wang 	/* PW7 (23h), SW3 (27h), AOW3 (25h) */
12683e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12693e95b9abSLydia Wang 	set_pin_power_state(codec, 0x23, &parm);
12703e95b9abSLydia Wang 	/* Smart 5.1 PW1(1ah) */
1271b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
12723e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
1273054d867eSTakashi Iwai 	update_power_state(codec, 0x27, parm);
12743e95b9abSLydia Wang 
12753e95b9abSLydia Wang 	/* Smart 5.1 PW5(1eh) */
1276b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
12773e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
1278054d867eSTakashi Iwai 	update_power_state(codec, 0x25, parm);
12793e95b9abSLydia Wang 
12803e95b9abSLydia Wang 	/* Mono out */
12813e95b9abSLydia Wang 	/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
12823e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x1c);
12833e95b9abSLydia Wang 
12843e95b9abSLydia Wang 	if (present)
12853e95b9abSLydia Wang 		mono_out = 0;
12863e95b9abSLydia Wang 	else {
12873e95b9abSLydia Wang 		present = snd_hda_jack_detect(codec, 0x1d);
1288b3f6008fSTakashi Iwai 		if (!spec->gen.indep_hp_enabled && present)
12893e95b9abSLydia Wang 			mono_out = 0;
12903e95b9abSLydia Wang 		else
12913e95b9abSLydia Wang 			mono_out = 1;
12923e95b9abSLydia Wang 	}
12933e95b9abSLydia Wang 	parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
1294054d867eSTakashi Iwai 	update_power_state(codec, 0x28, parm);
1295054d867eSTakashi Iwai 	update_power_state(codec, 0x29, parm);
1296054d867eSTakashi Iwai 	update_power_state(codec, 0x2a, parm);
12973e95b9abSLydia Wang 
12983e95b9abSLydia Wang 	/* PW 3/4 (1ch/1dh) */
12993e95b9abSLydia Wang 	parm = AC_PWRST_D3;
13003e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
13013e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
13023e95b9abSLydia Wang 	/* HP Independent Mode, power on AOW3 */
1303b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
1304054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
13053e95b9abSLydia Wang 
13063e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
13073e95b9abSLydia Wang 	/* MW0 (16h), AOW0 (10h) */
1308054d867eSTakashi Iwai 	update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
1309054d867eSTakashi Iwai 	update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm);
13103e95b9abSLydia Wang }
13113e95b9abSLydia Wang 
1312f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
1313f3db423dSLydia Wang {
1314f3db423dSLydia Wang 	struct via_spec *spec;
1315f3db423dSLydia Wang 	int err;
1316f3db423dSLydia Wang 
1317f3db423dSLydia Wang 	/* create a codec specific record */
13185b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1319f3db423dSLydia Wang 	if (spec == NULL)
1320f3db423dSLydia Wang 		return -ENOMEM;
1321f3db423dSLydia Wang 
1322b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
1323d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
1324d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
1325620e2b28STakashi Iwai 
1326f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
132712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1328f3db423dSLydia Wang 	if (err < 0) {
1329f3db423dSLydia Wang 		via_free(codec);
1330f3db423dSLydia Wang 		return err;
1331f3db423dSLydia Wang 	}
1332f3db423dSLydia Wang 
1333096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_init_verbs;
1334f3db423dSLydia Wang 
1335b3f6008fSTakashi Iwai 	spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer;
1336f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
1337f3db423dSLydia Wang 
1338f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
1339f3db423dSLydia Wang 
13403e95b9abSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
1341f3db423dSLydia Wang 	return 0;
1342f3db423dSLydia Wang }
134325eaba2fSLydia Wang 
134425eaba2fSLydia Wang /* for vt2002P */
134525eaba2fSLydia Wang 
1346096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = {
1347eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
1348eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
1349eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
1350eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
135125eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
135225eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
135325eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
135425eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
135525eaba2fSLydia Wang 	{ }
135625eaba2fSLydia Wang };
13574a918ffeSTakashi Iwai 
1358096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = {
135911890956SLydia Wang 	/* Enable Boost Volume backdoor */
136011890956SLydia Wang 	{0x1, 0xfb9, 0x24},
136111890956SLydia Wang 	/* Enable AOW0 to MW9 */
136211890956SLydia Wang 	{0x1, 0xfb8, 0x88},
136311890956SLydia Wang 	{ }
136411890956SLydia Wang };
136525eaba2fSLydia Wang 
13663e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
13673e95b9abSLydia Wang {
13683e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
13693e95b9abSLydia Wang 	int imux_is_smixer;
13703e95b9abSLydia Wang 	unsigned int parm;
13713e95b9abSLydia Wang 	unsigned int present;
13723e95b9abSLydia Wang 	/* MUX9 (1eh) = stereo mixer */
13733e95b9abSLydia Wang 	imux_is_smixer =
13743e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
13753e95b9abSLydia Wang 	/* inputs */
13763e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
13773e95b9abSLydia Wang 	parm = AC_PWRST_D3;
13783e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
13793e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
13803e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
13813e95b9abSLydia Wang 	parm = AC_PWRST_D0;
13823e95b9abSLydia Wang 	/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
1383054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
1384054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
1385054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1386054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
13873e95b9abSLydia Wang 
13883e95b9abSLydia Wang 	/* outputs */
13893e95b9abSLydia Wang 	/* AOW0 (8h)*/
1390054d867eSTakashi Iwai 	update_power_state(codec, 0x8, parm);
13913e95b9abSLydia Wang 
139211890956SLydia Wang 	if (spec->codec_type == VT1802) {
139311890956SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
139411890956SLydia Wang 		parm = AC_PWRST_D3;
139511890956SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
1396054d867eSTakashi Iwai 		update_power_state(codec, 0x18, parm);
1397054d867eSTakashi Iwai 		update_power_state(codec, 0x38, parm);
139811890956SLydia Wang 	} else {
13993e95b9abSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
14003e95b9abSLydia Wang 		parm = AC_PWRST_D3;
14013e95b9abSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
1402054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, parm);
1403054d867eSTakashi Iwai 		update_power_state(codec, 0x37, parm);
140411890956SLydia Wang 	}
14053e95b9abSLydia Wang 
140611890956SLydia Wang 	if (spec->codec_type == VT1802) {
140711890956SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
140811890956SLydia Wang 		parm = AC_PWRST_D3;
140911890956SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
1410054d867eSTakashi Iwai 		update_power_state(codec, 0x15, parm);
1411054d867eSTakashi Iwai 		update_power_state(codec, 0x35, parm);
141211890956SLydia Wang 	} else {
14133e95b9abSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
14143e95b9abSLydia Wang 		parm = AC_PWRST_D3;
14153e95b9abSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
1416054d867eSTakashi Iwai 		update_power_state(codec, 0x19, parm);
1417054d867eSTakashi Iwai 		update_power_state(codec, 0x35, parm);
141811890956SLydia Wang 	}
14193e95b9abSLydia Wang 
1420b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
1421054d867eSTakashi Iwai 		update_power_state(codec, 0x9, AC_PWRST_D0);
14223e95b9abSLydia Wang 
14233e95b9abSLydia Wang 	/* Class-D */
14243e95b9abSLydia Wang 	/* PW0 (24h), MW0(18h/14h), MUX0(34h) */
14253e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
14263e95b9abSLydia Wang 
14273e95b9abSLydia Wang 	parm = AC_PWRST_D3;
14283e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
14293e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
143011890956SLydia Wang 	if (spec->codec_type == VT1802)
1431054d867eSTakashi Iwai 		update_power_state(codec, 0x14, parm);
143211890956SLydia Wang 	else
1433054d867eSTakashi Iwai 		update_power_state(codec, 0x18, parm);
1434054d867eSTakashi Iwai 	update_power_state(codec, 0x34, parm);
14353e95b9abSLydia Wang 
14363e95b9abSLydia Wang 	/* Mono Out */
14373e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x26);
14383e95b9abSLydia Wang 
14393e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
144011890956SLydia Wang 	if (spec->codec_type == VT1802) {
144111890956SLydia Wang 		/* PW15 (33h), MW8(1ch), MUX8(3ch) */
1442054d867eSTakashi Iwai 		update_power_state(codec, 0x33, parm);
1443054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, parm);
1444054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, parm);
144511890956SLydia Wang 	} else {
14463e95b9abSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
1447054d867eSTakashi Iwai 		update_power_state(codec, 0x31, parm);
1448054d867eSTakashi Iwai 		update_power_state(codec, 0x17, parm);
1449054d867eSTakashi Iwai 		update_power_state(codec, 0x3b, parm);
145011890956SLydia Wang 	}
14513e95b9abSLydia Wang 	/* MW9 (21h) */
14523e95b9abSLydia Wang 	if (imux_is_smixer || !is_aa_path_mute(codec))
1453054d867eSTakashi Iwai 		update_power_state(codec, 0x21, AC_PWRST_D0);
14543e95b9abSLydia Wang 	else
1455054d867eSTakashi Iwai 		update_power_state(codec, 0x21, AC_PWRST_D3);
14563e95b9abSLydia Wang }
145725eaba2fSLydia Wang 
14584b527b65SDavid Henningsson /*
14594b527b65SDavid Henningsson  * pin fix-up
14604b527b65SDavid Henningsson  */
14614b527b65SDavid Henningsson enum {
14624b527b65SDavid Henningsson 	VIA_FIXUP_INTMIC_BOOST,
1463d5266125STakashi Iwai 	VIA_FIXUP_ASUS_G75,
14644b527b65SDavid Henningsson };
14654b527b65SDavid Henningsson 
14664b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec,
14674b527b65SDavid Henningsson 				  const struct hda_fixup *fix, int action)
14684b527b65SDavid Henningsson {
14694b527b65SDavid Henningsson 	if (action == HDA_FIXUP_ACT_PRE_PROBE)
14704b527b65SDavid Henningsson 		override_mic_boost(codec, 0x30, 0, 2, 40);
14714b527b65SDavid Henningsson }
14724b527b65SDavid Henningsson 
14734b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = {
14744b527b65SDavid Henningsson 	[VIA_FIXUP_INTMIC_BOOST] = {
14754b527b65SDavid Henningsson 		.type = HDA_FIXUP_FUNC,
14764b527b65SDavid Henningsson 		.v.func = via_fixup_intmic_boost,
14774b527b65SDavid Henningsson 	},
1478d5266125STakashi Iwai 	[VIA_FIXUP_ASUS_G75] = {
1479d5266125STakashi Iwai 		.type = HDA_FIXUP_PINS,
1480d5266125STakashi Iwai 		.v.pins = (const struct hda_pintbl[]) {
1481d5266125STakashi Iwai 			/* set 0x24 and 0x33 as speakers */
1482d5266125STakashi Iwai 			{ 0x24, 0x991301f0 },
1483d5266125STakashi Iwai 			{ 0x33, 0x991301f1 }, /* subwoofer */
1484d5266125STakashi Iwai 			{ }
1485d5266125STakashi Iwai 		}
1486d5266125STakashi Iwai 	},
14874b527b65SDavid Henningsson };
14884b527b65SDavid Henningsson 
14894b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = {
1490d5266125STakashi Iwai 	SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
14914b527b65SDavid Henningsson 	SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
14924b527b65SDavid Henningsson 	{}
14934b527b65SDavid Henningsson };
14944b527b65SDavid Henningsson 
1495ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
1496ef4da458STakashi Iwai  * Replace this with mixer NID 0x1c
1497ef4da458STakashi Iwai  */
1498ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec)
1499ef4da458STakashi Iwai {
1500ef4da458STakashi Iwai 	static hda_nid_t conn_24[] = { 0x14, 0x1c };
1501ef4da458STakashi Iwai 	static hda_nid_t conn_33[] = { 0x1c };
1502ef4da458STakashi Iwai 
1503ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
1504ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
1505ef4da458STakashi Iwai }
1506ef4da458STakashi Iwai 
150725eaba2fSLydia Wang /* patch for vt2002P */
150825eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
150925eaba2fSLydia Wang {
151025eaba2fSLydia Wang 	struct via_spec *spec;
151125eaba2fSLydia Wang 	int err;
151225eaba2fSLydia Wang 
151325eaba2fSLydia Wang 	/* create a codec specific record */
15145b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
151525eaba2fSLydia Wang 	if (spec == NULL)
151625eaba2fSLydia Wang 		return -ENOMEM;
151725eaba2fSLydia Wang 
1518b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1519d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1520d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
1521ef4da458STakashi Iwai 	if (spec->codec_type == VT1802)
1522ef4da458STakashi Iwai 		fix_vt1802_connections(codec);
152330b45033STakashi Iwai 	add_secret_dac_path(codec);
1524620e2b28STakashi Iwai 
15254b527b65SDavid Henningsson 	snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
15264b527b65SDavid Henningsson 	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
15274b527b65SDavid Henningsson 
152825eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
152912daef65STakashi Iwai 	err = via_parse_auto_config(codec);
153025eaba2fSLydia Wang 	if (err < 0) {
153125eaba2fSLydia Wang 		via_free(codec);
153225eaba2fSLydia Wang 		return err;
153325eaba2fSLydia Wang 	}
153425eaba2fSLydia Wang 
153511890956SLydia Wang 	if (spec->codec_type == VT1802)
15364a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
153711890956SLydia Wang 	else
15384a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
153911890956SLydia Wang 
154025eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
154125eaba2fSLydia Wang 
15423e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt2002P;
154325eaba2fSLydia Wang 	return 0;
154425eaba2fSLydia Wang }
1545ab6734e7SLydia Wang 
1546ab6734e7SLydia Wang /* for vt1812 */
1547ab6734e7SLydia Wang 
1548096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = {
1549ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
1550ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
1551ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
1552ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
1553ab6734e7SLydia Wang 	{ }
1554ab6734e7SLydia Wang };
1555ab6734e7SLydia Wang 
15563e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec)
15573e95b9abSLydia Wang {
15583e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
15593e95b9abSLydia Wang 	unsigned int parm;
15603e95b9abSLydia Wang 	unsigned int present;
15613e95b9abSLydia Wang 	/* inputs */
15623e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
15633e95b9abSLydia Wang 	parm = AC_PWRST_D3;
15643e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
15653e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
15663e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
15673e95b9abSLydia Wang 	parm = AC_PWRST_D0;
15683e95b9abSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
1569054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
1570054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
1571054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1572054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
15733e95b9abSLydia Wang 
15743e95b9abSLydia Wang 	/* outputs */
15753e95b9abSLydia Wang 	/* AOW0 (8h)*/
1576054d867eSTakashi Iwai 	update_power_state(codec, 0x8, AC_PWRST_D0);
15773e95b9abSLydia Wang 
15783e95b9abSLydia Wang 	/* PW4 (28h), MW4 (18h), MUX4(38h) */
15793e95b9abSLydia Wang 	parm = AC_PWRST_D3;
15803e95b9abSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
1581054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
1582054d867eSTakashi Iwai 	update_power_state(codec, 0x38, parm);
15833e95b9abSLydia Wang 
15843e95b9abSLydia Wang 	/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
15853e95b9abSLydia Wang 	parm = AC_PWRST_D3;
15863e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
1587054d867eSTakashi Iwai 	update_power_state(codec, 0x15, parm);
1588054d867eSTakashi Iwai 	update_power_state(codec, 0x35, parm);
1589b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
1590054d867eSTakashi Iwai 		update_power_state(codec, 0x9, AC_PWRST_D0);
15913e95b9abSLydia Wang 
15923e95b9abSLydia Wang 	/* Internal Speaker */
15933e95b9abSLydia Wang 	/* PW0 (24h), MW0(14h), MUX0(34h) */
15943e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
15953e95b9abSLydia Wang 
15963e95b9abSLydia Wang 	parm = AC_PWRST_D3;
15973e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
15983e95b9abSLydia Wang 	if (present) {
1599054d867eSTakashi Iwai 		update_power_state(codec, 0x14, AC_PWRST_D3);
1600054d867eSTakashi Iwai 		update_power_state(codec, 0x34, AC_PWRST_D3);
16013e95b9abSLydia Wang 	} else {
1602054d867eSTakashi Iwai 		update_power_state(codec, 0x14, AC_PWRST_D0);
1603054d867eSTakashi Iwai 		update_power_state(codec, 0x34, AC_PWRST_D0);
16043e95b9abSLydia Wang 	}
16053e95b9abSLydia Wang 
16063e95b9abSLydia Wang 
16073e95b9abSLydia Wang 	/* Mono Out */
16083e95b9abSLydia Wang 	/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
16093e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x28);
16103e95b9abSLydia Wang 
16113e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16123e95b9abSLydia Wang 	set_pin_power_state(codec, 0x31, &parm);
16133e95b9abSLydia Wang 	if (present) {
1614054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, AC_PWRST_D3);
1615054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, AC_PWRST_D3);
1616054d867eSTakashi Iwai 		update_power_state(codec, 0x3e, AC_PWRST_D3);
16173e95b9abSLydia Wang 	} else {
1618054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, AC_PWRST_D0);
1619054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, AC_PWRST_D0);
1620054d867eSTakashi Iwai 		update_power_state(codec, 0x3e, AC_PWRST_D0);
16213e95b9abSLydia Wang 	}
16223e95b9abSLydia Wang 
16233e95b9abSLydia Wang 	/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
16243e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16253e95b9abSLydia Wang 	set_pin_power_state(codec, 0x33, &parm);
1626054d867eSTakashi Iwai 	update_power_state(codec, 0x1d, parm);
1627054d867eSTakashi Iwai 	update_power_state(codec, 0x3d, parm);
16283e95b9abSLydia Wang 
16293e95b9abSLydia Wang }
1630ab6734e7SLydia Wang 
1631ab6734e7SLydia Wang /* patch for vt1812 */
1632ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
1633ab6734e7SLydia Wang {
1634ab6734e7SLydia Wang 	struct via_spec *spec;
1635ab6734e7SLydia Wang 	int err;
1636ab6734e7SLydia Wang 
1637ab6734e7SLydia Wang 	/* create a codec specific record */
16385b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1639ab6734e7SLydia Wang 	if (spec == NULL)
1640ab6734e7SLydia Wang 		return -ENOMEM;
1641ab6734e7SLydia Wang 
1642b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1643d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1644d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
164530b45033STakashi Iwai 	add_secret_dac_path(codec);
1646620e2b28STakashi Iwai 
1647ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
164812daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1649ab6734e7SLydia Wang 	if (err < 0) {
1650ab6734e7SLydia Wang 		via_free(codec);
1651ab6734e7SLydia Wang 		return err;
1652ab6734e7SLydia Wang 	}
1653ab6734e7SLydia Wang 
1654096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1812_init_verbs;
1655ab6734e7SLydia Wang 
1656ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
1657ab6734e7SLydia Wang 
16583e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1812;
1659ab6734e7SLydia Wang 	return 0;
1660ab6734e7SLydia Wang }
1661ab6734e7SLydia Wang 
166243737e0aSLydia Wang /* patch for vt3476 */
166343737e0aSLydia Wang 
166443737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = {
166543737e0aSLydia Wang 	/* Enable DMic 8/16/32K */
166643737e0aSLydia Wang 	{0x1, 0xF7B, 0x30},
166743737e0aSLydia Wang 	/* Enable Boost Volume backdoor */
166843737e0aSLydia Wang 	{0x1, 0xFB9, 0x20},
166943737e0aSLydia Wang 	/* Enable AOW-MW9 path */
167043737e0aSLydia Wang 	{0x1, 0xFB8, 0x10},
167143737e0aSLydia Wang 	{ }
167243737e0aSLydia Wang };
167343737e0aSLydia Wang 
167443737e0aSLydia Wang static void set_widgets_power_state_vt3476(struct hda_codec *codec)
167543737e0aSLydia Wang {
167643737e0aSLydia Wang 	struct via_spec *spec = codec->spec;
167743737e0aSLydia Wang 	int imux_is_smixer;
167843737e0aSLydia Wang 	unsigned int parm, parm2;
167943737e0aSLydia Wang 	/* MUX10 (1eh) = stereo mixer */
168043737e0aSLydia Wang 	imux_is_smixer =
168143737e0aSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 4;
168243737e0aSLydia Wang 	/* inputs */
168343737e0aSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
168443737e0aSLydia Wang 	parm = AC_PWRST_D3;
168543737e0aSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
168643737e0aSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
168743737e0aSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
168843737e0aSLydia Wang 	if (imux_is_smixer)
168943737e0aSLydia Wang 		parm = AC_PWRST_D0;
169043737e0aSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
169143737e0aSLydia Wang 	update_power_state(codec, 0x1e, parm);
169243737e0aSLydia Wang 	update_power_state(codec, 0x1f, parm);
169343737e0aSLydia Wang 	update_power_state(codec, 0x10, parm);
169443737e0aSLydia Wang 	update_power_state(codec, 0x11, parm);
169543737e0aSLydia Wang 
169643737e0aSLydia Wang 	/* outputs */
169743737e0aSLydia Wang 	/* PW3 (27h), MW3(37h), AOW3 (bh) */
169843737e0aSLydia Wang 	if (spec->codec_type == VT1705CF) {
169943737e0aSLydia Wang 		parm = AC_PWRST_D3;
170043737e0aSLydia Wang 		update_power_state(codec, 0x27, parm);
170143737e0aSLydia Wang 		update_power_state(codec, 0x37, parm);
170243737e0aSLydia Wang 	}	else {
170343737e0aSLydia Wang 		parm = AC_PWRST_D3;
170443737e0aSLydia Wang 		set_pin_power_state(codec, 0x27, &parm);
170543737e0aSLydia Wang 		update_power_state(codec, 0x37, parm);
170643737e0aSLydia Wang 	}
170743737e0aSLydia Wang 
170843737e0aSLydia Wang 	/* PW2 (26h), MW2(36h), AOW2 (ah) */
170943737e0aSLydia Wang 	parm = AC_PWRST_D3;
171043737e0aSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
171143737e0aSLydia Wang 	update_power_state(codec, 0x36, parm);
1712b3f6008fSTakashi Iwai 	if (smart51_enabled(codec)) {
171343737e0aSLydia Wang 		/* PW7(2bh), MW7(3bh), MUX7(1Bh) */
171443737e0aSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
171543737e0aSLydia Wang 		update_power_state(codec, 0x3b, parm);
171643737e0aSLydia Wang 		update_power_state(codec, 0x1b, parm);
171743737e0aSLydia Wang 	}
171843737e0aSLydia Wang 	update_conv_power_state(codec, 0xa, parm, 2);
171943737e0aSLydia Wang 
172043737e0aSLydia Wang 	/* PW1 (25h), MW1(35h), AOW1 (9h) */
172143737e0aSLydia Wang 	parm = AC_PWRST_D3;
172243737e0aSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
172343737e0aSLydia Wang 	update_power_state(codec, 0x35, parm);
1724b3f6008fSTakashi Iwai 	if (smart51_enabled(codec)) {
172543737e0aSLydia Wang 		/* PW6(2ah), MW6(3ah), MUX6(1ah) */
172643737e0aSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
172743737e0aSLydia Wang 		update_power_state(codec, 0x3a, parm);
172843737e0aSLydia Wang 		update_power_state(codec, 0x1a, parm);
172943737e0aSLydia Wang 	}
173043737e0aSLydia Wang 	update_conv_power_state(codec, 0x9, parm, 1);
173143737e0aSLydia Wang 
173243737e0aSLydia Wang 	/* PW4 (28h), MW4 (38h), MUX4(18h), AOW3(bh)/AOW0(8h) */
173343737e0aSLydia Wang 	parm = AC_PWRST_D3;
173443737e0aSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
173543737e0aSLydia Wang 	update_power_state(codec, 0x38, parm);
173643737e0aSLydia Wang 	update_power_state(codec, 0x18, parm);
1737b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
173843737e0aSLydia Wang 		update_conv_power_state(codec, 0xb, parm, 3);
173943737e0aSLydia Wang 	parm2 = parm; /* for pin 0x0b */
174043737e0aSLydia Wang 
174143737e0aSLydia Wang 	/* PW0 (24h), MW0(34h), MW9(3fh), AOW0 (8h) */
174243737e0aSLydia Wang 	parm = AC_PWRST_D3;
174343737e0aSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
174443737e0aSLydia Wang 	update_power_state(codec, 0x34, parm);
1745b3f6008fSTakashi Iwai 	if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3)
174643737e0aSLydia Wang 		parm = parm2;
174743737e0aSLydia Wang 	update_conv_power_state(codec, 0x8, parm, 0);
174843737e0aSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
174943737e0aSLydia Wang 	update_power_state(codec, 0x3f, imux_is_smixer ? AC_PWRST_D0 : parm);
175043737e0aSLydia Wang }
175143737e0aSLydia Wang 
175243737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec)
175343737e0aSLydia Wang {
175443737e0aSLydia Wang 	struct via_spec *spec;
175543737e0aSLydia Wang 	int err;
175643737e0aSLydia Wang 
175743737e0aSLydia Wang 	/* create a codec specific record */
175843737e0aSLydia Wang 	spec = via_new_spec(codec);
175943737e0aSLydia Wang 	if (spec == NULL)
176043737e0aSLydia Wang 		return -ENOMEM;
176143737e0aSLydia Wang 
1762b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x3f;
176343737e0aSLydia Wang 	add_secret_dac_path(codec);
176443737e0aSLydia Wang 
176543737e0aSLydia Wang 	/* automatic parse from the BIOS config */
176643737e0aSLydia Wang 	err = via_parse_auto_config(codec);
176743737e0aSLydia Wang 	if (err < 0) {
176843737e0aSLydia Wang 		via_free(codec);
176943737e0aSLydia Wang 		return err;
177043737e0aSLydia Wang 	}
177143737e0aSLydia Wang 
177243737e0aSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs;
177343737e0aSLydia Wang 
177443737e0aSLydia Wang 	codec->patch_ops = via_patch_ops;
177543737e0aSLydia Wang 
177643737e0aSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt3476;
177743737e0aSLydia Wang 
177843737e0aSLydia Wang 	return 0;
177943737e0aSLydia Wang }
178043737e0aSLydia Wang 
1781c577b8a1SJoseph Chan /*
1782c577b8a1SJoseph Chan  * patch entries
1783c577b8a1SJoseph Chan  */
178490dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = {
17853218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
17863218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
17873218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
17883218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
17893218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
1790ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17913218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
1792ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17933218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
1794ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17953218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
1796ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17973218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
1798ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17993218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
1800ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18013218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
1802ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18033218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
1804ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18053218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
1806ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18073218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
1808ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18093218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
1810ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18113218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
1812ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18133218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
1814ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18153218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
1816ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18173218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
1818ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18193218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
1820ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18213218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
1822d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18233218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
1824d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18253218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
1826d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18273218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
1828d949cac1SHarald Welte 	  .patch = patch_vt1708S},
1829bc92df7fSLydia Wang 	{ .id = 0x11064397, .name = "VT1705",
1830d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18313218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
1832d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18333218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
1834d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18353218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
1836d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18373218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
1838d949cac1SHarald Welte 	  .patch = patch_vt1702},
18393218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
1840d949cac1SHarald Welte 	  .patch = patch_vt1702},
18413218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
1842d949cac1SHarald Welte 	  .patch = patch_vt1702},
18433218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
1844d949cac1SHarald Welte 	  .patch = patch_vt1702},
18453218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
1846d949cac1SHarald Welte 	  .patch = patch_vt1702},
18473218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
1848d949cac1SHarald Welte 	  .patch = patch_vt1702},
18493218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
1850d949cac1SHarald Welte 	  .patch = patch_vt1702},
18513218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
1852d949cac1SHarald Welte 	  .patch = patch_vt1702},
1853eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
1854eb7188caSLydia Wang 	  .patch = patch_vt1718S},
1855eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
1856eb7188caSLydia Wang 	  .patch = patch_vt1718S},
1857bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
1858bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
1859bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
1860bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
1861f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
1862f3db423dSLydia Wang 	  .patch = patch_vt1716S},
1863f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
1864f3db423dSLydia Wang 	  .patch = patch_vt1716S},
186525eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
186625eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
1867ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
186836dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
186936dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
187011890956SLydia Wang 	{ .id = 0x11060446, .name = "VT1802",
187111890956SLydia Wang 		.patch = patch_vt2002P},
187211890956SLydia Wang 	{ .id = 0x11068446, .name = "VT1802",
187311890956SLydia Wang 		.patch = patch_vt2002P},
187443737e0aSLydia Wang 	{ .id = 0x11064760, .name = "VT1705CF",
187543737e0aSLydia Wang 		.patch = patch_vt3476},
18766121b84aSLydia Wang 	{ .id = 0x11064761, .name = "VT1708SCE",
18776121b84aSLydia Wang 		.patch = patch_vt3476},
18786121b84aSLydia Wang 	{ .id = 0x11064762, .name = "VT1808",
18796121b84aSLydia Wang 		.patch = patch_vt3476},
1880c577b8a1SJoseph Chan 	{} /* terminator */
1881c577b8a1SJoseph Chan };
18821289e9e8STakashi Iwai 
18831289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
18841289e9e8STakashi Iwai 
18851289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
18861289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
18871289e9e8STakashi Iwai 	.owner = THIS_MODULE,
18881289e9e8STakashi Iwai };
18891289e9e8STakashi Iwai 
18901289e9e8STakashi Iwai MODULE_LICENSE("GPL");
18911289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
18921289e9e8STakashi Iwai 
18931289e9e8STakashi Iwai static int __init patch_via_init(void)
18941289e9e8STakashi Iwai {
18951289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
18961289e9e8STakashi Iwai }
18971289e9e8STakashi Iwai 
18981289e9e8STakashi Iwai static void __exit patch_via_exit(void)
18991289e9e8STakashi Iwai {
19001289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
19011289e9e8STakashi Iwai }
19021289e9e8STakashi Iwai 
19031289e9e8STakashi Iwai module_init(patch_via_init)
19041289e9e8STakashi Iwai module_exit(patch_via_exit)
1905