xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision b3f6008f2d511133e0f04782c437a13b6865d26b)
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"
59*b3f6008fSTakashi 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 {
91*b3f6008fSTakashi Iwai 	struct hda_gen_spec gen;
92*b3f6008fSTakashi 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);
117*b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
118*b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
119*b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
120*b3f6008fSTakashi Iwai 				  int action);
121*b3f6008fSTakashi Iwai static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl);
122*b3f6008fSTakashi 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;
132*b3f6008fSTakashi 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;
137*b3f6008fSTakashi Iwai 	spec->no_pin_power_ctl = 1;
138*b3f6008fSTakashi Iwai 	spec->gen.pcm_playback_hook = via_playback_pcm_hook;
1395b0cb1d8SJaroslav Kysela 	return spec;
1405b0cb1d8SJaroslav Kysela }
1415b0cb1d8SJaroslav Kysela 
142744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
143d7426329SHarald Welte {
144744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
145d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
146d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
147d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
148d7426329SHarald Welte 
149d7426329SHarald Welte 	/* get codec type */
150d7426329SHarald Welte 	if (ven_id != 0x1106)
151d7426329SHarald Welte 		codec_type = UNKNOWN;
152d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
153d7426329SHarald Welte 		codec_type = VT1708;
154d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
155d7426329SHarald Welte 		codec_type = VT1709_10CH;
156d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
157d7426329SHarald Welte 		codec_type = VT1709_6CH;
158518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
159d7426329SHarald Welte 		codec_type = VT1708B_8CH;
160518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
161518bf3baSLydia Wang 			codec_type = VT1708BCE;
162518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
163d7426329SHarald Welte 		codec_type = VT1708B_4CH;
164d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
165d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
166d7426329SHarald Welte 		codec_type = VT1708S;
167d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
168d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
169d7426329SHarald Welte 		codec_type = VT1702;
170eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
171eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
172eb7188caSLydia Wang 		codec_type = VT1718S;
173f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
174f3db423dSLydia Wang 		codec_type = VT1716S;
175bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
176bb3c6bfcSLydia Wang 		codec_type = VT1718S;
17725eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
17825eaba2fSLydia Wang 		codec_type = VT2002P;
179ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
180ab6734e7SLydia Wang 		codec_type = VT1812;
18136dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
18236dd5c4aSLydia Wang 		codec_type = VT1708S;
18311890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
18411890956SLydia Wang 		codec_type = VT1802;
18543737e0aSLydia Wang 	else if (dev_id == 0x4760)
18643737e0aSLydia Wang 		codec_type = VT1705CF;
1876121b84aSLydia Wang 	else if (dev_id == 0x4761 || dev_id == 0x4762)
1886121b84aSLydia Wang 		codec_type = VT1808;
189d7426329SHarald Welte 	else
190d7426329SHarald Welte 		codec_type = UNKNOWN;
191d7426329SHarald Welte 	return codec_type;
192d7426329SHarald Welte };
193d7426329SHarald Welte 
194ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec);
195ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec);
1961f2e99feSLydia Wang 
197187d333eSTakashi Iwai #define hp_detect_with_aa(codec) \
198187d333eSTakashi Iwai 	(snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
199187d333eSTakashi Iwai 	 !is_aa_path_mute(codec))
2001f2e99feSLydia Wang 
201*b3f6008fSTakashi Iwai static void vt1708_stop_hp_work(struct hda_codec *codec)
2021f2e99feSLydia Wang {
203*b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
204*b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
2051f2e99feSLydia Wang 		return;
206187d333eSTakashi Iwai 	if (spec->hp_work_active) {
207*b3f6008fSTakashi Iwai 		snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
208*b3f6008fSTakashi Iwai 		cancel_delayed_work_sync(&codec->jackpoll_work);
209*b3f6008fSTakashi Iwai 		spec->hp_work_active = false;
210*b3f6008fSTakashi Iwai 		codec->jackpoll_interval = 0;
211187d333eSTakashi Iwai 	}
212187d333eSTakashi Iwai }
213187d333eSTakashi Iwai 
214*b3f6008fSTakashi Iwai static void vt1708_update_hp_work(struct hda_codec *codec)
215187d333eSTakashi Iwai {
216*b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
217*b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
218187d333eSTakashi Iwai 		return;
219187d333eSTakashi Iwai 	if (spec->vt1708_jack_detect &&
220*b3f6008fSTakashi Iwai 	    (spec->gen.active_streams || hp_detect_with_aa(codec))) {
221187d333eSTakashi Iwai 		if (!spec->hp_work_active) {
222*b3f6008fSTakashi Iwai 			codec->jackpoll_interval = msecs_to_jiffies(100);
223*b3f6008fSTakashi Iwai 			snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0);
224*b3f6008fSTakashi Iwai 			queue_delayed_work(codec->bus->workq,
225*b3f6008fSTakashi Iwai 					   &codec->jackpoll_work, 0);
226*b3f6008fSTakashi Iwai 			spec->hp_work_active = true;
227187d333eSTakashi Iwai 		}
228*b3f6008fSTakashi Iwai 	} else if (!hp_detect_with_aa(codec))
229*b3f6008fSTakashi 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 {
242054d867eSTakashi Iwai 	if (snd_hda_codec_read(codec, nid, 0,
243054d867eSTakashi Iwai 			       AC_VERB_GET_POWER_STATE, 0) == parm)
244054d867eSTakashi Iwai 		return;
245054d867eSTakashi Iwai 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
246054d867eSTakashi Iwai }
247054d867eSTakashi Iwai 
24843737e0aSLydia Wang static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid,
24943737e0aSLydia Wang 			       unsigned int parm, unsigned int index)
25043737e0aSLydia Wang {
25143737e0aSLydia Wang 	struct via_spec *spec = codec->spec;
25243737e0aSLydia Wang 	unsigned int format;
25343737e0aSLydia Wang 	if (snd_hda_codec_read(codec, nid, 0,
25443737e0aSLydia Wang 			       AC_VERB_GET_POWER_STATE, 0) == parm)
25543737e0aSLydia Wang 		return;
25643737e0aSLydia Wang 	format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
25743737e0aSLydia Wang 	if (format && (spec->dac_stream_tag[index] != format))
25843737e0aSLydia Wang 		spec->dac_stream_tag[index] = format;
25943737e0aSLydia Wang 
26043737e0aSLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
26143737e0aSLydia Wang 	if (parm == AC_PWRST_D0) {
26243737e0aSLydia Wang 		format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
26343737e0aSLydia Wang 		if (!format && (spec->dac_stream_tag[index] != format))
26443737e0aSLydia Wang 			snd_hda_codec_write(codec, nid, 0,
26543737e0aSLydia Wang 						  AC_VERB_SET_CHANNEL_STREAMID,
26643737e0aSLydia Wang 						  spec->dac_stream_tag[index]);
26743737e0aSLydia Wang 	}
26843737e0aSLydia Wang }
26943737e0aSLydia Wang 
270*b3f6008fSTakashi Iwai static bool smart51_enabled(struct hda_codec *codec)
271*b3f6008fSTakashi Iwai {
272*b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
273*b3f6008fSTakashi Iwai 	return spec->gen.ext_channel_count > 2;
274*b3f6008fSTakashi Iwai }
275*b3f6008fSTakashi Iwai 
276*b3f6008fSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
277*b3f6008fSTakashi Iwai {
278*b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
279*b3f6008fSTakashi Iwai 	int i;
280*b3f6008fSTakashi Iwai 
281*b3f6008fSTakashi Iwai 	for (i = 0; i < spec->gen.multi_ios; i++)
282*b3f6008fSTakashi Iwai 		if (spec->gen.multi_io[i].pin == pin)
283*b3f6008fSTakashi Iwai 			return true;
284*b3f6008fSTakashi Iwai 	return false;
285*b3f6008fSTakashi Iwai }
286*b3f6008fSTakashi Iwai 
287f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
288f5271101SLydia Wang 				unsigned int *affected_parm)
289f5271101SLydia Wang {
290f5271101SLydia Wang 	unsigned parm;
291f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
292f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
293f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
294f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
2951564b287SLydia Wang 	struct via_spec *spec = codec->spec;
29624088a58STakashi Iwai 	unsigned present = 0;
29724088a58STakashi Iwai 
29824088a58STakashi Iwai 	no_presence |= spec->no_pin_power_ctl;
29924088a58STakashi Iwai 	if (!no_presence)
30024088a58STakashi Iwai 		present = snd_hda_jack_detect(codec, nid);
301*b3f6008fSTakashi Iwai 	if ((smart51_enabled(codec) && is_smart51_pins(codec, nid))
3021564b287SLydia Wang 	    || ((no_presence || present)
3031564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
304f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
305f5271101SLydia Wang 		parm = AC_PWRST_D0;
306f5271101SLydia Wang 	} else
307f5271101SLydia Wang 		parm = AC_PWRST_D3;
308f5271101SLydia Wang 
309054d867eSTakashi Iwai 	update_power_state(codec, nid, parm);
310f5271101SLydia Wang }
311f5271101SLydia Wang 
31224088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
31324088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
31424088a58STakashi Iwai {
315dda415d4STakashi Iwai 	return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
31624088a58STakashi Iwai }
31724088a58STakashi Iwai 
31824088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
31924088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
32024088a58STakashi Iwai {
32124088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
32224088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
32324088a58STakashi Iwai 	ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
32424088a58STakashi Iwai 	return 0;
32524088a58STakashi Iwai }
32624088a58STakashi Iwai 
32724088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
32824088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
32924088a58STakashi Iwai {
33024088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
33124088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
33224088a58STakashi Iwai 	unsigned int val = !ucontrol->value.enumerated.item[0];
33324088a58STakashi Iwai 
33424088a58STakashi Iwai 	if (val == spec->no_pin_power_ctl)
33524088a58STakashi Iwai 		return 0;
33624088a58STakashi Iwai 	spec->no_pin_power_ctl = val;
33724088a58STakashi Iwai 	set_widgets_power_state(codec);
338e9d010c2STakashi Iwai 	analog_low_current_mode(codec);
33924088a58STakashi Iwai 	return 1;
34024088a58STakashi Iwai }
34124088a58STakashi Iwai 
342*b3f6008fSTakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = {
343*b3f6008fSTakashi Iwai 	{
34424088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
34524088a58STakashi Iwai 	.name = "Dynamic Power-Control",
34624088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
34724088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
34824088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
349*b3f6008fSTakashi Iwai 	},
350*b3f6008fSTakashi Iwai 	{} /* terminator */
35124088a58STakashi Iwai };
35224088a58STakashi Iwai 
35324088a58STakashi Iwai 
354f5271101SLydia Wang /* check AA path's mute status */
355ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec)
356ada509ecSTakashi Iwai {
357ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
358ada509ecSTakashi Iwai 	const struct hda_amp_list *p;
359ada509ecSTakashi Iwai 	int i, ch, v;
360ada509ecSTakashi Iwai 
361*b3f6008fSTakashi Iwai 	for (i = 0; i < spec->gen.num_loopbacks; i++) {
362*b3f6008fSTakashi Iwai 		p = &spec->gen.loopback_list[i];
363ada509ecSTakashi Iwai 		for (ch = 0; ch < 2; ch++) {
364ada509ecSTakashi Iwai 			v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
365ada509ecSTakashi Iwai 						   p->idx);
366ada509ecSTakashi Iwai 			if (!(v & HDA_AMP_MUTE) && v > 0)
367ada509ecSTakashi Iwai 				return false;
368f5271101SLydia Wang 		}
369f5271101SLydia Wang 	}
370ada509ecSTakashi Iwai 	return true;
371f5271101SLydia Wang }
372f5271101SLydia Wang 
373f5271101SLydia Wang /* enter/exit analog low-current mode */
374e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force)
375f5271101SLydia Wang {
376f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
377ada509ecSTakashi Iwai 	bool enable;
378ada509ecSTakashi Iwai 	unsigned int verb, parm;
379f5271101SLydia Wang 
380e9d010c2STakashi Iwai 	if (spec->no_pin_power_ctl)
381e9d010c2STakashi Iwai 		enable = false;
382e9d010c2STakashi Iwai 	else
383*b3f6008fSTakashi Iwai 		enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
384e9d010c2STakashi Iwai 	if (enable == spec->alc_mode && !force)
385e9d010c2STakashi Iwai 		return;
386e9d010c2STakashi Iwai 	spec->alc_mode = enable;
387f5271101SLydia Wang 
388f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
389f5271101SLydia Wang 	switch (spec->codec_type) {
390f5271101SLydia Wang 	case VT1708B_8CH:
391f5271101SLydia Wang 	case VT1708B_4CH:
392f5271101SLydia Wang 		verb = 0xf70;
393f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
394f5271101SLydia Wang 		break;
395f5271101SLydia Wang 	case VT1708S:
396eb7188caSLydia Wang 	case VT1718S:
397f3db423dSLydia Wang 	case VT1716S:
398f5271101SLydia Wang 		verb = 0xf73;
399f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
400f5271101SLydia Wang 		break;
401f5271101SLydia Wang 	case VT1702:
402f5271101SLydia Wang 		verb = 0xf73;
403f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
404f5271101SLydia Wang 		break;
40525eaba2fSLydia Wang 	case VT2002P:
406ab6734e7SLydia Wang 	case VT1812:
40711890956SLydia Wang 	case VT1802:
40825eaba2fSLydia Wang 		verb = 0xf93;
40925eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
41025eaba2fSLydia Wang 		break;
41143737e0aSLydia Wang 	case VT1705CF:
4126121b84aSLydia Wang 	case VT1808:
41343737e0aSLydia Wang 		verb = 0xf82;
41443737e0aSLydia Wang 		parm = enable ? 0x00 : 0xe0;  /* 0x00: 4/40x, 0xe0: 1x */
41543737e0aSLydia Wang 		break;
416f5271101SLydia Wang 	default:
417f5271101SLydia Wang 		return;		/* other codecs are not supported */
418f5271101SLydia Wang 	}
419f5271101SLydia Wang 	/* send verb */
420f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
421f5271101SLydia Wang }
422f5271101SLydia Wang 
423e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec)
424e9d010c2STakashi Iwai {
425e9d010c2STakashi Iwai 	return __analog_low_current_mode(codec, false);
426e9d010c2STakashi Iwai }
427e9d010c2STakashi Iwai 
428c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
429c577b8a1SJoseph Chan {
430c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
4315b0cb1d8SJaroslav Kysela 	int err, i;
432c577b8a1SJoseph Chan 
433*b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_controls(codec);
434*b3f6008fSTakashi Iwai 	if (err < 0)
435*b3f6008fSTakashi Iwai 		return err;
436*b3f6008fSTakashi Iwai 
43724088a58STakashi Iwai 	if (spec->set_widgets_power_state)
438*b3f6008fSTakashi Iwai 		spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum;
43924088a58STakashi Iwai 
440c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
441c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
442c577b8a1SJoseph Chan 		if (err < 0)
443c577b8a1SJoseph Chan 			return err;
444c577b8a1SJoseph Chan 	}
445c577b8a1SJoseph Chan 
446c577b8a1SJoseph Chan 	return 0;
447c577b8a1SJoseph Chan }
448c577b8a1SJoseph Chan 
449*b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
450*b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
451*b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
452*b3f6008fSTakashi Iwai 				  int action)
453c577b8a1SJoseph Chan {
454*b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
455*b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
456c577b8a1SJoseph Chan }
457c577b8a1SJoseph Chan 
458c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
459c577b8a1SJoseph Chan {
460c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
461c577b8a1SJoseph Chan 
462c577b8a1SJoseph Chan 	if (!spec)
463c577b8a1SJoseph Chan 		return;
464c577b8a1SJoseph Chan 
465*b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
466*b3f6008fSTakashi Iwai 	snd_hda_gen_spec_free(&spec->gen);
467a86a88eaSTakashi Iwai 	kfree(spec);
468c577b8a1SJoseph Chan }
469c577b8a1SJoseph Chan 
4702a43952aSTakashi Iwai #ifdef CONFIG_PM
47168cb2b55STakashi Iwai static int via_suspend(struct hda_codec *codec)
4721f2e99feSLydia Wang {
4731f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
474*b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
47594c142a1SDavid Henningsson 
47694c142a1SDavid Henningsson 	if (spec->codec_type == VT1802) {
47794c142a1SDavid Henningsson 		/* Fix pop noise on headphones */
47894c142a1SDavid Henningsson 		int i;
479*b3f6008fSTakashi Iwai 		for (i = 0; i < spec->gen.autocfg.hp_outs; i++)
480*b3f6008fSTakashi Iwai 			snd_hda_set_pin_ctl(codec, spec->gen.autocfg.hp_pins[i], 0);
48194c142a1SDavid Henningsson 	}
48294c142a1SDavid Henningsson 
4831f2e99feSLydia Wang 	return 0;
4841f2e99feSLydia Wang }
4851f2e99feSLydia Wang #endif
4861f2e99feSLydia Wang 
48783012a7cSTakashi Iwai #ifdef CONFIG_PM
488cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
489cb53c626STakashi Iwai {
490cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
491*b3f6008fSTakashi Iwai 	set_widgets_power_state(codec);
492*b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
493*b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
494*b3f6008fSTakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
495cb53c626STakashi Iwai }
496cb53c626STakashi Iwai #endif
497cb53c626STakashi Iwai 
498c577b8a1SJoseph Chan /*
499c577b8a1SJoseph Chan  */
5005d41762aSTakashi Iwai 
5015d41762aSTakashi Iwai static int via_init(struct hda_codec *codec);
5025d41762aSTakashi Iwai 
50390dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
504c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
505*b3f6008fSTakashi Iwai 	.build_pcms = snd_hda_gen_build_pcms,
506c577b8a1SJoseph Chan 	.init = via_init,
507c577b8a1SJoseph Chan 	.free = via_free,
5084e2d16d3SDavid Henningsson 	.unsol_event = snd_hda_jack_unsol_event,
5092a43952aSTakashi Iwai #ifdef CONFIG_PM
5101f2e99feSLydia Wang 	.suspend = via_suspend,
511cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
512cb53c626STakashi Iwai #endif
513c577b8a1SJoseph Chan };
514c577b8a1SJoseph Chan 
5154a79616dSTakashi Iwai 
516*b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = {
517*b3f6008fSTakashi Iwai 	/* power down jack detect function */
518*b3f6008fSTakashi Iwai 	{0x1, 0xf81, 0x1},
519*b3f6008fSTakashi Iwai 	{ }
5204a79616dSTakashi Iwai };
52176d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
52276d9b0ddSHarald Welte {
52376d9b0ddSHarald Welte 	unsigned int def_conf;
52476d9b0ddSHarald Welte 	unsigned char seqassoc;
52576d9b0ddSHarald Welte 
5262f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
52776d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
52876d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
52982ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
53082ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
53176d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
5322f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
53376d9b0ddSHarald Welte 	}
53476d9b0ddSHarald Welte 
53576d9b0ddSHarald Welte 	return;
53676d9b0ddSHarald Welte }
53776d9b0ddSHarald Welte 
538e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
5391f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
5401f2e99feSLydia Wang {
5411f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
5421f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
5431f2e99feSLydia Wang 
5441f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
5451f2e99feSLydia Wang 		return 0;
546e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
5471f2e99feSLydia Wang 	return 0;
5481f2e99feSLydia Wang }
5491f2e99feSLydia Wang 
550e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
5511f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
5521f2e99feSLydia Wang {
5531f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
5541f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
555187d333eSTakashi Iwai 	int val;
5561f2e99feSLydia Wang 
5571f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
5581f2e99feSLydia Wang 		return 0;
559187d333eSTakashi Iwai 	val = !!ucontrol->value.integer.value[0];
560187d333eSTakashi Iwai 	if (spec->vt1708_jack_detect == val)
561187d333eSTakashi Iwai 		return 0;
562187d333eSTakashi Iwai 	spec->vt1708_jack_detect = val;
563*b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
564187d333eSTakashi Iwai 	return 1;
5651f2e99feSLydia Wang }
5661f2e99feSLydia Wang 
567*b3f6008fSTakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = {
568*b3f6008fSTakashi Iwai 	{
5691f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5701f2e99feSLydia Wang 	.name = "Jack Detect",
5711f2e99feSLydia Wang 	.count = 1,
5721f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
573e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
574e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
575*b3f6008fSTakashi Iwai 	},
576*b3f6008fSTakashi Iwai 	{} /* terminator */
5771f2e99feSLydia Wang };
5781f2e99feSLydia Wang 
579*b3f6008fSTakashi Iwai static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl)
5804e2d16d3SDavid Henningsson {
5814e2d16d3SDavid Henningsson 	set_widgets_power_state(codec);
582*b3f6008fSTakashi Iwai 	snd_hda_gen_hp_automute(codec, tbl);
583*b3f6008fSTakashi Iwai }
584*b3f6008fSTakashi Iwai 
585*b3f6008fSTakashi Iwai static void via_line_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl)
586*b3f6008fSTakashi Iwai {
587*b3f6008fSTakashi Iwai 	set_widgets_power_state(codec);
588*b3f6008fSTakashi Iwai 	snd_hda_gen_line_automute(codec, tbl);
5894e2d16d3SDavid Henningsson }
5904e2d16d3SDavid Henningsson 
5914e2d16d3SDavid Henningsson static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_tbl *tbl)
5924e2d16d3SDavid Henningsson {
5934e2d16d3SDavid Henningsson 	set_widgets_power_state(codec);
5944e2d16d3SDavid Henningsson }
5954e2d16d3SDavid Henningsson 
596*b3f6008fSTakashi Iwai #define VIA_JACK_EVENT	(HDA_GEN_LAST_EVENT + 1)
597*b3f6008fSTakashi Iwai 
598*b3f6008fSTakashi Iwai static void via_set_jack_unsol_events(struct hda_codec *codec)
5994a918ffeSTakashi Iwai {
6004a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
601*b3f6008fSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->gen.autocfg;
602*b3f6008fSTakashi Iwai 	hda_nid_t pin;
6034a918ffeSTakashi Iwai 	int i;
6044a918ffeSTakashi Iwai 
605*b3f6008fSTakashi Iwai 	spec->gen.hp_automute_hook = via_hp_automute;
6064a918ffeSTakashi Iwai 	if (cfg->speaker_pins[0])
607*b3f6008fSTakashi Iwai 		spec->gen.line_automute_hook = via_line_automute;
6084e2d16d3SDavid Henningsson 
6094a918ffeSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
610*b3f6008fSTakashi Iwai 		pin = cfg->line_out_pins[i];
611*b3f6008fSTakashi Iwai 		if (pin && !snd_hda_jack_tbl_get(codec, pin) &&
612*b3f6008fSTakashi Iwai 		    is_jack_detectable(codec, pin))
613*b3f6008fSTakashi Iwai 			snd_hda_jack_detect_enable_callback(codec, pin,
6144e2d16d3SDavid Henningsson 							    VIA_JACK_EVENT,
6154e2d16d3SDavid Henningsson 							    via_jack_powerstate_event);
6164a918ffeSTakashi Iwai 	}
617*b3f6008fSTakashi Iwai 
618*b3f6008fSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
619*b3f6008fSTakashi Iwai 		pin = cfg->line_out_pins[i];
620*b3f6008fSTakashi Iwai 		if (pin && !snd_hda_jack_tbl_get(codec, pin) &&
621*b3f6008fSTakashi Iwai 		    is_jack_detectable(codec, pin))
622*b3f6008fSTakashi Iwai 			snd_hda_jack_detect_enable_callback(codec, pin,
623*b3f6008fSTakashi Iwai 							    VIA_JACK_EVENT,
624*b3f6008fSTakashi Iwai 							    via_jack_powerstate_event);
625*b3f6008fSTakashi Iwai 	}
626*b3f6008fSTakashi Iwai }
627*b3f6008fSTakashi Iwai 
628*b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec)
629*b3f6008fSTakashi Iwai {
630*b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
631*b3f6008fSTakashi Iwai 	int err;
632*b3f6008fSTakashi Iwai 
633*b3f6008fSTakashi Iwai 	err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
634*b3f6008fSTakashi Iwai 	if (err < 0)
635*b3f6008fSTakashi Iwai 		return err;
636*b3f6008fSTakashi Iwai 
637*b3f6008fSTakashi Iwai 	err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
638*b3f6008fSTakashi Iwai 	if (err < 0)
639*b3f6008fSTakashi Iwai 		return err;
640*b3f6008fSTakashi Iwai 
641*b3f6008fSTakashi Iwai 	via_set_jack_unsol_events(codec);
642*b3f6008fSTakashi Iwai 	return 0;
6434a918ffeSTakashi Iwai }
6444a918ffeSTakashi Iwai 
6455d41762aSTakashi Iwai static int via_init(struct hda_codec *codec)
6465d41762aSTakashi Iwai {
6475d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
6485d41762aSTakashi Iwai 	int i;
6495d41762aSTakashi Iwai 
6505d41762aSTakashi Iwai 	for (i = 0; i < spec->num_iverbs; i++)
6515d41762aSTakashi Iwai 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
6525d41762aSTakashi Iwai 
653e9d010c2STakashi Iwai 	/* init power states */
654e9d010c2STakashi Iwai 	set_widgets_power_state(codec);
655e9d010c2STakashi Iwai 	__analog_low_current_mode(codec, true);
656e9d010c2STakashi Iwai 
657*b3f6008fSTakashi Iwai 	snd_hda_gen_init(codec);
65811890956SLydia Wang 
659*b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
66025eaba2fSLydia Wang 
661c577b8a1SJoseph Chan 	return 0;
662c577b8a1SJoseph Chan }
663c577b8a1SJoseph Chan 
664*b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec)
665337b9d02STakashi Iwai {
666337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
667*b3f6008fSTakashi Iwai 	int i, err;
668337b9d02STakashi Iwai 
669*b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_pcms(codec);
670*b3f6008fSTakashi Iwai 	if (err < 0 || codec->vendor_id != 0x11061708)
671*b3f6008fSTakashi Iwai 		return err;
672*b3f6008fSTakashi Iwai 
673*b3f6008fSTakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
674*b3f6008fSTakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
675*b3f6008fSTakashi Iwai 	 * disable the 24bit format, so far.
676*b3f6008fSTakashi Iwai 	 */
677*b3f6008fSTakashi Iwai 	for (i = 0; i < codec->num_pcms; i++) {
678*b3f6008fSTakashi Iwai 		struct hda_pcm *info = &spec->gen.pcm_rec[i];
679*b3f6008fSTakashi Iwai 		if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
680*b3f6008fSTakashi Iwai 		    info->pcm_type != HDA_PCM_TYPE_AUDIO)
681*b3f6008fSTakashi Iwai 			continue;
682*b3f6008fSTakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats =
683*b3f6008fSTakashi Iwai 			SNDRV_PCM_FMTBIT_S16_LE;
684337b9d02STakashi Iwai 	}
685*b3f6008fSTakashi Iwai 
6861c55d521STakashi Iwai 	return 0;
687337b9d02STakashi Iwai }
688337b9d02STakashi Iwai 
689c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
690c577b8a1SJoseph Chan {
691c577b8a1SJoseph Chan 	struct via_spec *spec;
692c577b8a1SJoseph Chan 	int err;
693c577b8a1SJoseph Chan 
694c577b8a1SJoseph Chan 	/* create a codec specific record */
6955b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
696c577b8a1SJoseph Chan 	if (spec == NULL)
697c577b8a1SJoseph Chan 		return -ENOMEM;
698c577b8a1SJoseph Chan 
699*b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x17;
700*b3f6008fSTakashi Iwai 
701*b3f6008fSTakashi Iwai 	/* set jackpoll_interval while parsing the codec */
702*b3f6008fSTakashi Iwai 	codec->jackpoll_interval = msecs_to_jiffies(100);
703*b3f6008fSTakashi Iwai 	spec->vt1708_jack_detect = 1;
704*b3f6008fSTakashi Iwai 
705*b3f6008fSTakashi Iwai 	/* don't support the input jack switching due to lack of unsol event */
706*b3f6008fSTakashi Iwai 	/* (it may work with polling, though, but it needs testing) */
707*b3f6008fSTakashi Iwai 	spec->gen.suppress_auto_mic = 1;
708620e2b28STakashi Iwai 
70912daef65STakashi Iwai 	/* Add HP and CD pin config connect bit re-config action */
71012daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
71112daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
71212daef65STakashi Iwai 
713c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
71412daef65STakashi Iwai 	err = via_parse_auto_config(codec);
715c577b8a1SJoseph Chan 	if (err < 0) {
716c577b8a1SJoseph Chan 		via_free(codec);
717c577b8a1SJoseph Chan 		return err;
718c577b8a1SJoseph Chan 	}
719c577b8a1SJoseph Chan 
72012daef65STakashi Iwai 	/* add jack detect on/off control */
721*b3f6008fSTakashi Iwai 	spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl;
722c577b8a1SJoseph Chan 
723e322a36dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
724e322a36dSLydia Wang 
725c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
726*b3f6008fSTakashi Iwai 	codec->patch_ops.build_pcms = vt1708_build_pcms;
727c577b8a1SJoseph Chan 
728*b3f6008fSTakashi Iwai 	/* clear jackpoll_interval again; it's set dynamically */
729*b3f6008fSTakashi Iwai 	codec->jackpoll_interval = 0;
730*b3f6008fSTakashi Iwai 
731c577b8a1SJoseph Chan 	return 0;
732c577b8a1SJoseph Chan }
733c577b8a1SJoseph Chan 
734ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec)
735c577b8a1SJoseph Chan {
736c577b8a1SJoseph Chan 	struct via_spec *spec;
737c577b8a1SJoseph Chan 	int err;
738c577b8a1SJoseph Chan 
739c577b8a1SJoseph Chan 	/* create a codec specific record */
7405b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
741c577b8a1SJoseph Chan 	if (spec == NULL)
742c577b8a1SJoseph Chan 		return -ENOMEM;
743c577b8a1SJoseph Chan 
744*b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x18;
745620e2b28STakashi Iwai 
74612daef65STakashi Iwai 	err = via_parse_auto_config(codec);
747c577b8a1SJoseph Chan 	if (err < 0) {
748c577b8a1SJoseph Chan 		via_free(codec);
749c577b8a1SJoseph Chan 		return err;
750c577b8a1SJoseph Chan 	}
751c577b8a1SJoseph Chan 
752c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
753c577b8a1SJoseph Chan 
754f7278fd0SJosepch Chan 	return 0;
755f7278fd0SJosepch Chan }
756f7278fd0SJosepch Chan 
7573e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
7583e95b9abSLydia Wang {
7593e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
7603e95b9abSLydia Wang 	int imux_is_smixer;
7613e95b9abSLydia Wang 	unsigned int parm;
7623e95b9abSLydia Wang 	int is_8ch = 0;
763bc92df7fSLydia Wang 	if ((spec->codec_type != VT1708B_4CH) &&
764bc92df7fSLydia Wang 	    (codec->vendor_id != 0x11064397))
7653e95b9abSLydia Wang 		is_8ch = 1;
7663e95b9abSLydia Wang 
7673e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
7683e95b9abSLydia Wang 	imux_is_smixer =
7693e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
7703e95b9abSLydia Wang 	 == ((spec->codec_type == VT1708S) ? 5 : 0));
7713e95b9abSLydia Wang 	/* inputs */
7723e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
7733e95b9abSLydia Wang 	parm = AC_PWRST_D3;
7743e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
7753e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
7763e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
7773e95b9abSLydia Wang 	if (imux_is_smixer)
7783e95b9abSLydia Wang 		parm = AC_PWRST_D0;
7793e95b9abSLydia Wang 	/* SW0 (17h), AIW 0/1 (13h/14h) */
780054d867eSTakashi Iwai 	update_power_state(codec, 0x17, parm);
781054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
782054d867eSTakashi Iwai 	update_power_state(codec, 0x14, parm);
7833e95b9abSLydia Wang 
7843e95b9abSLydia Wang 	/* outputs */
7853e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
7863e95b9abSLydia Wang 	parm = AC_PWRST_D3;
7873e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
788*b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
7893e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
790054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
791054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
7923e95b9abSLydia Wang 
7933e95b9abSLydia Wang 	/* PW6 (22h), SW2 (26h), AOW2 (24h) */
7943e95b9abSLydia Wang 	if (is_8ch) {
7953e95b9abSLydia Wang 		parm = AC_PWRST_D3;
7963e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
797*b3f6008fSTakashi Iwai 		if (smart51_enabled(codec))
7983e95b9abSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
799054d867eSTakashi Iwai 		update_power_state(codec, 0x26, parm);
800054d867eSTakashi Iwai 		update_power_state(codec, 0x24, parm);
801bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397) {
802bc92df7fSLydia Wang 		/* PW7(23h), SW2(27h), AOW2(25h) */
803bc92df7fSLydia Wang 		parm = AC_PWRST_D3;
804bc92df7fSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
805*b3f6008fSTakashi Iwai 		if (smart51_enabled(codec))
806bc92df7fSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
807054d867eSTakashi Iwai 		update_power_state(codec, 0x27, parm);
808054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
8093e95b9abSLydia Wang 	}
8103e95b9abSLydia Wang 
8113e95b9abSLydia Wang 	/* PW 3/4/7 (1ch/1dh/23h) */
8123e95b9abSLydia Wang 	parm = AC_PWRST_D3;
8133e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
8143e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
8153e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
8163e95b9abSLydia Wang 	if (is_8ch)
8173e95b9abSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
8183e95b9abSLydia Wang 
8193e95b9abSLydia Wang 	/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
820054d867eSTakashi Iwai 	update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
821054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
8223e95b9abSLydia Wang 	if (is_8ch) {
823054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
824054d867eSTakashi Iwai 		update_power_state(codec, 0x27, parm);
825*b3f6008fSTakashi Iwai 	} else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled)
826054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
8273e95b9abSLydia Wang }
8283e95b9abSLydia Wang 
829518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
830ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec)
831f7278fd0SJosepch Chan {
832f7278fd0SJosepch Chan 	struct via_spec *spec;
833f7278fd0SJosepch Chan 	int err;
834f7278fd0SJosepch Chan 
835518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
836518bf3baSLydia Wang 		return patch_vt1708S(codec);
837ddd304d8STakashi Iwai 
838f7278fd0SJosepch Chan 	/* create a codec specific record */
8395b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
840f7278fd0SJosepch Chan 	if (spec == NULL)
841f7278fd0SJosepch Chan 		return -ENOMEM;
842f7278fd0SJosepch Chan 
843*b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
844620e2b28STakashi Iwai 
845f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
84612daef65STakashi Iwai 	err = via_parse_auto_config(codec);
847f7278fd0SJosepch Chan 	if (err < 0) {
848f7278fd0SJosepch Chan 		via_free(codec);
849f7278fd0SJosepch Chan 		return err;
850f7278fd0SJosepch Chan 	}
851f7278fd0SJosepch Chan 
852f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
853f7278fd0SJosepch Chan 
8543e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
8553e95b9abSLydia Wang 
856f7278fd0SJosepch Chan 	return 0;
857f7278fd0SJosepch Chan }
858f7278fd0SJosepch Chan 
859d949cac1SHarald Welte /* Patch for VT1708S */
860096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = {
861d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
862d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
863bc7e7e5cSLydia Wang 	/* don't bybass mixer */
864bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
865d949cac1SHarald Welte 	{ }
866d949cac1SHarald Welte };
867d949cac1SHarald Welte 
8686369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
8696369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
8706369bcfcSLydia Wang {
8716369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
8726369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
8736369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
8746369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
8756369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
8766369bcfcSLydia Wang }
8776369bcfcSLydia Wang 
878d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
879d949cac1SHarald Welte {
880d949cac1SHarald Welte 	struct via_spec *spec;
881d949cac1SHarald Welte 	int err;
882d949cac1SHarald Welte 
883d949cac1SHarald Welte 	/* create a codec specific record */
8845b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
885d949cac1SHarald Welte 	if (spec == NULL)
886d949cac1SHarald Welte 		return -ENOMEM;
887d949cac1SHarald Welte 
888*b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
889d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
890d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
891620e2b28STakashi Iwai 
892518bf3baSLydia Wang 	/* correct names for VT1708BCE */
893518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
894518bf3baSLydia Wang 		kfree(codec->chip_name);
895518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
896518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
897518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
898518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
899970f630fSLydia Wang 	}
900bc92df7fSLydia Wang 	/* correct names for VT1705 */
901bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)	{
902bc92df7fSLydia Wang 		kfree(codec->chip_name);
903bc92df7fSLydia Wang 		codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
904bc92df7fSLydia Wang 		snprintf(codec->bus->card->mixername,
905bc92df7fSLydia Wang 			 sizeof(codec->bus->card->mixername),
906bc92df7fSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
907bc92df7fSLydia Wang 	}
908*b3f6008fSTakashi Iwai 
909*b3f6008fSTakashi Iwai 	/* automatic parse from the BIOS config */
910*b3f6008fSTakashi Iwai 	err = via_parse_auto_config(codec);
911*b3f6008fSTakashi Iwai 	if (err < 0) {
912*b3f6008fSTakashi Iwai 		via_free(codec);
913*b3f6008fSTakashi Iwai 		return err;
914*b3f6008fSTakashi Iwai 	}
915*b3f6008fSTakashi Iwai 
916*b3f6008fSTakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
917*b3f6008fSTakashi Iwai 
918*b3f6008fSTakashi Iwai 	codec->patch_ops = via_patch_ops;
919*b3f6008fSTakashi Iwai 
9203e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
921d949cac1SHarald Welte 	return 0;
922d949cac1SHarald Welte }
923d949cac1SHarald Welte 
924d949cac1SHarald Welte /* Patch for VT1702 */
925d949cac1SHarald Welte 
926096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = {
927bc7e7e5cSLydia Wang 	/* mixer enable */
928bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
929bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
930bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
931d949cac1SHarald Welte 	{ }
932d949cac1SHarald Welte };
933d949cac1SHarald Welte 
9343e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec)
9353e95b9abSLydia Wang {
9363e95b9abSLydia Wang 	int imux_is_smixer =
9373e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
9383e95b9abSLydia Wang 	unsigned int parm;
9393e95b9abSLydia Wang 	/* inputs */
9403e95b9abSLydia Wang 	/* PW 1/2/5 (14h/15h/18h) */
9413e95b9abSLydia Wang 	parm = AC_PWRST_D3;
9423e95b9abSLydia Wang 	set_pin_power_state(codec, 0x14, &parm);
9433e95b9abSLydia Wang 	set_pin_power_state(codec, 0x15, &parm);
9443e95b9abSLydia Wang 	set_pin_power_state(codec, 0x18, &parm);
9453e95b9abSLydia Wang 	if (imux_is_smixer)
9463e95b9abSLydia Wang 		parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
9473e95b9abSLydia Wang 	/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
948054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
949054d867eSTakashi Iwai 	update_power_state(codec, 0x12, parm);
950054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
951054d867eSTakashi Iwai 	update_power_state(codec, 0x20, parm);
9523e95b9abSLydia Wang 
9533e95b9abSLydia Wang 	/* outputs */
9543e95b9abSLydia Wang 	/* PW 3/4 (16h/17h) */
9553e95b9abSLydia Wang 	parm = AC_PWRST_D3;
9563e95b9abSLydia Wang 	set_pin_power_state(codec, 0x17, &parm);
9573e95b9abSLydia Wang 	set_pin_power_state(codec, 0x16, &parm);
9583e95b9abSLydia Wang 	/* MW0 (1ah), AOW 0/1 (10h/1dh) */
959054d867eSTakashi Iwai 	update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm);
960054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
961054d867eSTakashi Iwai 	update_power_state(codec, 0x1d, parm);
9623e95b9abSLydia Wang }
9633e95b9abSLydia Wang 
964d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
965d949cac1SHarald Welte {
966d949cac1SHarald Welte 	struct via_spec *spec;
967d949cac1SHarald Welte 	int err;
968d949cac1SHarald Welte 
969d949cac1SHarald Welte 	/* create a codec specific record */
9705b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
971d949cac1SHarald Welte 	if (spec == NULL)
972d949cac1SHarald Welte 		return -ENOMEM;
973d949cac1SHarald Welte 
974*b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x1a;
975620e2b28STakashi Iwai 
97612daef65STakashi Iwai 	/* limit AA path volume to 0 dB */
97712daef65STakashi Iwai 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
97812daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
97912daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
98012daef65STakashi Iwai 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
98112daef65STakashi Iwai 				  (1 << AC_AMPCAP_MUTE_SHIFT));
98212daef65STakashi Iwai 
983d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
98412daef65STakashi Iwai 	err = via_parse_auto_config(codec);
985d949cac1SHarald Welte 	if (err < 0) {
986d949cac1SHarald Welte 		via_free(codec);
987d949cac1SHarald Welte 		return err;
988d949cac1SHarald Welte 	}
989d949cac1SHarald Welte 
990096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
991d949cac1SHarald Welte 
992d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
993d949cac1SHarald Welte 
9943e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
995d949cac1SHarald Welte 	return 0;
996d949cac1SHarald Welte }
997d949cac1SHarald Welte 
998eb7188caSLydia Wang /* Patch for VT1718S */
999eb7188caSLydia Wang 
1000096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = {
10014ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
10024ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
1003eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
1004eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
10055d41762aSTakashi Iwai 
1006eb7188caSLydia Wang 	{ }
1007eb7188caSLydia Wang };
1008eb7188caSLydia Wang 
10093e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
10103e95b9abSLydia Wang {
10113e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
10123e95b9abSLydia Wang 	int imux_is_smixer;
10136162552bSTakashi Iwai 	unsigned int parm, parm2;
10143e95b9abSLydia Wang 	/* MUX6 (1eh) = stereo mixer */
10153e95b9abSLydia Wang 	imux_is_smixer =
10163e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
10173e95b9abSLydia Wang 	/* inputs */
10183e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
10193e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10203e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
10213e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
10223e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
10233e95b9abSLydia Wang 	if (imux_is_smixer)
10243e95b9abSLydia Wang 		parm = AC_PWRST_D0;
10253e95b9abSLydia Wang 	/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
1026054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
1027054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
1028054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1029054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
10303e95b9abSLydia Wang 
10313e95b9abSLydia Wang 	/* outputs */
10323e95b9abSLydia Wang 	/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
10333e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10343e95b9abSLydia Wang 	set_pin_power_state(codec, 0x27, &parm);
1035054d867eSTakashi Iwai 	update_power_state(codec, 0x1a, parm);
10366162552bSTakashi Iwai 	parm2 = parm; /* for pin 0x0b */
10373e95b9abSLydia Wang 
10383e95b9abSLydia Wang 	/* PW2 (26h), AOW2 (ah) */
10393e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10403e95b9abSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
1041*b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
10423e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
1043054d867eSTakashi Iwai 	update_power_state(codec, 0xa, parm);
10443e95b9abSLydia Wang 
10453e95b9abSLydia Wang 	/* PW0 (24h), AOW0 (8h) */
10463e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10473e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
1048*b3f6008fSTakashi Iwai 	if (!spec->gen.indep_hp_enabled) /* check for redirected HP */
10493e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
1050054d867eSTakashi Iwai 	update_power_state(codec, 0x8, parm);
1051*b3f6008fSTakashi Iwai 	if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3)
10526162552bSTakashi Iwai 		parm = parm2;
10536162552bSTakashi Iwai 	update_power_state(codec, 0xb, parm);
10543e95b9abSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
1055054d867eSTakashi Iwai 	update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm);
10563e95b9abSLydia Wang 
10573e95b9abSLydia Wang 	/* PW1 (25h), AOW1 (9h) */
10583e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10593e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
1060*b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
10613e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
1062054d867eSTakashi Iwai 	update_power_state(codec, 0x9, parm);
10633e95b9abSLydia Wang 
1064*b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled) {
10653e95b9abSLydia Wang 		/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
10663e95b9abSLydia Wang 		parm = AC_PWRST_D3;
10673e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
1068054d867eSTakashi Iwai 		update_power_state(codec, 0x1b, parm);
1069054d867eSTakashi Iwai 		update_power_state(codec, 0x34, parm);
1070054d867eSTakashi Iwai 		update_power_state(codec, 0xc, parm);
10713e95b9abSLydia Wang 	}
10723e95b9abSLydia Wang }
10733e95b9abSLydia Wang 
107430b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs
107530b45033STakashi Iwai  * This isn't listed from the raw info, but the chip has a secret connection.
107630b45033STakashi Iwai  */
107730b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec)
107830b45033STakashi Iwai {
107930b45033STakashi Iwai 	struct via_spec *spec = codec->spec;
108030b45033STakashi Iwai 	int i, nums;
108130b45033STakashi Iwai 	hda_nid_t conn[8];
108230b45033STakashi Iwai 	hda_nid_t nid;
108330b45033STakashi Iwai 
1084*b3f6008fSTakashi Iwai 	if (!spec->gen.mixer_nid)
108530b45033STakashi Iwai 		return 0;
1086*b3f6008fSTakashi Iwai 	nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
108730b45033STakashi Iwai 				       ARRAY_SIZE(conn) - 1);
108830b45033STakashi Iwai 	for (i = 0; i < nums; i++) {
108930b45033STakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
109030b45033STakashi Iwai 			return 0;
109130b45033STakashi Iwai 	}
109230b45033STakashi Iwai 
109330b45033STakashi Iwai 	/* find the primary DAC and add to the connection list */
109430b45033STakashi Iwai 	nid = codec->start_nid;
109530b45033STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, nid++) {
109630b45033STakashi Iwai 		unsigned int caps = get_wcaps(codec, nid);
109730b45033STakashi Iwai 		if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
109830b45033STakashi Iwai 		    !(caps & AC_WCAP_DIGITAL)) {
109930b45033STakashi Iwai 			conn[nums++] = nid;
110030b45033STakashi Iwai 			return snd_hda_override_conn_list(codec,
1101*b3f6008fSTakashi Iwai 							  spec->gen.mixer_nid,
110230b45033STakashi Iwai 							  nums, conn);
110330b45033STakashi Iwai 		}
110430b45033STakashi Iwai 	}
110530b45033STakashi Iwai 	return 0;
110630b45033STakashi Iwai }
110730b45033STakashi Iwai 
110830b45033STakashi Iwai 
1109eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
1110eb7188caSLydia Wang {
1111eb7188caSLydia Wang 	struct via_spec *spec;
1112eb7188caSLydia Wang 	int err;
1113eb7188caSLydia Wang 
1114eb7188caSLydia Wang 	/* create a codec specific record */
11155b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1116eb7188caSLydia Wang 	if (spec == NULL)
1117eb7188caSLydia Wang 		return -ENOMEM;
1118eb7188caSLydia Wang 
1119*b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1120d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1121d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
112230b45033STakashi Iwai 	add_secret_dac_path(codec);
1123620e2b28STakashi Iwai 
1124eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
112512daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1126eb7188caSLydia Wang 	if (err < 0) {
1127eb7188caSLydia Wang 		via_free(codec);
1128eb7188caSLydia Wang 		return err;
1129eb7188caSLydia Wang 	}
1130eb7188caSLydia Wang 
1131096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
1132eb7188caSLydia Wang 
1133eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
1134eb7188caSLydia Wang 
11353e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1718S;
11363e95b9abSLydia Wang 
1137eb7188caSLydia Wang 	return 0;
1138eb7188caSLydia Wang }
1139f3db423dSLydia Wang 
1140f3db423dSLydia Wang /* Patch for VT1716S */
1141f3db423dSLydia Wang 
1142f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
1143f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
1144f3db423dSLydia Wang {
1145f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
1146f3db423dSLydia Wang 	uinfo->count = 1;
1147f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
1148f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
1149f3db423dSLydia Wang 	return 0;
1150f3db423dSLydia Wang }
1151f3db423dSLydia Wang 
1152f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
1153f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
1154f3db423dSLydia Wang {
1155f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1156f3db423dSLydia Wang 	int index = 0;
1157f3db423dSLydia Wang 
1158f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
1159f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
1160f3db423dSLydia Wang 	if (index != -1)
1161f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
1162f3db423dSLydia Wang 
1163f3db423dSLydia Wang 	return 0;
1164f3db423dSLydia Wang }
1165f3db423dSLydia Wang 
1166f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
1167f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
1168f3db423dSLydia Wang {
1169f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1170f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
1171f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
1172f3db423dSLydia Wang 
1173f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
1174f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
1175f3db423dSLydia Wang 	spec->dmic_enabled = index;
11763e95b9abSLydia Wang 	set_widgets_power_state(codec);
1177f3db423dSLydia Wang 	return 1;
1178f3db423dSLydia Wang }
1179f3db423dSLydia Wang 
118090dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
1181f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
1182f3db423dSLydia Wang 	{
1183f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1184f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
11855b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
1186f3db423dSLydia Wang 	 .count = 1,
1187f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
1188f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
1189f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
1190f3db423dSLydia Wang 	 },
1191f3db423dSLydia Wang 	{}			/* end */
1192f3db423dSLydia Wang };
1193f3db423dSLydia Wang 
1194f3db423dSLydia Wang 
1195f3db423dSLydia Wang /* mono-out mixer elements */
119690dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
1197f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
1198f3db423dSLydia Wang 	{ } /* end */
1199f3db423dSLydia Wang };
1200f3db423dSLydia Wang 
1201096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = {
1202f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
1203f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
1204f3db423dSLydia Wang 	/* don't bybass mixer */
1205f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
1206f3db423dSLydia Wang 	/* Enable mono output */
1207f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
1208f3db423dSLydia Wang 	{ }
1209f3db423dSLydia Wang };
1210f3db423dSLydia Wang 
12113e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
12123e95b9abSLydia Wang {
12133e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
12143e95b9abSLydia Wang 	int imux_is_smixer;
12153e95b9abSLydia Wang 	unsigned int parm;
12163e95b9abSLydia Wang 	unsigned int mono_out, present;
12173e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
12183e95b9abSLydia Wang 	imux_is_smixer =
12193e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0,
12203e95b9abSLydia Wang 			    AC_VERB_GET_CONNECT_SEL, 0x00) ==  5);
12213e95b9abSLydia Wang 	/* inputs */
12223e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
12233e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12243e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
12253e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
12263e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
12273e95b9abSLydia Wang 	if (imux_is_smixer)
12283e95b9abSLydia Wang 		parm = AC_PWRST_D0;
12293e95b9abSLydia Wang 	/* SW0 (17h), AIW0(13h) */
1230054d867eSTakashi Iwai 	update_power_state(codec, 0x17, parm);
1231054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
12323e95b9abSLydia Wang 
12333e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12343e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
12353e95b9abSLydia Wang 	/* PW11 (22h) */
12363e95b9abSLydia Wang 	if (spec->dmic_enabled)
12373e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
12383e95b9abSLydia Wang 	else
1239054d867eSTakashi Iwai 		update_power_state(codec, 0x22, AC_PWRST_D3);
12403e95b9abSLydia Wang 
12413e95b9abSLydia Wang 	/* SW2(26h), AIW1(14h) */
1242054d867eSTakashi Iwai 	update_power_state(codec, 0x26, parm);
1243054d867eSTakashi Iwai 	update_power_state(codec, 0x14, parm);
12443e95b9abSLydia Wang 
12453e95b9abSLydia Wang 	/* outputs */
12463e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
12473e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12483e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
12493e95b9abSLydia Wang 	/* Smart 5.1 PW2(1bh) */
1250*b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
12513e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
1252054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
1253054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
12543e95b9abSLydia Wang 
12553e95b9abSLydia Wang 	/* PW7 (23h), SW3 (27h), AOW3 (25h) */
12563e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12573e95b9abSLydia Wang 	set_pin_power_state(codec, 0x23, &parm);
12583e95b9abSLydia Wang 	/* Smart 5.1 PW1(1ah) */
1259*b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
12603e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
1261054d867eSTakashi Iwai 	update_power_state(codec, 0x27, parm);
12623e95b9abSLydia Wang 
12633e95b9abSLydia Wang 	/* Smart 5.1 PW5(1eh) */
1264*b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
12653e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
1266054d867eSTakashi Iwai 	update_power_state(codec, 0x25, parm);
12673e95b9abSLydia Wang 
12683e95b9abSLydia Wang 	/* Mono out */
12693e95b9abSLydia Wang 	/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
12703e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x1c);
12713e95b9abSLydia Wang 
12723e95b9abSLydia Wang 	if (present)
12733e95b9abSLydia Wang 		mono_out = 0;
12743e95b9abSLydia Wang 	else {
12753e95b9abSLydia Wang 		present = snd_hda_jack_detect(codec, 0x1d);
1276*b3f6008fSTakashi Iwai 		if (!spec->gen.indep_hp_enabled && present)
12773e95b9abSLydia Wang 			mono_out = 0;
12783e95b9abSLydia Wang 		else
12793e95b9abSLydia Wang 			mono_out = 1;
12803e95b9abSLydia Wang 	}
12813e95b9abSLydia Wang 	parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
1282054d867eSTakashi Iwai 	update_power_state(codec, 0x28, parm);
1283054d867eSTakashi Iwai 	update_power_state(codec, 0x29, parm);
1284054d867eSTakashi Iwai 	update_power_state(codec, 0x2a, parm);
12853e95b9abSLydia Wang 
12863e95b9abSLydia Wang 	/* PW 3/4 (1ch/1dh) */
12873e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12883e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
12893e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
12903e95b9abSLydia Wang 	/* HP Independent Mode, power on AOW3 */
1291*b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
1292054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
12933e95b9abSLydia Wang 
12943e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
12953e95b9abSLydia Wang 	/* MW0 (16h), AOW0 (10h) */
1296054d867eSTakashi Iwai 	update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
1297054d867eSTakashi Iwai 	update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm);
12983e95b9abSLydia Wang }
12993e95b9abSLydia Wang 
1300f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
1301f3db423dSLydia Wang {
1302f3db423dSLydia Wang 	struct via_spec *spec;
1303f3db423dSLydia Wang 	int err;
1304f3db423dSLydia Wang 
1305f3db423dSLydia Wang 	/* create a codec specific record */
13065b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1307f3db423dSLydia Wang 	if (spec == NULL)
1308f3db423dSLydia Wang 		return -ENOMEM;
1309f3db423dSLydia Wang 
1310*b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
1311d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
1312d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
1313620e2b28STakashi Iwai 
1314f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
131512daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1316f3db423dSLydia Wang 	if (err < 0) {
1317f3db423dSLydia Wang 		via_free(codec);
1318f3db423dSLydia Wang 		return err;
1319f3db423dSLydia Wang 	}
1320f3db423dSLydia Wang 
1321096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_init_verbs;
1322f3db423dSLydia Wang 
1323*b3f6008fSTakashi Iwai 	spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer;
1324f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
1325f3db423dSLydia Wang 
1326f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
1327f3db423dSLydia Wang 
13283e95b9abSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
1329f3db423dSLydia Wang 	return 0;
1330f3db423dSLydia Wang }
133125eaba2fSLydia Wang 
133225eaba2fSLydia Wang /* for vt2002P */
133325eaba2fSLydia Wang 
1334096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = {
1335eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
1336eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
1337eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
1338eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
133925eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
134025eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
134125eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
134225eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
134325eaba2fSLydia Wang 	{ }
134425eaba2fSLydia Wang };
13454a918ffeSTakashi Iwai 
1346096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = {
134711890956SLydia Wang 	/* Enable Boost Volume backdoor */
134811890956SLydia Wang 	{0x1, 0xfb9, 0x24},
134911890956SLydia Wang 	/* Enable AOW0 to MW9 */
135011890956SLydia Wang 	{0x1, 0xfb8, 0x88},
135111890956SLydia Wang 	{ }
135211890956SLydia Wang };
135325eaba2fSLydia Wang 
13543e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
13553e95b9abSLydia Wang {
13563e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
13573e95b9abSLydia Wang 	int imux_is_smixer;
13583e95b9abSLydia Wang 	unsigned int parm;
13593e95b9abSLydia Wang 	unsigned int present;
13603e95b9abSLydia Wang 	/* MUX9 (1eh) = stereo mixer */
13613e95b9abSLydia Wang 	imux_is_smixer =
13623e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
13633e95b9abSLydia Wang 	/* inputs */
13643e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
13653e95b9abSLydia Wang 	parm = AC_PWRST_D3;
13663e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
13673e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
13683e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
13693e95b9abSLydia Wang 	parm = AC_PWRST_D0;
13703e95b9abSLydia Wang 	/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
1371054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
1372054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
1373054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1374054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
13753e95b9abSLydia Wang 
13763e95b9abSLydia Wang 	/* outputs */
13773e95b9abSLydia Wang 	/* AOW0 (8h)*/
1378054d867eSTakashi Iwai 	update_power_state(codec, 0x8, parm);
13793e95b9abSLydia Wang 
138011890956SLydia Wang 	if (spec->codec_type == VT1802) {
138111890956SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
138211890956SLydia Wang 		parm = AC_PWRST_D3;
138311890956SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
1384054d867eSTakashi Iwai 		update_power_state(codec, 0x18, parm);
1385054d867eSTakashi Iwai 		update_power_state(codec, 0x38, parm);
138611890956SLydia Wang 	} else {
13873e95b9abSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
13883e95b9abSLydia Wang 		parm = AC_PWRST_D3;
13893e95b9abSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
1390054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, parm);
1391054d867eSTakashi Iwai 		update_power_state(codec, 0x37, parm);
139211890956SLydia Wang 	}
13933e95b9abSLydia Wang 
139411890956SLydia Wang 	if (spec->codec_type == VT1802) {
139511890956SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
139611890956SLydia Wang 		parm = AC_PWRST_D3;
139711890956SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
1398054d867eSTakashi Iwai 		update_power_state(codec, 0x15, parm);
1399054d867eSTakashi Iwai 		update_power_state(codec, 0x35, parm);
140011890956SLydia Wang 	} else {
14013e95b9abSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
14023e95b9abSLydia Wang 		parm = AC_PWRST_D3;
14033e95b9abSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
1404054d867eSTakashi Iwai 		update_power_state(codec, 0x19, parm);
1405054d867eSTakashi Iwai 		update_power_state(codec, 0x35, parm);
140611890956SLydia Wang 	}
14073e95b9abSLydia Wang 
1408*b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
1409054d867eSTakashi Iwai 		update_power_state(codec, 0x9, AC_PWRST_D0);
14103e95b9abSLydia Wang 
14113e95b9abSLydia Wang 	/* Class-D */
14123e95b9abSLydia Wang 	/* PW0 (24h), MW0(18h/14h), MUX0(34h) */
14133e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
14143e95b9abSLydia Wang 
14153e95b9abSLydia Wang 	parm = AC_PWRST_D3;
14163e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
14173e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
141811890956SLydia Wang 	if (spec->codec_type == VT1802)
1419054d867eSTakashi Iwai 		update_power_state(codec, 0x14, parm);
142011890956SLydia Wang 	else
1421054d867eSTakashi Iwai 		update_power_state(codec, 0x18, parm);
1422054d867eSTakashi Iwai 	update_power_state(codec, 0x34, parm);
14233e95b9abSLydia Wang 
14243e95b9abSLydia Wang 	/* Mono Out */
14253e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x26);
14263e95b9abSLydia Wang 
14273e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
142811890956SLydia Wang 	if (spec->codec_type == VT1802) {
142911890956SLydia Wang 		/* PW15 (33h), MW8(1ch), MUX8(3ch) */
1430054d867eSTakashi Iwai 		update_power_state(codec, 0x33, parm);
1431054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, parm);
1432054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, parm);
143311890956SLydia Wang 	} else {
14343e95b9abSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
1435054d867eSTakashi Iwai 		update_power_state(codec, 0x31, parm);
1436054d867eSTakashi Iwai 		update_power_state(codec, 0x17, parm);
1437054d867eSTakashi Iwai 		update_power_state(codec, 0x3b, parm);
143811890956SLydia Wang 	}
14393e95b9abSLydia Wang 	/* MW9 (21h) */
14403e95b9abSLydia Wang 	if (imux_is_smixer || !is_aa_path_mute(codec))
1441054d867eSTakashi Iwai 		update_power_state(codec, 0x21, AC_PWRST_D0);
14423e95b9abSLydia Wang 	else
1443054d867eSTakashi Iwai 		update_power_state(codec, 0x21, AC_PWRST_D3);
14443e95b9abSLydia Wang }
144525eaba2fSLydia Wang 
14464b527b65SDavid Henningsson /*
14474b527b65SDavid Henningsson  * pin fix-up
14484b527b65SDavid Henningsson  */
14494b527b65SDavid Henningsson enum {
14504b527b65SDavid Henningsson 	VIA_FIXUP_INTMIC_BOOST,
1451d5266125STakashi Iwai 	VIA_FIXUP_ASUS_G75,
14524b527b65SDavid Henningsson };
14534b527b65SDavid Henningsson 
14544b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec,
14554b527b65SDavid Henningsson 				  const struct hda_fixup *fix, int action)
14564b527b65SDavid Henningsson {
14574b527b65SDavid Henningsson 	if (action == HDA_FIXUP_ACT_PRE_PROBE)
14584b527b65SDavid Henningsson 		override_mic_boost(codec, 0x30, 0, 2, 40);
14594b527b65SDavid Henningsson }
14604b527b65SDavid Henningsson 
14614b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = {
14624b527b65SDavid Henningsson 	[VIA_FIXUP_INTMIC_BOOST] = {
14634b527b65SDavid Henningsson 		.type = HDA_FIXUP_FUNC,
14644b527b65SDavid Henningsson 		.v.func = via_fixup_intmic_boost,
14654b527b65SDavid Henningsson 	},
1466d5266125STakashi Iwai 	[VIA_FIXUP_ASUS_G75] = {
1467d5266125STakashi Iwai 		.type = HDA_FIXUP_PINS,
1468d5266125STakashi Iwai 		.v.pins = (const struct hda_pintbl[]) {
1469d5266125STakashi Iwai 			/* set 0x24 and 0x33 as speakers */
1470d5266125STakashi Iwai 			{ 0x24, 0x991301f0 },
1471d5266125STakashi Iwai 			{ 0x33, 0x991301f1 }, /* subwoofer */
1472d5266125STakashi Iwai 			{ }
1473d5266125STakashi Iwai 		}
1474d5266125STakashi Iwai 	},
14754b527b65SDavid Henningsson };
14764b527b65SDavid Henningsson 
14774b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = {
1478d5266125STakashi Iwai 	SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
14794b527b65SDavid Henningsson 	SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
14804b527b65SDavid Henningsson 	{}
14814b527b65SDavid Henningsson };
14824b527b65SDavid Henningsson 
1483ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
1484ef4da458STakashi Iwai  * Replace this with mixer NID 0x1c
1485ef4da458STakashi Iwai  */
1486ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec)
1487ef4da458STakashi Iwai {
1488ef4da458STakashi Iwai 	static hda_nid_t conn_24[] = { 0x14, 0x1c };
1489ef4da458STakashi Iwai 	static hda_nid_t conn_33[] = { 0x1c };
1490ef4da458STakashi Iwai 
1491ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
1492ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
1493ef4da458STakashi Iwai }
1494ef4da458STakashi Iwai 
149525eaba2fSLydia Wang /* patch for vt2002P */
149625eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
149725eaba2fSLydia Wang {
149825eaba2fSLydia Wang 	struct via_spec *spec;
149925eaba2fSLydia Wang 	int err;
150025eaba2fSLydia Wang 
150125eaba2fSLydia Wang 	/* create a codec specific record */
15025b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
150325eaba2fSLydia Wang 	if (spec == NULL)
150425eaba2fSLydia Wang 		return -ENOMEM;
150525eaba2fSLydia Wang 
1506*b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1507d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1508d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
1509ef4da458STakashi Iwai 	if (spec->codec_type == VT1802)
1510ef4da458STakashi Iwai 		fix_vt1802_connections(codec);
151130b45033STakashi Iwai 	add_secret_dac_path(codec);
1512620e2b28STakashi Iwai 
15134b527b65SDavid Henningsson 	snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
15144b527b65SDavid Henningsson 	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
15154b527b65SDavid Henningsson 
151625eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
151712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
151825eaba2fSLydia Wang 	if (err < 0) {
151925eaba2fSLydia Wang 		via_free(codec);
152025eaba2fSLydia Wang 		return err;
152125eaba2fSLydia Wang 	}
152225eaba2fSLydia Wang 
152311890956SLydia Wang 	if (spec->codec_type == VT1802)
15244a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
152511890956SLydia Wang 	else
15264a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
152711890956SLydia Wang 
152825eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
152925eaba2fSLydia Wang 
15303e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt2002P;
153125eaba2fSLydia Wang 	return 0;
153225eaba2fSLydia Wang }
1533ab6734e7SLydia Wang 
1534ab6734e7SLydia Wang /* for vt1812 */
1535ab6734e7SLydia Wang 
1536096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = {
1537ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
1538ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
1539ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
1540ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
1541ab6734e7SLydia Wang 	{ }
1542ab6734e7SLydia Wang };
1543ab6734e7SLydia Wang 
15443e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec)
15453e95b9abSLydia Wang {
15463e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
15473e95b9abSLydia Wang 	unsigned int parm;
15483e95b9abSLydia Wang 	unsigned int present;
15493e95b9abSLydia Wang 	/* inputs */
15503e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
15513e95b9abSLydia Wang 	parm = AC_PWRST_D3;
15523e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
15533e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
15543e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
15553e95b9abSLydia Wang 	parm = AC_PWRST_D0;
15563e95b9abSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
1557054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
1558054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
1559054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1560054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
15613e95b9abSLydia Wang 
15623e95b9abSLydia Wang 	/* outputs */
15633e95b9abSLydia Wang 	/* AOW0 (8h)*/
1564054d867eSTakashi Iwai 	update_power_state(codec, 0x8, AC_PWRST_D0);
15653e95b9abSLydia Wang 
15663e95b9abSLydia Wang 	/* PW4 (28h), MW4 (18h), MUX4(38h) */
15673e95b9abSLydia Wang 	parm = AC_PWRST_D3;
15683e95b9abSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
1569054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
1570054d867eSTakashi Iwai 	update_power_state(codec, 0x38, parm);
15713e95b9abSLydia Wang 
15723e95b9abSLydia Wang 	/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
15733e95b9abSLydia Wang 	parm = AC_PWRST_D3;
15743e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
1575054d867eSTakashi Iwai 	update_power_state(codec, 0x15, parm);
1576054d867eSTakashi Iwai 	update_power_state(codec, 0x35, parm);
1577*b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
1578054d867eSTakashi Iwai 		update_power_state(codec, 0x9, AC_PWRST_D0);
15793e95b9abSLydia Wang 
15803e95b9abSLydia Wang 	/* Internal Speaker */
15813e95b9abSLydia Wang 	/* PW0 (24h), MW0(14h), MUX0(34h) */
15823e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
15833e95b9abSLydia Wang 
15843e95b9abSLydia Wang 	parm = AC_PWRST_D3;
15853e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
15863e95b9abSLydia Wang 	if (present) {
1587054d867eSTakashi Iwai 		update_power_state(codec, 0x14, AC_PWRST_D3);
1588054d867eSTakashi Iwai 		update_power_state(codec, 0x34, AC_PWRST_D3);
15893e95b9abSLydia Wang 	} else {
1590054d867eSTakashi Iwai 		update_power_state(codec, 0x14, AC_PWRST_D0);
1591054d867eSTakashi Iwai 		update_power_state(codec, 0x34, AC_PWRST_D0);
15923e95b9abSLydia Wang 	}
15933e95b9abSLydia Wang 
15943e95b9abSLydia Wang 
15953e95b9abSLydia Wang 	/* Mono Out */
15963e95b9abSLydia Wang 	/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
15973e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x28);
15983e95b9abSLydia Wang 
15993e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16003e95b9abSLydia Wang 	set_pin_power_state(codec, 0x31, &parm);
16013e95b9abSLydia Wang 	if (present) {
1602054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, AC_PWRST_D3);
1603054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, AC_PWRST_D3);
1604054d867eSTakashi Iwai 		update_power_state(codec, 0x3e, AC_PWRST_D3);
16053e95b9abSLydia Wang 	} else {
1606054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, AC_PWRST_D0);
1607054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, AC_PWRST_D0);
1608054d867eSTakashi Iwai 		update_power_state(codec, 0x3e, AC_PWRST_D0);
16093e95b9abSLydia Wang 	}
16103e95b9abSLydia Wang 
16113e95b9abSLydia Wang 	/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
16123e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16133e95b9abSLydia Wang 	set_pin_power_state(codec, 0x33, &parm);
1614054d867eSTakashi Iwai 	update_power_state(codec, 0x1d, parm);
1615054d867eSTakashi Iwai 	update_power_state(codec, 0x3d, parm);
16163e95b9abSLydia Wang 
16173e95b9abSLydia Wang }
1618ab6734e7SLydia Wang 
1619ab6734e7SLydia Wang /* patch for vt1812 */
1620ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
1621ab6734e7SLydia Wang {
1622ab6734e7SLydia Wang 	struct via_spec *spec;
1623ab6734e7SLydia Wang 	int err;
1624ab6734e7SLydia Wang 
1625ab6734e7SLydia Wang 	/* create a codec specific record */
16265b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1627ab6734e7SLydia Wang 	if (spec == NULL)
1628ab6734e7SLydia Wang 		return -ENOMEM;
1629ab6734e7SLydia Wang 
1630*b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1631d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1632d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
163330b45033STakashi Iwai 	add_secret_dac_path(codec);
1634620e2b28STakashi Iwai 
1635ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
163612daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1637ab6734e7SLydia Wang 	if (err < 0) {
1638ab6734e7SLydia Wang 		via_free(codec);
1639ab6734e7SLydia Wang 		return err;
1640ab6734e7SLydia Wang 	}
1641ab6734e7SLydia Wang 
1642096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1812_init_verbs;
1643ab6734e7SLydia Wang 
1644ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
1645ab6734e7SLydia Wang 
16463e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1812;
1647ab6734e7SLydia Wang 	return 0;
1648ab6734e7SLydia Wang }
1649ab6734e7SLydia Wang 
165043737e0aSLydia Wang /* patch for vt3476 */
165143737e0aSLydia Wang 
165243737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = {
165343737e0aSLydia Wang 	/* Enable DMic 8/16/32K */
165443737e0aSLydia Wang 	{0x1, 0xF7B, 0x30},
165543737e0aSLydia Wang 	/* Enable Boost Volume backdoor */
165643737e0aSLydia Wang 	{0x1, 0xFB9, 0x20},
165743737e0aSLydia Wang 	/* Enable AOW-MW9 path */
165843737e0aSLydia Wang 	{0x1, 0xFB8, 0x10},
165943737e0aSLydia Wang 	{ }
166043737e0aSLydia Wang };
166143737e0aSLydia Wang 
166243737e0aSLydia Wang static void set_widgets_power_state_vt3476(struct hda_codec *codec)
166343737e0aSLydia Wang {
166443737e0aSLydia Wang 	struct via_spec *spec = codec->spec;
166543737e0aSLydia Wang 	int imux_is_smixer;
166643737e0aSLydia Wang 	unsigned int parm, parm2;
166743737e0aSLydia Wang 	/* MUX10 (1eh) = stereo mixer */
166843737e0aSLydia Wang 	imux_is_smixer =
166943737e0aSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 4;
167043737e0aSLydia Wang 	/* inputs */
167143737e0aSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
167243737e0aSLydia Wang 	parm = AC_PWRST_D3;
167343737e0aSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
167443737e0aSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
167543737e0aSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
167643737e0aSLydia Wang 	if (imux_is_smixer)
167743737e0aSLydia Wang 		parm = AC_PWRST_D0;
167843737e0aSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
167943737e0aSLydia Wang 	update_power_state(codec, 0x1e, parm);
168043737e0aSLydia Wang 	update_power_state(codec, 0x1f, parm);
168143737e0aSLydia Wang 	update_power_state(codec, 0x10, parm);
168243737e0aSLydia Wang 	update_power_state(codec, 0x11, parm);
168343737e0aSLydia Wang 
168443737e0aSLydia Wang 	/* outputs */
168543737e0aSLydia Wang 	/* PW3 (27h), MW3(37h), AOW3 (bh) */
168643737e0aSLydia Wang 	if (spec->codec_type == VT1705CF) {
168743737e0aSLydia Wang 		parm = AC_PWRST_D3;
168843737e0aSLydia Wang 		update_power_state(codec, 0x27, parm);
168943737e0aSLydia Wang 		update_power_state(codec, 0x37, parm);
169043737e0aSLydia Wang 	}	else {
169143737e0aSLydia Wang 		parm = AC_PWRST_D3;
169243737e0aSLydia Wang 		set_pin_power_state(codec, 0x27, &parm);
169343737e0aSLydia Wang 		update_power_state(codec, 0x37, parm);
169443737e0aSLydia Wang 	}
169543737e0aSLydia Wang 
169643737e0aSLydia Wang 	/* PW2 (26h), MW2(36h), AOW2 (ah) */
169743737e0aSLydia Wang 	parm = AC_PWRST_D3;
169843737e0aSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
169943737e0aSLydia Wang 	update_power_state(codec, 0x36, parm);
1700*b3f6008fSTakashi Iwai 	if (smart51_enabled(codec)) {
170143737e0aSLydia Wang 		/* PW7(2bh), MW7(3bh), MUX7(1Bh) */
170243737e0aSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
170343737e0aSLydia Wang 		update_power_state(codec, 0x3b, parm);
170443737e0aSLydia Wang 		update_power_state(codec, 0x1b, parm);
170543737e0aSLydia Wang 	}
170643737e0aSLydia Wang 	update_conv_power_state(codec, 0xa, parm, 2);
170743737e0aSLydia Wang 
170843737e0aSLydia Wang 	/* PW1 (25h), MW1(35h), AOW1 (9h) */
170943737e0aSLydia Wang 	parm = AC_PWRST_D3;
171043737e0aSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
171143737e0aSLydia Wang 	update_power_state(codec, 0x35, parm);
1712*b3f6008fSTakashi Iwai 	if (smart51_enabled(codec)) {
171343737e0aSLydia Wang 		/* PW6(2ah), MW6(3ah), MUX6(1ah) */
171443737e0aSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
171543737e0aSLydia Wang 		update_power_state(codec, 0x3a, parm);
171643737e0aSLydia Wang 		update_power_state(codec, 0x1a, parm);
171743737e0aSLydia Wang 	}
171843737e0aSLydia Wang 	update_conv_power_state(codec, 0x9, parm, 1);
171943737e0aSLydia Wang 
172043737e0aSLydia Wang 	/* PW4 (28h), MW4 (38h), MUX4(18h), AOW3(bh)/AOW0(8h) */
172143737e0aSLydia Wang 	parm = AC_PWRST_D3;
172243737e0aSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
172343737e0aSLydia Wang 	update_power_state(codec, 0x38, parm);
172443737e0aSLydia Wang 	update_power_state(codec, 0x18, parm);
1725*b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
172643737e0aSLydia Wang 		update_conv_power_state(codec, 0xb, parm, 3);
172743737e0aSLydia Wang 	parm2 = parm; /* for pin 0x0b */
172843737e0aSLydia Wang 
172943737e0aSLydia Wang 	/* PW0 (24h), MW0(34h), MW9(3fh), AOW0 (8h) */
173043737e0aSLydia Wang 	parm = AC_PWRST_D3;
173143737e0aSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
173243737e0aSLydia Wang 	update_power_state(codec, 0x34, parm);
1733*b3f6008fSTakashi Iwai 	if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3)
173443737e0aSLydia Wang 		parm = parm2;
173543737e0aSLydia Wang 	update_conv_power_state(codec, 0x8, parm, 0);
173643737e0aSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
173743737e0aSLydia Wang 	update_power_state(codec, 0x3f, imux_is_smixer ? AC_PWRST_D0 : parm);
173843737e0aSLydia Wang }
173943737e0aSLydia Wang 
174043737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec)
174143737e0aSLydia Wang {
174243737e0aSLydia Wang 	struct via_spec *spec;
174343737e0aSLydia Wang 	int err;
174443737e0aSLydia Wang 
174543737e0aSLydia Wang 	/* create a codec specific record */
174643737e0aSLydia Wang 	spec = via_new_spec(codec);
174743737e0aSLydia Wang 	if (spec == NULL)
174843737e0aSLydia Wang 		return -ENOMEM;
174943737e0aSLydia Wang 
1750*b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x3f;
175143737e0aSLydia Wang 	add_secret_dac_path(codec);
175243737e0aSLydia Wang 
175343737e0aSLydia Wang 	/* automatic parse from the BIOS config */
175443737e0aSLydia Wang 	err = via_parse_auto_config(codec);
175543737e0aSLydia Wang 	if (err < 0) {
175643737e0aSLydia Wang 		via_free(codec);
175743737e0aSLydia Wang 		return err;
175843737e0aSLydia Wang 	}
175943737e0aSLydia Wang 
176043737e0aSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs;
176143737e0aSLydia Wang 
176243737e0aSLydia Wang 	codec->patch_ops = via_patch_ops;
176343737e0aSLydia Wang 
176443737e0aSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt3476;
176543737e0aSLydia Wang 
176643737e0aSLydia Wang 	return 0;
176743737e0aSLydia Wang }
176843737e0aSLydia Wang 
1769c577b8a1SJoseph Chan /*
1770c577b8a1SJoseph Chan  * patch entries
1771c577b8a1SJoseph Chan  */
177290dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = {
17733218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
17743218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
17753218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
17763218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
17773218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
1778ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17793218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
1780ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17813218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
1782ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17833218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
1784ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17853218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
1786ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17873218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
1788ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17893218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
1790ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17913218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
1792ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17933218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
1794ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
17953218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
1796ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
17973218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
1798ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
17993218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
1800ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18013218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
1802ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18033218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
1804ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18053218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
1806ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18073218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
1808ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18093218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
1810d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18113218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
1812d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18133218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
1814d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18153218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
1816d949cac1SHarald Welte 	  .patch = patch_vt1708S},
1817bc92df7fSLydia Wang 	{ .id = 0x11064397, .name = "VT1705",
1818d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18193218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
1820d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18213218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
1822d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18233218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
1824d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18253218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
1826d949cac1SHarald Welte 	  .patch = patch_vt1702},
18273218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
1828d949cac1SHarald Welte 	  .patch = patch_vt1702},
18293218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
1830d949cac1SHarald Welte 	  .patch = patch_vt1702},
18313218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
1832d949cac1SHarald Welte 	  .patch = patch_vt1702},
18333218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
1834d949cac1SHarald Welte 	  .patch = patch_vt1702},
18353218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
1836d949cac1SHarald Welte 	  .patch = patch_vt1702},
18373218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
1838d949cac1SHarald Welte 	  .patch = patch_vt1702},
18393218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
1840d949cac1SHarald Welte 	  .patch = patch_vt1702},
1841eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
1842eb7188caSLydia Wang 	  .patch = patch_vt1718S},
1843eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
1844eb7188caSLydia Wang 	  .patch = patch_vt1718S},
1845bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
1846bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
1847bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
1848bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
1849f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
1850f3db423dSLydia Wang 	  .patch = patch_vt1716S},
1851f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
1852f3db423dSLydia Wang 	  .patch = patch_vt1716S},
185325eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
185425eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
1855ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
185636dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
185736dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
185811890956SLydia Wang 	{ .id = 0x11060446, .name = "VT1802",
185911890956SLydia Wang 		.patch = patch_vt2002P},
186011890956SLydia Wang 	{ .id = 0x11068446, .name = "VT1802",
186111890956SLydia Wang 		.patch = patch_vt2002P},
186243737e0aSLydia Wang 	{ .id = 0x11064760, .name = "VT1705CF",
186343737e0aSLydia Wang 		.patch = patch_vt3476},
18646121b84aSLydia Wang 	{ .id = 0x11064761, .name = "VT1708SCE",
18656121b84aSLydia Wang 		.patch = patch_vt3476},
18666121b84aSLydia Wang 	{ .id = 0x11064762, .name = "VT1808",
18676121b84aSLydia Wang 		.patch = patch_vt3476},
1868c577b8a1SJoseph Chan 	{} /* terminator */
1869c577b8a1SJoseph Chan };
18701289e9e8STakashi Iwai 
18711289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
18721289e9e8STakashi Iwai 
18731289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
18741289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
18751289e9e8STakashi Iwai 	.owner = THIS_MODULE,
18761289e9e8STakashi Iwai };
18771289e9e8STakashi Iwai 
18781289e9e8STakashi Iwai MODULE_LICENSE("GPL");
18791289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
18801289e9e8STakashi Iwai 
18811289e9e8STakashi Iwai static int __init patch_via_init(void)
18821289e9e8STakashi Iwai {
18831289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
18841289e9e8STakashi Iwai }
18851289e9e8STakashi Iwai 
18861289e9e8STakashi Iwai static void __exit patch_via_exit(void)
18871289e9e8STakashi Iwai {
18881289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
18891289e9e8STakashi Iwai }
18901289e9e8STakashi Iwai 
18911289e9e8STakashi Iwai module_init(patch_via_init)
18921289e9e8STakashi Iwai module_exit(patch_via_exit)
1893