xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision 139611705ad5ce7b35b8b7957c5ca406deb3ff9b)
1c577b8a1SJoseph Chan /*
2c577b8a1SJoseph Chan  * Universal Interface for Intel High Definition Audio Codec
3c577b8a1SJoseph Chan  *
48e86597fSLydia Wang  * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
5c577b8a1SJoseph Chan  *
68e86597fSLydia Wang  *  (C) 2006-2009 VIA Technology, Inc.
78e86597fSLydia Wang  *  (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
8c577b8a1SJoseph Chan  *
9c577b8a1SJoseph Chan  *  This driver is free software; you can redistribute it and/or modify
10c577b8a1SJoseph Chan  *  it under the terms of the GNU General Public License as published by
11c577b8a1SJoseph Chan  *  the Free Software Foundation; either version 2 of the License, or
12c577b8a1SJoseph Chan  *  (at your option) any later version.
13c577b8a1SJoseph Chan  *
14c577b8a1SJoseph Chan  *  This driver is distributed in the hope that it will be useful,
15c577b8a1SJoseph Chan  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16c577b8a1SJoseph Chan  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17c577b8a1SJoseph Chan  *  GNU General Public License for more details.
18c577b8a1SJoseph Chan  *
19c577b8a1SJoseph Chan  *  You should have received a copy of the GNU General Public License
20c577b8a1SJoseph Chan  *  along with this program; if not, write to the Free Software
21c577b8a1SJoseph Chan  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22c577b8a1SJoseph Chan  */
23c577b8a1SJoseph Chan 
24c577b8a1SJoseph Chan /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
25c577b8a1SJoseph Chan /*									     */
26c577b8a1SJoseph Chan /* 2006-03-03  Lydia Wang  Create the basic patch to support VT1708 codec    */
27c577b8a1SJoseph Chan /* 2006-03-14  Lydia Wang  Modify hard code for some pin widget nid	     */
28c577b8a1SJoseph Chan /* 2006-08-02  Lydia Wang  Add support to VT1709 codec			     */
29c577b8a1SJoseph Chan /* 2006-09-08  Lydia Wang  Fix internal loopback recording source select bug */
30f7278fd0SJosepch Chan /* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization	     */
31f7278fd0SJosepch Chan /* 2007-09-17  Lydia Wang  Add VT1708B codec support			    */
3276d9b0ddSHarald Welte /* 2007-11-14  Lydia Wang  Add VT1708A codec HP and CD pin connect config    */
33fb4cb772SHarald Welte /* 2008-02-03  Lydia Wang  Fix Rear channels and Back channels inverse issue */
34d949cac1SHarald Welte /* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support	     */
3569e52a80SHarald Welte /* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin	     */
360aa62aefSHarald Welte /* 2008-04-09  Lydia Wang  Add Independent HP feature			     */
3798aa34c0SHarald Welte /* 2008-05-28  Lydia Wang  Add second S/PDIF Out support for VT1702	     */
38d7426329SHarald Welte /* 2008-09-15  Logan Li	   Add VT1708S Mic Boost workaround/backdoor	     */
398e86597fSLydia Wang /* 2009-02-16  Logan Li	   Add support for VT1718S			     */
408e86597fSLydia Wang /* 2009-03-13  Logan Li	   Add support for VT1716S			     */
418e86597fSLydia Wang /* 2009-04-14  Lydai Wang  Add support for VT1828S and VT2020		     */
428e86597fSLydia Wang /* 2009-07-08  Lydia Wang  Add support for VT2002P			     */
438e86597fSLydia Wang /* 2009-07-21  Lydia Wang  Add support for VT1812			     */
4436dd5c4aSLydia Wang /* 2009-09-19  Lydia Wang  Add support for VT1818S			     */
45c577b8a1SJoseph Chan /*									     */
46c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47c577b8a1SJoseph Chan 
48c577b8a1SJoseph Chan 
49c577b8a1SJoseph Chan #include <linux/init.h>
50c577b8a1SJoseph Chan #include <linux/delay.h>
51c577b8a1SJoseph Chan #include <linux/slab.h>
52da155d5bSPaul Gortmaker #include <linux/module.h>
53c577b8a1SJoseph Chan #include <sound/core.h>
540aa62aefSHarald Welte #include <sound/asoundef.h>
55c577b8a1SJoseph Chan #include "hda_codec.h"
56c577b8a1SJoseph Chan #include "hda_local.h"
57128bc4baSTakashi Iwai #include "hda_auto_parser.h"
581835a0f9STakashi Iwai #include "hda_jack.h"
59b3f6008fSTakashi Iwai #include "hda_generic.h"
60c577b8a1SJoseph Chan 
61c577b8a1SJoseph Chan /* Pin Widget NID */
6276d9b0ddSHarald Welte #define VT1708_HP_PIN_NID	0x20
6376d9b0ddSHarald Welte #define VT1708_CD_PIN_NID	0x24
64c577b8a1SJoseph Chan 
65d7426329SHarald Welte enum VIA_HDA_CODEC {
66d7426329SHarald Welte 	UNKNOWN = -1,
67d7426329SHarald Welte 	VT1708,
68d7426329SHarald Welte 	VT1709_10CH,
69d7426329SHarald Welte 	VT1709_6CH,
70d7426329SHarald Welte 	VT1708B_8CH,
71d7426329SHarald Welte 	VT1708B_4CH,
72d7426329SHarald Welte 	VT1708S,
73518bf3baSLydia Wang 	VT1708BCE,
74d7426329SHarald Welte 	VT1702,
75eb7188caSLydia Wang 	VT1718S,
76f3db423dSLydia Wang 	VT1716S,
7725eaba2fSLydia Wang 	VT2002P,
78ab6734e7SLydia Wang 	VT1812,
7911890956SLydia Wang 	VT1802,
8043737e0aSLydia Wang 	VT1705CF,
816121b84aSLydia Wang 	VT1808,
82d7426329SHarald Welte 	CODEC_TYPES,
83d7426329SHarald Welte };
84d7426329SHarald Welte 
8511890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \
8611890956SLydia Wang 	((spec)->codec_type == VT2002P ||\
8711890956SLydia Wang 	 (spec)->codec_type == VT1812 ||\
8811890956SLydia Wang 	 (spec)->codec_type == VT1802)
8911890956SLydia Wang 
901f2e99feSLydia Wang struct via_spec {
91b3f6008fSTakashi Iwai 	struct hda_gen_spec gen;
92b3f6008fSTakashi Iwai 
931f2e99feSLydia Wang 	/* codec parameterization */
9490dd48a1STakashi Iwai 	const struct snd_kcontrol_new *mixers[6];
951f2e99feSLydia Wang 	unsigned int num_mixers;
961f2e99feSLydia Wang 
9790dd48a1STakashi Iwai 	const struct hda_verb *init_verbs[5];
981f2e99feSLydia Wang 	unsigned int num_iverbs;
991f2e99feSLydia Wang 
1001f2e99feSLydia Wang 	/* HP mode source */
101f3db423dSLydia Wang 	unsigned int dmic_enabled;
10224088a58STakashi Iwai 	unsigned int no_pin_power_ctl;
1031f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1041f2e99feSLydia Wang 
105e9d010c2STakashi Iwai 	/* analog low-power control */
106e9d010c2STakashi Iwai 	bool alc_mode;
107e9d010c2STakashi Iwai 
1081f2e99feSLydia Wang 	/* work to check hp jack state */
109187d333eSTakashi Iwai 	int hp_work_active;
110e06e5a29STakashi Iwai 	int vt1708_jack_detect;
1113e95b9abSLydia Wang 
1123e95b9abSLydia Wang 	void (*set_widgets_power_state)(struct hda_codec *codec);
11343737e0aSLydia Wang 	unsigned int dac_stream_tag[4];
1141f2e99feSLydia Wang };
1151f2e99feSLydia Wang 
1160341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
117b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
118b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
119b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
120b3f6008fSTakashi Iwai 				  int action);
121b3f6008fSTakashi Iwai static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl);
122b3f6008fSTakashi Iwai 
1235b0cb1d8SJaroslav Kysela static struct via_spec *via_new_spec(struct hda_codec *codec)
1245b0cb1d8SJaroslav Kysela {
1255b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1265b0cb1d8SJaroslav Kysela 
1275b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1285b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1295b0cb1d8SJaroslav Kysela 		return NULL;
1305b0cb1d8SJaroslav Kysela 
1315b0cb1d8SJaroslav Kysela 	codec->spec = spec;
132b3f6008fSTakashi Iwai 	snd_hda_gen_spec_init(&spec->gen);
1330341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
1340341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
1350341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
1360341ccd7SLydia Wang 		spec->codec_type = VT1708S;
137b3f6008fSTakashi Iwai 	spec->no_pin_power_ctl = 1;
138*13961170STakashi Iwai 	spec->gen.indep_hp = 1;
139b3f6008fSTakashi Iwai 	spec->gen.pcm_playback_hook = via_playback_pcm_hook;
1405b0cb1d8SJaroslav Kysela 	return spec;
1415b0cb1d8SJaroslav Kysela }
1425b0cb1d8SJaroslav Kysela 
143744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
144d7426329SHarald Welte {
145744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
146d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
147d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
148d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
149d7426329SHarald Welte 
150d7426329SHarald Welte 	/* get codec type */
151d7426329SHarald Welte 	if (ven_id != 0x1106)
152d7426329SHarald Welte 		codec_type = UNKNOWN;
153d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
154d7426329SHarald Welte 		codec_type = VT1708;
155d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
156d7426329SHarald Welte 		codec_type = VT1709_10CH;
157d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
158d7426329SHarald Welte 		codec_type = VT1709_6CH;
159518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
160d7426329SHarald Welte 		codec_type = VT1708B_8CH;
161518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
162518bf3baSLydia Wang 			codec_type = VT1708BCE;
163518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
164d7426329SHarald Welte 		codec_type = VT1708B_4CH;
165d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
166d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
167d7426329SHarald Welte 		codec_type = VT1708S;
168d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
169d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
170d7426329SHarald Welte 		codec_type = VT1702;
171eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
172eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
173eb7188caSLydia Wang 		codec_type = VT1718S;
174f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
175f3db423dSLydia Wang 		codec_type = VT1716S;
176bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
177bb3c6bfcSLydia Wang 		codec_type = VT1718S;
17825eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
17925eaba2fSLydia Wang 		codec_type = VT2002P;
180ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
181ab6734e7SLydia Wang 		codec_type = VT1812;
18236dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
18336dd5c4aSLydia Wang 		codec_type = VT1708S;
18411890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
18511890956SLydia Wang 		codec_type = VT1802;
18643737e0aSLydia Wang 	else if (dev_id == 0x4760)
18743737e0aSLydia Wang 		codec_type = VT1705CF;
1886121b84aSLydia Wang 	else if (dev_id == 0x4761 || dev_id == 0x4762)
1896121b84aSLydia Wang 		codec_type = VT1808;
190d7426329SHarald Welte 	else
191d7426329SHarald Welte 		codec_type = UNKNOWN;
192d7426329SHarald Welte 	return codec_type;
193d7426329SHarald Welte };
194d7426329SHarald Welte 
195ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec);
196ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec);
1971f2e99feSLydia Wang 
198187d333eSTakashi Iwai #define hp_detect_with_aa(codec) \
199187d333eSTakashi Iwai 	(snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
200187d333eSTakashi Iwai 	 !is_aa_path_mute(codec))
2011f2e99feSLydia Wang 
202b3f6008fSTakashi Iwai static void vt1708_stop_hp_work(struct hda_codec *codec)
2031f2e99feSLydia Wang {
204b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
205b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
2061f2e99feSLydia Wang 		return;
207187d333eSTakashi Iwai 	if (spec->hp_work_active) {
208b3f6008fSTakashi Iwai 		snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
209b3f6008fSTakashi Iwai 		cancel_delayed_work_sync(&codec->jackpoll_work);
210b3f6008fSTakashi Iwai 		spec->hp_work_active = false;
211b3f6008fSTakashi Iwai 		codec->jackpoll_interval = 0;
212187d333eSTakashi Iwai 	}
213187d333eSTakashi Iwai }
214187d333eSTakashi Iwai 
215b3f6008fSTakashi Iwai static void vt1708_update_hp_work(struct hda_codec *codec)
216187d333eSTakashi Iwai {
217b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
218b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
219187d333eSTakashi Iwai 		return;
220187d333eSTakashi Iwai 	if (spec->vt1708_jack_detect &&
221b3f6008fSTakashi Iwai 	    (spec->gen.active_streams || hp_detect_with_aa(codec))) {
222187d333eSTakashi Iwai 		if (!spec->hp_work_active) {
223b3f6008fSTakashi Iwai 			codec->jackpoll_interval = msecs_to_jiffies(100);
224b3f6008fSTakashi Iwai 			snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0);
225b3f6008fSTakashi Iwai 			queue_delayed_work(codec->bus->workq,
226b3f6008fSTakashi Iwai 					   &codec->jackpoll_work, 0);
227b3f6008fSTakashi Iwai 			spec->hp_work_active = true;
228187d333eSTakashi Iwai 		}
229b3f6008fSTakashi Iwai 	} else if (!hp_detect_with_aa(codec))
230b3f6008fSTakashi Iwai 		vt1708_stop_hp_work(codec);
2311f2e99feSLydia Wang }
232f5271101SLydia Wang 
2333e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec)
2343e95b9abSLydia Wang {
2353e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
2363e95b9abSLydia Wang 	if (spec->set_widgets_power_state)
2373e95b9abSLydia Wang 		spec->set_widgets_power_state(codec);
2383e95b9abSLydia Wang }
23925eaba2fSLydia Wang 
240054d867eSTakashi Iwai static void update_power_state(struct hda_codec *codec, hda_nid_t nid,
241054d867eSTakashi Iwai 			       unsigned int parm)
242054d867eSTakashi Iwai {
243054d867eSTakashi Iwai 	if (snd_hda_codec_read(codec, nid, 0,
244054d867eSTakashi Iwai 			       AC_VERB_GET_POWER_STATE, 0) == parm)
245054d867eSTakashi Iwai 		return;
246054d867eSTakashi Iwai 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
247054d867eSTakashi Iwai }
248054d867eSTakashi Iwai 
24943737e0aSLydia Wang static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid,
25043737e0aSLydia Wang 			       unsigned int parm, unsigned int index)
25143737e0aSLydia Wang {
25243737e0aSLydia Wang 	struct via_spec *spec = codec->spec;
25343737e0aSLydia Wang 	unsigned int format;
25443737e0aSLydia Wang 	if (snd_hda_codec_read(codec, nid, 0,
25543737e0aSLydia Wang 			       AC_VERB_GET_POWER_STATE, 0) == parm)
25643737e0aSLydia Wang 		return;
25743737e0aSLydia Wang 	format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
25843737e0aSLydia Wang 	if (format && (spec->dac_stream_tag[index] != format))
25943737e0aSLydia Wang 		spec->dac_stream_tag[index] = format;
26043737e0aSLydia Wang 
26143737e0aSLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
26243737e0aSLydia Wang 	if (parm == AC_PWRST_D0) {
26343737e0aSLydia Wang 		format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
26443737e0aSLydia Wang 		if (!format && (spec->dac_stream_tag[index] != format))
26543737e0aSLydia Wang 			snd_hda_codec_write(codec, nid, 0,
26643737e0aSLydia Wang 						  AC_VERB_SET_CHANNEL_STREAMID,
26743737e0aSLydia Wang 						  spec->dac_stream_tag[index]);
26843737e0aSLydia Wang 	}
26943737e0aSLydia Wang }
27043737e0aSLydia Wang 
271b3f6008fSTakashi Iwai static bool smart51_enabled(struct hda_codec *codec)
272b3f6008fSTakashi Iwai {
273b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
274b3f6008fSTakashi Iwai 	return spec->gen.ext_channel_count > 2;
275b3f6008fSTakashi Iwai }
276b3f6008fSTakashi Iwai 
277b3f6008fSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
278b3f6008fSTakashi Iwai {
279b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
280b3f6008fSTakashi Iwai 	int i;
281b3f6008fSTakashi Iwai 
282b3f6008fSTakashi Iwai 	for (i = 0; i < spec->gen.multi_ios; i++)
283b3f6008fSTakashi Iwai 		if (spec->gen.multi_io[i].pin == pin)
284b3f6008fSTakashi Iwai 			return true;
285b3f6008fSTakashi Iwai 	return false;
286b3f6008fSTakashi Iwai }
287b3f6008fSTakashi Iwai 
288f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
289f5271101SLydia Wang 				unsigned int *affected_parm)
290f5271101SLydia Wang {
291f5271101SLydia Wang 	unsigned parm;
292f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
293f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
294f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
295f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
2961564b287SLydia Wang 	struct via_spec *spec = codec->spec;
29724088a58STakashi Iwai 	unsigned present = 0;
29824088a58STakashi Iwai 
29924088a58STakashi Iwai 	no_presence |= spec->no_pin_power_ctl;
30024088a58STakashi Iwai 	if (!no_presence)
30124088a58STakashi Iwai 		present = snd_hda_jack_detect(codec, nid);
302b3f6008fSTakashi Iwai 	if ((smart51_enabled(codec) && is_smart51_pins(codec, nid))
3031564b287SLydia Wang 	    || ((no_presence || present)
3041564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
305f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
306f5271101SLydia Wang 		parm = AC_PWRST_D0;
307f5271101SLydia Wang 	} else
308f5271101SLydia Wang 		parm = AC_PWRST_D3;
309f5271101SLydia Wang 
310054d867eSTakashi Iwai 	update_power_state(codec, nid, parm);
311f5271101SLydia Wang }
312f5271101SLydia Wang 
31324088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
31424088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
31524088a58STakashi Iwai {
316dda415d4STakashi Iwai 	return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
31724088a58STakashi Iwai }
31824088a58STakashi Iwai 
31924088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
32024088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
32124088a58STakashi Iwai {
32224088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
32324088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
32424088a58STakashi Iwai 	ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
32524088a58STakashi Iwai 	return 0;
32624088a58STakashi Iwai }
32724088a58STakashi Iwai 
32824088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
32924088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
33024088a58STakashi Iwai {
33124088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
33224088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
33324088a58STakashi Iwai 	unsigned int val = !ucontrol->value.enumerated.item[0];
33424088a58STakashi Iwai 
33524088a58STakashi Iwai 	if (val == spec->no_pin_power_ctl)
33624088a58STakashi Iwai 		return 0;
33724088a58STakashi Iwai 	spec->no_pin_power_ctl = val;
33824088a58STakashi Iwai 	set_widgets_power_state(codec);
339e9d010c2STakashi Iwai 	analog_low_current_mode(codec);
34024088a58STakashi Iwai 	return 1;
34124088a58STakashi Iwai }
34224088a58STakashi Iwai 
343b3f6008fSTakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = {
344b3f6008fSTakashi Iwai 	{
34524088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
34624088a58STakashi Iwai 	.name = "Dynamic Power-Control",
34724088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
34824088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
34924088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
350b3f6008fSTakashi Iwai 	},
351b3f6008fSTakashi Iwai 	{} /* terminator */
35224088a58STakashi Iwai };
35324088a58STakashi Iwai 
35424088a58STakashi Iwai 
355f5271101SLydia Wang /* check AA path's mute status */
356ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec)
357ada509ecSTakashi Iwai {
358ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
359ada509ecSTakashi Iwai 	const struct hda_amp_list *p;
360ada509ecSTakashi Iwai 	int i, ch, v;
361ada509ecSTakashi Iwai 
362b3f6008fSTakashi Iwai 	for (i = 0; i < spec->gen.num_loopbacks; i++) {
363b3f6008fSTakashi Iwai 		p = &spec->gen.loopback_list[i];
364ada509ecSTakashi Iwai 		for (ch = 0; ch < 2; ch++) {
365ada509ecSTakashi Iwai 			v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
366ada509ecSTakashi Iwai 						   p->idx);
367ada509ecSTakashi Iwai 			if (!(v & HDA_AMP_MUTE) && v > 0)
368ada509ecSTakashi Iwai 				return false;
369f5271101SLydia Wang 		}
370f5271101SLydia Wang 	}
371ada509ecSTakashi Iwai 	return true;
372f5271101SLydia Wang }
373f5271101SLydia Wang 
374f5271101SLydia Wang /* enter/exit analog low-current mode */
375e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force)
376f5271101SLydia Wang {
377f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
378ada509ecSTakashi Iwai 	bool enable;
379ada509ecSTakashi Iwai 	unsigned int verb, parm;
380f5271101SLydia Wang 
381e9d010c2STakashi Iwai 	if (spec->no_pin_power_ctl)
382e9d010c2STakashi Iwai 		enable = false;
383e9d010c2STakashi Iwai 	else
384b3f6008fSTakashi Iwai 		enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
385e9d010c2STakashi Iwai 	if (enable == spec->alc_mode && !force)
386e9d010c2STakashi Iwai 		return;
387e9d010c2STakashi Iwai 	spec->alc_mode = enable;
388f5271101SLydia Wang 
389f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
390f5271101SLydia Wang 	switch (spec->codec_type) {
391f5271101SLydia Wang 	case VT1708B_8CH:
392f5271101SLydia Wang 	case VT1708B_4CH:
393f5271101SLydia Wang 		verb = 0xf70;
394f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
395f5271101SLydia Wang 		break;
396f5271101SLydia Wang 	case VT1708S:
397eb7188caSLydia Wang 	case VT1718S:
398f3db423dSLydia Wang 	case VT1716S:
399f5271101SLydia Wang 		verb = 0xf73;
400f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
401f5271101SLydia Wang 		break;
402f5271101SLydia Wang 	case VT1702:
403f5271101SLydia Wang 		verb = 0xf73;
404f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
405f5271101SLydia Wang 		break;
40625eaba2fSLydia Wang 	case VT2002P:
407ab6734e7SLydia Wang 	case VT1812:
40811890956SLydia Wang 	case VT1802:
40925eaba2fSLydia Wang 		verb = 0xf93;
41025eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
41125eaba2fSLydia Wang 		break;
41243737e0aSLydia Wang 	case VT1705CF:
4136121b84aSLydia Wang 	case VT1808:
41443737e0aSLydia Wang 		verb = 0xf82;
41543737e0aSLydia Wang 		parm = enable ? 0x00 : 0xe0;  /* 0x00: 4/40x, 0xe0: 1x */
41643737e0aSLydia Wang 		break;
417f5271101SLydia Wang 	default:
418f5271101SLydia Wang 		return;		/* other codecs are not supported */
419f5271101SLydia Wang 	}
420f5271101SLydia Wang 	/* send verb */
421f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
422f5271101SLydia Wang }
423f5271101SLydia Wang 
424e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec)
425e9d010c2STakashi Iwai {
426e9d010c2STakashi Iwai 	return __analog_low_current_mode(codec, false);
427e9d010c2STakashi Iwai }
428e9d010c2STakashi Iwai 
429c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
430c577b8a1SJoseph Chan {
431c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
4325b0cb1d8SJaroslav Kysela 	int err, i;
433c577b8a1SJoseph Chan 
434b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_controls(codec);
435b3f6008fSTakashi Iwai 	if (err < 0)
436b3f6008fSTakashi Iwai 		return err;
437b3f6008fSTakashi Iwai 
43824088a58STakashi Iwai 	if (spec->set_widgets_power_state)
439b3f6008fSTakashi Iwai 		spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum;
44024088a58STakashi Iwai 
441c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
442c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
443c577b8a1SJoseph Chan 		if (err < 0)
444c577b8a1SJoseph Chan 			return err;
445c577b8a1SJoseph Chan 	}
446c577b8a1SJoseph Chan 
447c577b8a1SJoseph Chan 	return 0;
448c577b8a1SJoseph Chan }
449c577b8a1SJoseph Chan 
450b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
451b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
452b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
453b3f6008fSTakashi Iwai 				  int action)
454c577b8a1SJoseph Chan {
455b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
456b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
457c577b8a1SJoseph Chan }
458c577b8a1SJoseph Chan 
459c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
460c577b8a1SJoseph Chan {
461c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
462c577b8a1SJoseph Chan 
463c577b8a1SJoseph Chan 	if (!spec)
464c577b8a1SJoseph Chan 		return;
465c577b8a1SJoseph Chan 
466b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
467b3f6008fSTakashi Iwai 	snd_hda_gen_spec_free(&spec->gen);
468a86a88eaSTakashi Iwai 	kfree(spec);
469c577b8a1SJoseph Chan }
470c577b8a1SJoseph Chan 
4712a43952aSTakashi Iwai #ifdef CONFIG_PM
47268cb2b55STakashi Iwai static int via_suspend(struct hda_codec *codec)
4731f2e99feSLydia Wang {
4741f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
475b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
47694c142a1SDavid Henningsson 
47794c142a1SDavid Henningsson 	if (spec->codec_type == VT1802) {
47894c142a1SDavid Henningsson 		/* Fix pop noise on headphones */
47994c142a1SDavid Henningsson 		int i;
480b3f6008fSTakashi Iwai 		for (i = 0; i < spec->gen.autocfg.hp_outs; i++)
481b3f6008fSTakashi Iwai 			snd_hda_set_pin_ctl(codec, spec->gen.autocfg.hp_pins[i], 0);
48294c142a1SDavid Henningsson 	}
48394c142a1SDavid Henningsson 
4841f2e99feSLydia Wang 	return 0;
4851f2e99feSLydia Wang }
4861f2e99feSLydia Wang #endif
4871f2e99feSLydia Wang 
48883012a7cSTakashi Iwai #ifdef CONFIG_PM
489cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
490cb53c626STakashi Iwai {
491cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
492b3f6008fSTakashi Iwai 	set_widgets_power_state(codec);
493b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
494b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
495b3f6008fSTakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
496cb53c626STakashi Iwai }
497cb53c626STakashi Iwai #endif
498cb53c626STakashi Iwai 
499c577b8a1SJoseph Chan /*
500c577b8a1SJoseph Chan  */
5015d41762aSTakashi Iwai 
5025d41762aSTakashi Iwai static int via_init(struct hda_codec *codec);
5035d41762aSTakashi Iwai 
50490dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
505c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
506b3f6008fSTakashi Iwai 	.build_pcms = snd_hda_gen_build_pcms,
507c577b8a1SJoseph Chan 	.init = via_init,
508c577b8a1SJoseph Chan 	.free = via_free,
5094e2d16d3SDavid Henningsson 	.unsol_event = snd_hda_jack_unsol_event,
5102a43952aSTakashi Iwai #ifdef CONFIG_PM
5111f2e99feSLydia Wang 	.suspend = via_suspend,
512cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
513cb53c626STakashi Iwai #endif
514c577b8a1SJoseph Chan };
515c577b8a1SJoseph Chan 
5164a79616dSTakashi Iwai 
517b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = {
518b3f6008fSTakashi Iwai 	/* power down jack detect function */
519b3f6008fSTakashi Iwai 	{0x1, 0xf81, 0x1},
520b3f6008fSTakashi Iwai 	{ }
5214a79616dSTakashi Iwai };
52276d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
52376d9b0ddSHarald Welte {
52476d9b0ddSHarald Welte 	unsigned int def_conf;
52576d9b0ddSHarald Welte 	unsigned char seqassoc;
52676d9b0ddSHarald Welte 
5272f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
52876d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
52976d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
53082ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
53182ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
53276d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
5332f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
53476d9b0ddSHarald Welte 	}
53576d9b0ddSHarald Welte 
53676d9b0ddSHarald Welte 	return;
53776d9b0ddSHarald Welte }
53876d9b0ddSHarald Welte 
539e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
5401f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
5411f2e99feSLydia Wang {
5421f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
5431f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
5441f2e99feSLydia Wang 
5451f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
5461f2e99feSLydia Wang 		return 0;
547e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
5481f2e99feSLydia Wang 	return 0;
5491f2e99feSLydia Wang }
5501f2e99feSLydia Wang 
551e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
5521f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
5531f2e99feSLydia Wang {
5541f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
5551f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
556187d333eSTakashi Iwai 	int val;
5571f2e99feSLydia Wang 
5581f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
5591f2e99feSLydia Wang 		return 0;
560187d333eSTakashi Iwai 	val = !!ucontrol->value.integer.value[0];
561187d333eSTakashi Iwai 	if (spec->vt1708_jack_detect == val)
562187d333eSTakashi Iwai 		return 0;
563187d333eSTakashi Iwai 	spec->vt1708_jack_detect = val;
564b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
565187d333eSTakashi Iwai 	return 1;
5661f2e99feSLydia Wang }
5671f2e99feSLydia Wang 
568b3f6008fSTakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = {
569b3f6008fSTakashi Iwai 	{
5701f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5711f2e99feSLydia Wang 	.name = "Jack Detect",
5721f2e99feSLydia Wang 	.count = 1,
5731f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
574e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
575e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
576b3f6008fSTakashi Iwai 	},
577b3f6008fSTakashi Iwai 	{} /* terminator */
5781f2e99feSLydia Wang };
5791f2e99feSLydia Wang 
580b3f6008fSTakashi Iwai static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl)
5814e2d16d3SDavid Henningsson {
5824e2d16d3SDavid Henningsson 	set_widgets_power_state(codec);
583b3f6008fSTakashi Iwai 	snd_hda_gen_hp_automute(codec, tbl);
584b3f6008fSTakashi Iwai }
585b3f6008fSTakashi Iwai 
586b3f6008fSTakashi Iwai static void via_line_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl)
587b3f6008fSTakashi Iwai {
588b3f6008fSTakashi Iwai 	set_widgets_power_state(codec);
589b3f6008fSTakashi Iwai 	snd_hda_gen_line_automute(codec, tbl);
5904e2d16d3SDavid Henningsson }
5914e2d16d3SDavid Henningsson 
5924e2d16d3SDavid Henningsson static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_tbl *tbl)
5934e2d16d3SDavid Henningsson {
5944e2d16d3SDavid Henningsson 	set_widgets_power_state(codec);
5954e2d16d3SDavid Henningsson }
5964e2d16d3SDavid Henningsson 
597b3f6008fSTakashi Iwai #define VIA_JACK_EVENT	(HDA_GEN_LAST_EVENT + 1)
598b3f6008fSTakashi Iwai 
599b3f6008fSTakashi Iwai static void via_set_jack_unsol_events(struct hda_codec *codec)
6004a918ffeSTakashi Iwai {
6014a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
602b3f6008fSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->gen.autocfg;
603b3f6008fSTakashi Iwai 	hda_nid_t pin;
6044a918ffeSTakashi Iwai 	int i;
6054a918ffeSTakashi Iwai 
606b3f6008fSTakashi Iwai 	spec->gen.hp_automute_hook = via_hp_automute;
6074a918ffeSTakashi Iwai 	if (cfg->speaker_pins[0])
608b3f6008fSTakashi Iwai 		spec->gen.line_automute_hook = via_line_automute;
6094e2d16d3SDavid Henningsson 
6104a918ffeSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
611b3f6008fSTakashi Iwai 		pin = cfg->line_out_pins[i];
612b3f6008fSTakashi Iwai 		if (pin && !snd_hda_jack_tbl_get(codec, pin) &&
613b3f6008fSTakashi Iwai 		    is_jack_detectable(codec, pin))
614b3f6008fSTakashi Iwai 			snd_hda_jack_detect_enable_callback(codec, pin,
6154e2d16d3SDavid Henningsson 							    VIA_JACK_EVENT,
6164e2d16d3SDavid Henningsson 							    via_jack_powerstate_event);
6174a918ffeSTakashi Iwai 	}
618b3f6008fSTakashi Iwai 
619b3f6008fSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
620b3f6008fSTakashi Iwai 		pin = cfg->line_out_pins[i];
621b3f6008fSTakashi Iwai 		if (pin && !snd_hda_jack_tbl_get(codec, pin) &&
622b3f6008fSTakashi Iwai 		    is_jack_detectable(codec, pin))
623b3f6008fSTakashi Iwai 			snd_hda_jack_detect_enable_callback(codec, pin,
624b3f6008fSTakashi Iwai 							    VIA_JACK_EVENT,
625b3f6008fSTakashi Iwai 							    via_jack_powerstate_event);
626b3f6008fSTakashi Iwai 	}
627b3f6008fSTakashi Iwai }
628b3f6008fSTakashi Iwai 
629b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec)
630b3f6008fSTakashi Iwai {
631b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
632b3f6008fSTakashi Iwai 	int err;
633b3f6008fSTakashi Iwai 
634b3f6008fSTakashi Iwai 	err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
635b3f6008fSTakashi Iwai 	if (err < 0)
636b3f6008fSTakashi Iwai 		return err;
637b3f6008fSTakashi Iwai 
638b3f6008fSTakashi Iwai 	err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
639b3f6008fSTakashi Iwai 	if (err < 0)
640b3f6008fSTakashi Iwai 		return err;
641b3f6008fSTakashi Iwai 
642b3f6008fSTakashi Iwai 	via_set_jack_unsol_events(codec);
643b3f6008fSTakashi Iwai 	return 0;
6444a918ffeSTakashi Iwai }
6454a918ffeSTakashi Iwai 
6465d41762aSTakashi Iwai static int via_init(struct hda_codec *codec)
6475d41762aSTakashi Iwai {
6485d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
6495d41762aSTakashi Iwai 	int i;
6505d41762aSTakashi Iwai 
6515d41762aSTakashi Iwai 	for (i = 0; i < spec->num_iverbs; i++)
6525d41762aSTakashi Iwai 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
6535d41762aSTakashi Iwai 
654e9d010c2STakashi Iwai 	/* init power states */
655e9d010c2STakashi Iwai 	set_widgets_power_state(codec);
656e9d010c2STakashi Iwai 	__analog_low_current_mode(codec, true);
657e9d010c2STakashi Iwai 
658b3f6008fSTakashi Iwai 	snd_hda_gen_init(codec);
65911890956SLydia Wang 
660b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
66125eaba2fSLydia Wang 
662c577b8a1SJoseph Chan 	return 0;
663c577b8a1SJoseph Chan }
664c577b8a1SJoseph Chan 
665b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec)
666337b9d02STakashi Iwai {
667337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
668b3f6008fSTakashi Iwai 	int i, err;
669337b9d02STakashi Iwai 
670b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_pcms(codec);
671b3f6008fSTakashi Iwai 	if (err < 0 || codec->vendor_id != 0x11061708)
672b3f6008fSTakashi Iwai 		return err;
673b3f6008fSTakashi Iwai 
674b3f6008fSTakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
675b3f6008fSTakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
676b3f6008fSTakashi Iwai 	 * disable the 24bit format, so far.
677b3f6008fSTakashi Iwai 	 */
678b3f6008fSTakashi Iwai 	for (i = 0; i < codec->num_pcms; i++) {
679b3f6008fSTakashi Iwai 		struct hda_pcm *info = &spec->gen.pcm_rec[i];
680b3f6008fSTakashi Iwai 		if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
681b3f6008fSTakashi Iwai 		    info->pcm_type != HDA_PCM_TYPE_AUDIO)
682b3f6008fSTakashi Iwai 			continue;
683b3f6008fSTakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats =
684b3f6008fSTakashi Iwai 			SNDRV_PCM_FMTBIT_S16_LE;
685337b9d02STakashi Iwai 	}
686b3f6008fSTakashi Iwai 
6871c55d521STakashi Iwai 	return 0;
688337b9d02STakashi Iwai }
689337b9d02STakashi Iwai 
690c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
691c577b8a1SJoseph Chan {
692c577b8a1SJoseph Chan 	struct via_spec *spec;
693c577b8a1SJoseph Chan 	int err;
694c577b8a1SJoseph Chan 
695c577b8a1SJoseph Chan 	/* create a codec specific record */
6965b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
697c577b8a1SJoseph Chan 	if (spec == NULL)
698c577b8a1SJoseph Chan 		return -ENOMEM;
699c577b8a1SJoseph Chan 
700b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x17;
701b3f6008fSTakashi Iwai 
702b3f6008fSTakashi Iwai 	/* set jackpoll_interval while parsing the codec */
703b3f6008fSTakashi Iwai 	codec->jackpoll_interval = msecs_to_jiffies(100);
704b3f6008fSTakashi Iwai 	spec->vt1708_jack_detect = 1;
705b3f6008fSTakashi Iwai 
706b3f6008fSTakashi Iwai 	/* don't support the input jack switching due to lack of unsol event */
707b3f6008fSTakashi Iwai 	/* (it may work with polling, though, but it needs testing) */
708b3f6008fSTakashi Iwai 	spec->gen.suppress_auto_mic = 1;
709620e2b28STakashi Iwai 
71012daef65STakashi Iwai 	/* Add HP and CD pin config connect bit re-config action */
71112daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
71212daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
71312daef65STakashi Iwai 
714c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
71512daef65STakashi Iwai 	err = via_parse_auto_config(codec);
716c577b8a1SJoseph Chan 	if (err < 0) {
717c577b8a1SJoseph Chan 		via_free(codec);
718c577b8a1SJoseph Chan 		return err;
719c577b8a1SJoseph Chan 	}
720c577b8a1SJoseph Chan 
72112daef65STakashi Iwai 	/* add jack detect on/off control */
722b3f6008fSTakashi Iwai 	spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl;
723c577b8a1SJoseph Chan 
724e322a36dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
725e322a36dSLydia Wang 
726c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
727b3f6008fSTakashi Iwai 	codec->patch_ops.build_pcms = vt1708_build_pcms;
728c577b8a1SJoseph Chan 
729b3f6008fSTakashi Iwai 	/* clear jackpoll_interval again; it's set dynamically */
730b3f6008fSTakashi Iwai 	codec->jackpoll_interval = 0;
731b3f6008fSTakashi Iwai 
732c577b8a1SJoseph Chan 	return 0;
733c577b8a1SJoseph Chan }
734c577b8a1SJoseph Chan 
735ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec)
736c577b8a1SJoseph Chan {
737c577b8a1SJoseph Chan 	struct via_spec *spec;
738c577b8a1SJoseph Chan 	int err;
739c577b8a1SJoseph Chan 
740c577b8a1SJoseph Chan 	/* create a codec specific record */
7415b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
742c577b8a1SJoseph Chan 	if (spec == NULL)
743c577b8a1SJoseph Chan 		return -ENOMEM;
744c577b8a1SJoseph Chan 
745b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x18;
746620e2b28STakashi Iwai 
74712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
748c577b8a1SJoseph Chan 	if (err < 0) {
749c577b8a1SJoseph Chan 		via_free(codec);
750c577b8a1SJoseph Chan 		return err;
751c577b8a1SJoseph Chan 	}
752c577b8a1SJoseph Chan 
753c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
754c577b8a1SJoseph Chan 
755f7278fd0SJosepch Chan 	return 0;
756f7278fd0SJosepch Chan }
757f7278fd0SJosepch Chan 
7583e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
7593e95b9abSLydia Wang {
7603e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
7613e95b9abSLydia Wang 	int imux_is_smixer;
7623e95b9abSLydia Wang 	unsigned int parm;
7633e95b9abSLydia Wang 	int is_8ch = 0;
764bc92df7fSLydia Wang 	if ((spec->codec_type != VT1708B_4CH) &&
765bc92df7fSLydia Wang 	    (codec->vendor_id != 0x11064397))
7663e95b9abSLydia Wang 		is_8ch = 1;
7673e95b9abSLydia Wang 
7683e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
7693e95b9abSLydia Wang 	imux_is_smixer =
7703e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
7713e95b9abSLydia Wang 	 == ((spec->codec_type == VT1708S) ? 5 : 0));
7723e95b9abSLydia Wang 	/* inputs */
7733e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
7743e95b9abSLydia Wang 	parm = AC_PWRST_D3;
7753e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
7763e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
7773e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
7783e95b9abSLydia Wang 	if (imux_is_smixer)
7793e95b9abSLydia Wang 		parm = AC_PWRST_D0;
7803e95b9abSLydia Wang 	/* SW0 (17h), AIW 0/1 (13h/14h) */
781054d867eSTakashi Iwai 	update_power_state(codec, 0x17, parm);
782054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
783054d867eSTakashi Iwai 	update_power_state(codec, 0x14, parm);
7843e95b9abSLydia Wang 
7853e95b9abSLydia Wang 	/* outputs */
7863e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
7873e95b9abSLydia Wang 	parm = AC_PWRST_D3;
7883e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
789b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
7903e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
791054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
792054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
7933e95b9abSLydia Wang 
7943e95b9abSLydia Wang 	/* PW6 (22h), SW2 (26h), AOW2 (24h) */
7953e95b9abSLydia Wang 	if (is_8ch) {
7963e95b9abSLydia Wang 		parm = AC_PWRST_D3;
7973e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
798b3f6008fSTakashi Iwai 		if (smart51_enabled(codec))
7993e95b9abSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
800054d867eSTakashi Iwai 		update_power_state(codec, 0x26, parm);
801054d867eSTakashi Iwai 		update_power_state(codec, 0x24, parm);
802bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397) {
803bc92df7fSLydia Wang 		/* PW7(23h), SW2(27h), AOW2(25h) */
804bc92df7fSLydia Wang 		parm = AC_PWRST_D3;
805bc92df7fSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
806b3f6008fSTakashi Iwai 		if (smart51_enabled(codec))
807bc92df7fSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
808054d867eSTakashi Iwai 		update_power_state(codec, 0x27, parm);
809054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
8103e95b9abSLydia Wang 	}
8113e95b9abSLydia Wang 
8123e95b9abSLydia Wang 	/* PW 3/4/7 (1ch/1dh/23h) */
8133e95b9abSLydia Wang 	parm = AC_PWRST_D3;
8143e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
8153e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
8163e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
8173e95b9abSLydia Wang 	if (is_8ch)
8183e95b9abSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
8193e95b9abSLydia Wang 
8203e95b9abSLydia Wang 	/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
821054d867eSTakashi Iwai 	update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
822054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
8233e95b9abSLydia Wang 	if (is_8ch) {
824054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
825054d867eSTakashi Iwai 		update_power_state(codec, 0x27, parm);
826b3f6008fSTakashi Iwai 	} else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled)
827054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
8283e95b9abSLydia Wang }
8293e95b9abSLydia Wang 
830518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
831ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec)
832f7278fd0SJosepch Chan {
833f7278fd0SJosepch Chan 	struct via_spec *spec;
834f7278fd0SJosepch Chan 	int err;
835f7278fd0SJosepch Chan 
836518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
837518bf3baSLydia Wang 		return patch_vt1708S(codec);
838ddd304d8STakashi Iwai 
839f7278fd0SJosepch Chan 	/* create a codec specific record */
8405b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
841f7278fd0SJosepch Chan 	if (spec == NULL)
842f7278fd0SJosepch Chan 		return -ENOMEM;
843f7278fd0SJosepch Chan 
844b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
845620e2b28STakashi Iwai 
846f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
84712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
848f7278fd0SJosepch Chan 	if (err < 0) {
849f7278fd0SJosepch Chan 		via_free(codec);
850f7278fd0SJosepch Chan 		return err;
851f7278fd0SJosepch Chan 	}
852f7278fd0SJosepch Chan 
853f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
854f7278fd0SJosepch Chan 
8553e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
8563e95b9abSLydia Wang 
857f7278fd0SJosepch Chan 	return 0;
858f7278fd0SJosepch Chan }
859f7278fd0SJosepch Chan 
860d949cac1SHarald Welte /* Patch for VT1708S */
861096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = {
862d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
863d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
864bc7e7e5cSLydia Wang 	/* don't bybass mixer */
865bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
866d949cac1SHarald Welte 	{ }
867d949cac1SHarald Welte };
868d949cac1SHarald Welte 
8696369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
8706369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
8716369bcfcSLydia Wang {
8726369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
8736369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
8746369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
8756369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
8766369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
8776369bcfcSLydia Wang }
8786369bcfcSLydia Wang 
879d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
880d949cac1SHarald Welte {
881d949cac1SHarald Welte 	struct via_spec *spec;
882d949cac1SHarald Welte 	int err;
883d949cac1SHarald Welte 
884d949cac1SHarald Welte 	/* create a codec specific record */
8855b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
886d949cac1SHarald Welte 	if (spec == NULL)
887d949cac1SHarald Welte 		return -ENOMEM;
888d949cac1SHarald Welte 
889b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
890d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
891d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
892620e2b28STakashi Iwai 
893518bf3baSLydia Wang 	/* correct names for VT1708BCE */
894518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
895518bf3baSLydia Wang 		kfree(codec->chip_name);
896518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
897518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
898518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
899518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
900970f630fSLydia Wang 	}
901bc92df7fSLydia Wang 	/* correct names for VT1705 */
902bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)	{
903bc92df7fSLydia Wang 		kfree(codec->chip_name);
904bc92df7fSLydia Wang 		codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
905bc92df7fSLydia Wang 		snprintf(codec->bus->card->mixername,
906bc92df7fSLydia Wang 			 sizeof(codec->bus->card->mixername),
907bc92df7fSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
908bc92df7fSLydia Wang 	}
909b3f6008fSTakashi Iwai 
910b3f6008fSTakashi Iwai 	/* automatic parse from the BIOS config */
911b3f6008fSTakashi Iwai 	err = via_parse_auto_config(codec);
912b3f6008fSTakashi Iwai 	if (err < 0) {
913b3f6008fSTakashi Iwai 		via_free(codec);
914b3f6008fSTakashi Iwai 		return err;
915b3f6008fSTakashi Iwai 	}
916b3f6008fSTakashi Iwai 
917b3f6008fSTakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
918b3f6008fSTakashi Iwai 
919b3f6008fSTakashi Iwai 	codec->patch_ops = via_patch_ops;
920b3f6008fSTakashi Iwai 
9213e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
922d949cac1SHarald Welte 	return 0;
923d949cac1SHarald Welte }
924d949cac1SHarald Welte 
925d949cac1SHarald Welte /* Patch for VT1702 */
926d949cac1SHarald Welte 
927096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = {
928bc7e7e5cSLydia Wang 	/* mixer enable */
929bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
930bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
931bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
932d949cac1SHarald Welte 	{ }
933d949cac1SHarald Welte };
934d949cac1SHarald Welte 
9353e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec)
9363e95b9abSLydia Wang {
9373e95b9abSLydia Wang 	int imux_is_smixer =
9383e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
9393e95b9abSLydia Wang 	unsigned int parm;
9403e95b9abSLydia Wang 	/* inputs */
9413e95b9abSLydia Wang 	/* PW 1/2/5 (14h/15h/18h) */
9423e95b9abSLydia Wang 	parm = AC_PWRST_D3;
9433e95b9abSLydia Wang 	set_pin_power_state(codec, 0x14, &parm);
9443e95b9abSLydia Wang 	set_pin_power_state(codec, 0x15, &parm);
9453e95b9abSLydia Wang 	set_pin_power_state(codec, 0x18, &parm);
9463e95b9abSLydia Wang 	if (imux_is_smixer)
9473e95b9abSLydia Wang 		parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
9483e95b9abSLydia Wang 	/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
949054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
950054d867eSTakashi Iwai 	update_power_state(codec, 0x12, parm);
951054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
952054d867eSTakashi Iwai 	update_power_state(codec, 0x20, parm);
9533e95b9abSLydia Wang 
9543e95b9abSLydia Wang 	/* outputs */
9553e95b9abSLydia Wang 	/* PW 3/4 (16h/17h) */
9563e95b9abSLydia Wang 	parm = AC_PWRST_D3;
9573e95b9abSLydia Wang 	set_pin_power_state(codec, 0x17, &parm);
9583e95b9abSLydia Wang 	set_pin_power_state(codec, 0x16, &parm);
9593e95b9abSLydia Wang 	/* MW0 (1ah), AOW 0/1 (10h/1dh) */
960054d867eSTakashi Iwai 	update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm);
961054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
962054d867eSTakashi Iwai 	update_power_state(codec, 0x1d, parm);
9633e95b9abSLydia Wang }
9643e95b9abSLydia Wang 
965d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
966d949cac1SHarald Welte {
967d949cac1SHarald Welte 	struct via_spec *spec;
968d949cac1SHarald Welte 	int err;
969d949cac1SHarald Welte 
970d949cac1SHarald Welte 	/* create a codec specific record */
9715b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
972d949cac1SHarald Welte 	if (spec == NULL)
973d949cac1SHarald Welte 		return -ENOMEM;
974d949cac1SHarald Welte 
975b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x1a;
976620e2b28STakashi Iwai 
97712daef65STakashi Iwai 	/* limit AA path volume to 0 dB */
97812daef65STakashi Iwai 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
97912daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
98012daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
98112daef65STakashi Iwai 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
98212daef65STakashi Iwai 				  (1 << AC_AMPCAP_MUTE_SHIFT));
98312daef65STakashi Iwai 
984d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
98512daef65STakashi Iwai 	err = via_parse_auto_config(codec);
986d949cac1SHarald Welte 	if (err < 0) {
987d949cac1SHarald Welte 		via_free(codec);
988d949cac1SHarald Welte 		return err;
989d949cac1SHarald Welte 	}
990d949cac1SHarald Welte 
991096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
992d949cac1SHarald Welte 
993d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
994d949cac1SHarald Welte 
9953e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
996d949cac1SHarald Welte 	return 0;
997d949cac1SHarald Welte }
998d949cac1SHarald Welte 
999eb7188caSLydia Wang /* Patch for VT1718S */
1000eb7188caSLydia Wang 
1001096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = {
10024ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
10034ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
1004eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
1005eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
10065d41762aSTakashi Iwai 
1007eb7188caSLydia Wang 	{ }
1008eb7188caSLydia Wang };
1009eb7188caSLydia Wang 
10103e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
10113e95b9abSLydia Wang {
10123e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
10133e95b9abSLydia Wang 	int imux_is_smixer;
10146162552bSTakashi Iwai 	unsigned int parm, parm2;
10153e95b9abSLydia Wang 	/* MUX6 (1eh) = stereo mixer */
10163e95b9abSLydia Wang 	imux_is_smixer =
10173e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
10183e95b9abSLydia Wang 	/* inputs */
10193e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
10203e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10213e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
10223e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
10233e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
10243e95b9abSLydia Wang 	if (imux_is_smixer)
10253e95b9abSLydia Wang 		parm = AC_PWRST_D0;
10263e95b9abSLydia Wang 	/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
1027054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
1028054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
1029054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1030054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
10313e95b9abSLydia Wang 
10323e95b9abSLydia Wang 	/* outputs */
10333e95b9abSLydia Wang 	/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
10343e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10353e95b9abSLydia Wang 	set_pin_power_state(codec, 0x27, &parm);
1036054d867eSTakashi Iwai 	update_power_state(codec, 0x1a, parm);
10376162552bSTakashi Iwai 	parm2 = parm; /* for pin 0x0b */
10383e95b9abSLydia Wang 
10393e95b9abSLydia Wang 	/* PW2 (26h), AOW2 (ah) */
10403e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10413e95b9abSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
1042b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
10433e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
1044054d867eSTakashi Iwai 	update_power_state(codec, 0xa, parm);
10453e95b9abSLydia Wang 
10463e95b9abSLydia Wang 	/* PW0 (24h), AOW0 (8h) */
10473e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10483e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
1049b3f6008fSTakashi Iwai 	if (!spec->gen.indep_hp_enabled) /* check for redirected HP */
10503e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
1051054d867eSTakashi Iwai 	update_power_state(codec, 0x8, parm);
1052b3f6008fSTakashi Iwai 	if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3)
10536162552bSTakashi Iwai 		parm = parm2;
10546162552bSTakashi Iwai 	update_power_state(codec, 0xb, parm);
10553e95b9abSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
1056054d867eSTakashi Iwai 	update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm);
10573e95b9abSLydia Wang 
10583e95b9abSLydia Wang 	/* PW1 (25h), AOW1 (9h) */
10593e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10603e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
1061b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
10623e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
1063054d867eSTakashi Iwai 	update_power_state(codec, 0x9, parm);
10643e95b9abSLydia Wang 
1065b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled) {
10663e95b9abSLydia Wang 		/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
10673e95b9abSLydia Wang 		parm = AC_PWRST_D3;
10683e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
1069054d867eSTakashi Iwai 		update_power_state(codec, 0x1b, parm);
1070054d867eSTakashi Iwai 		update_power_state(codec, 0x34, parm);
1071054d867eSTakashi Iwai 		update_power_state(codec, 0xc, parm);
10723e95b9abSLydia Wang 	}
10733e95b9abSLydia Wang }
10743e95b9abSLydia Wang 
107530b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs
107630b45033STakashi Iwai  * This isn't listed from the raw info, but the chip has a secret connection.
107730b45033STakashi Iwai  */
107830b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec)
107930b45033STakashi Iwai {
108030b45033STakashi Iwai 	struct via_spec *spec = codec->spec;
108130b45033STakashi Iwai 	int i, nums;
108230b45033STakashi Iwai 	hda_nid_t conn[8];
108330b45033STakashi Iwai 	hda_nid_t nid;
108430b45033STakashi Iwai 
1085b3f6008fSTakashi Iwai 	if (!spec->gen.mixer_nid)
108630b45033STakashi Iwai 		return 0;
1087b3f6008fSTakashi Iwai 	nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
108830b45033STakashi Iwai 				       ARRAY_SIZE(conn) - 1);
108930b45033STakashi Iwai 	for (i = 0; i < nums; i++) {
109030b45033STakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
109130b45033STakashi Iwai 			return 0;
109230b45033STakashi Iwai 	}
109330b45033STakashi Iwai 
109430b45033STakashi Iwai 	/* find the primary DAC and add to the connection list */
109530b45033STakashi Iwai 	nid = codec->start_nid;
109630b45033STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, nid++) {
109730b45033STakashi Iwai 		unsigned int caps = get_wcaps(codec, nid);
109830b45033STakashi Iwai 		if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
109930b45033STakashi Iwai 		    !(caps & AC_WCAP_DIGITAL)) {
110030b45033STakashi Iwai 			conn[nums++] = nid;
110130b45033STakashi Iwai 			return snd_hda_override_conn_list(codec,
1102b3f6008fSTakashi Iwai 							  spec->gen.mixer_nid,
110330b45033STakashi Iwai 							  nums, conn);
110430b45033STakashi Iwai 		}
110530b45033STakashi Iwai 	}
110630b45033STakashi Iwai 	return 0;
110730b45033STakashi Iwai }
110830b45033STakashi Iwai 
110930b45033STakashi Iwai 
1110eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
1111eb7188caSLydia Wang {
1112eb7188caSLydia Wang 	struct via_spec *spec;
1113eb7188caSLydia Wang 	int err;
1114eb7188caSLydia Wang 
1115eb7188caSLydia Wang 	/* create a codec specific record */
11165b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1117eb7188caSLydia Wang 	if (spec == NULL)
1118eb7188caSLydia Wang 		return -ENOMEM;
1119eb7188caSLydia Wang 
1120b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1121d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1122d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
112330b45033STakashi Iwai 	add_secret_dac_path(codec);
1124620e2b28STakashi Iwai 
1125eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
112612daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1127eb7188caSLydia Wang 	if (err < 0) {
1128eb7188caSLydia Wang 		via_free(codec);
1129eb7188caSLydia Wang 		return err;
1130eb7188caSLydia Wang 	}
1131eb7188caSLydia Wang 
1132096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
1133eb7188caSLydia Wang 
1134eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
1135eb7188caSLydia Wang 
11363e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1718S;
11373e95b9abSLydia Wang 
1138eb7188caSLydia Wang 	return 0;
1139eb7188caSLydia Wang }
1140f3db423dSLydia Wang 
1141f3db423dSLydia Wang /* Patch for VT1716S */
1142f3db423dSLydia Wang 
1143f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
1144f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
1145f3db423dSLydia Wang {
1146f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
1147f3db423dSLydia Wang 	uinfo->count = 1;
1148f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
1149f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
1150f3db423dSLydia Wang 	return 0;
1151f3db423dSLydia Wang }
1152f3db423dSLydia Wang 
1153f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
1154f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
1155f3db423dSLydia Wang {
1156f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1157f3db423dSLydia Wang 	int index = 0;
1158f3db423dSLydia Wang 
1159f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
1160f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
1161f3db423dSLydia Wang 	if (index != -1)
1162f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
1163f3db423dSLydia Wang 
1164f3db423dSLydia Wang 	return 0;
1165f3db423dSLydia Wang }
1166f3db423dSLydia Wang 
1167f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
1168f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
1169f3db423dSLydia Wang {
1170f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1171f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
1172f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
1173f3db423dSLydia Wang 
1174f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
1175f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
1176f3db423dSLydia Wang 	spec->dmic_enabled = index;
11773e95b9abSLydia Wang 	set_widgets_power_state(codec);
1178f3db423dSLydia Wang 	return 1;
1179f3db423dSLydia Wang }
1180f3db423dSLydia Wang 
118190dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
1182f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
1183f3db423dSLydia Wang 	{
1184f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1185f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
11865b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
1187f3db423dSLydia Wang 	 .count = 1,
1188f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
1189f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
1190f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
1191f3db423dSLydia Wang 	 },
1192f3db423dSLydia Wang 	{}			/* end */
1193f3db423dSLydia Wang };
1194f3db423dSLydia Wang 
1195f3db423dSLydia Wang 
1196f3db423dSLydia Wang /* mono-out mixer elements */
119790dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
1198f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
1199f3db423dSLydia Wang 	{ } /* end */
1200f3db423dSLydia Wang };
1201f3db423dSLydia Wang 
1202096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = {
1203f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
1204f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
1205f3db423dSLydia Wang 	/* don't bybass mixer */
1206f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
1207f3db423dSLydia Wang 	/* Enable mono output */
1208f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
1209f3db423dSLydia Wang 	{ }
1210f3db423dSLydia Wang };
1211f3db423dSLydia Wang 
12123e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
12133e95b9abSLydia Wang {
12143e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
12153e95b9abSLydia Wang 	int imux_is_smixer;
12163e95b9abSLydia Wang 	unsigned int parm;
12173e95b9abSLydia Wang 	unsigned int mono_out, present;
12183e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
12193e95b9abSLydia Wang 	imux_is_smixer =
12203e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0,
12213e95b9abSLydia Wang 			    AC_VERB_GET_CONNECT_SEL, 0x00) ==  5);
12223e95b9abSLydia Wang 	/* inputs */
12233e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
12243e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12253e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
12263e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
12273e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
12283e95b9abSLydia Wang 	if (imux_is_smixer)
12293e95b9abSLydia Wang 		parm = AC_PWRST_D0;
12303e95b9abSLydia Wang 	/* SW0 (17h), AIW0(13h) */
1231054d867eSTakashi Iwai 	update_power_state(codec, 0x17, parm);
1232054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
12333e95b9abSLydia Wang 
12343e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12353e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
12363e95b9abSLydia Wang 	/* PW11 (22h) */
12373e95b9abSLydia Wang 	if (spec->dmic_enabled)
12383e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
12393e95b9abSLydia Wang 	else
1240054d867eSTakashi Iwai 		update_power_state(codec, 0x22, AC_PWRST_D3);
12413e95b9abSLydia Wang 
12423e95b9abSLydia Wang 	/* SW2(26h), AIW1(14h) */
1243054d867eSTakashi Iwai 	update_power_state(codec, 0x26, parm);
1244054d867eSTakashi Iwai 	update_power_state(codec, 0x14, parm);
12453e95b9abSLydia Wang 
12463e95b9abSLydia Wang 	/* outputs */
12473e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
12483e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12493e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
12503e95b9abSLydia Wang 	/* Smart 5.1 PW2(1bh) */
1251b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
12523e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
1253054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
1254054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
12553e95b9abSLydia Wang 
12563e95b9abSLydia Wang 	/* PW7 (23h), SW3 (27h), AOW3 (25h) */
12573e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12583e95b9abSLydia Wang 	set_pin_power_state(codec, 0x23, &parm);
12593e95b9abSLydia Wang 	/* Smart 5.1 PW1(1ah) */
1260b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
12613e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
1262054d867eSTakashi Iwai 	update_power_state(codec, 0x27, parm);
12633e95b9abSLydia Wang 
12643e95b9abSLydia Wang 	/* Smart 5.1 PW5(1eh) */
1265b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
12663e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
1267054d867eSTakashi Iwai 	update_power_state(codec, 0x25, parm);
12683e95b9abSLydia Wang 
12693e95b9abSLydia Wang 	/* Mono out */
12703e95b9abSLydia Wang 	/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
12713e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x1c);
12723e95b9abSLydia Wang 
12733e95b9abSLydia Wang 	if (present)
12743e95b9abSLydia Wang 		mono_out = 0;
12753e95b9abSLydia Wang 	else {
12763e95b9abSLydia Wang 		present = snd_hda_jack_detect(codec, 0x1d);
1277b3f6008fSTakashi Iwai 		if (!spec->gen.indep_hp_enabled && present)
12783e95b9abSLydia Wang 			mono_out = 0;
12793e95b9abSLydia Wang 		else
12803e95b9abSLydia Wang 			mono_out = 1;
12813e95b9abSLydia Wang 	}
12823e95b9abSLydia Wang 	parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
1283054d867eSTakashi Iwai 	update_power_state(codec, 0x28, parm);
1284054d867eSTakashi Iwai 	update_power_state(codec, 0x29, parm);
1285054d867eSTakashi Iwai 	update_power_state(codec, 0x2a, parm);
12863e95b9abSLydia Wang 
12873e95b9abSLydia Wang 	/* PW 3/4 (1ch/1dh) */
12883e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12893e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
12903e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
12913e95b9abSLydia Wang 	/* HP Independent Mode, power on AOW3 */
1292b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
1293054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
12943e95b9abSLydia Wang 
12953e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
12963e95b9abSLydia Wang 	/* MW0 (16h), AOW0 (10h) */
1297054d867eSTakashi Iwai 	update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
1298054d867eSTakashi Iwai 	update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm);
12993e95b9abSLydia Wang }
13003e95b9abSLydia Wang 
1301f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
1302f3db423dSLydia Wang {
1303f3db423dSLydia Wang 	struct via_spec *spec;
1304f3db423dSLydia Wang 	int err;
1305f3db423dSLydia Wang 
1306f3db423dSLydia Wang 	/* create a codec specific record */
13075b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1308f3db423dSLydia Wang 	if (spec == NULL)
1309f3db423dSLydia Wang 		return -ENOMEM;
1310f3db423dSLydia Wang 
1311b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
1312d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
1313d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
1314620e2b28STakashi Iwai 
1315f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
131612daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1317f3db423dSLydia Wang 	if (err < 0) {
1318f3db423dSLydia Wang 		via_free(codec);
1319f3db423dSLydia Wang 		return err;
1320f3db423dSLydia Wang 	}
1321f3db423dSLydia Wang 
1322096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_init_verbs;
1323f3db423dSLydia Wang 
1324b3f6008fSTakashi Iwai 	spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer;
1325f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
1326f3db423dSLydia Wang 
1327f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
1328f3db423dSLydia Wang 
13293e95b9abSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
1330f3db423dSLydia Wang 	return 0;
1331f3db423dSLydia Wang }
133225eaba2fSLydia Wang 
133325eaba2fSLydia Wang /* for vt2002P */
133425eaba2fSLydia Wang 
1335096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = {
1336eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
1337eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
1338eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
1339eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
134025eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
134125eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
134225eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
134325eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
134425eaba2fSLydia Wang 	{ }
134525eaba2fSLydia Wang };
13464a918ffeSTakashi Iwai 
1347096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = {
134811890956SLydia Wang 	/* Enable Boost Volume backdoor */
134911890956SLydia Wang 	{0x1, 0xfb9, 0x24},
135011890956SLydia Wang 	/* Enable AOW0 to MW9 */
135111890956SLydia Wang 	{0x1, 0xfb8, 0x88},
135211890956SLydia Wang 	{ }
135311890956SLydia Wang };
135425eaba2fSLydia Wang 
13553e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
13563e95b9abSLydia Wang {
13573e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
13583e95b9abSLydia Wang 	int imux_is_smixer;
13593e95b9abSLydia Wang 	unsigned int parm;
13603e95b9abSLydia Wang 	unsigned int present;
13613e95b9abSLydia Wang 	/* MUX9 (1eh) = stereo mixer */
13623e95b9abSLydia Wang 	imux_is_smixer =
13633e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
13643e95b9abSLydia Wang 	/* inputs */
13653e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
13663e95b9abSLydia Wang 	parm = AC_PWRST_D3;
13673e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
13683e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
13693e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
13703e95b9abSLydia Wang 	parm = AC_PWRST_D0;
13713e95b9abSLydia Wang 	/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
1372054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
1373054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
1374054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1375054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
13763e95b9abSLydia Wang 
13773e95b9abSLydia Wang 	/* outputs */
13783e95b9abSLydia Wang 	/* AOW0 (8h)*/
1379054d867eSTakashi Iwai 	update_power_state(codec, 0x8, parm);
13803e95b9abSLydia Wang 
138111890956SLydia Wang 	if (spec->codec_type == VT1802) {
138211890956SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
138311890956SLydia Wang 		parm = AC_PWRST_D3;
138411890956SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
1385054d867eSTakashi Iwai 		update_power_state(codec, 0x18, parm);
1386054d867eSTakashi Iwai 		update_power_state(codec, 0x38, parm);
138711890956SLydia Wang 	} else {
13883e95b9abSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
13893e95b9abSLydia Wang 		parm = AC_PWRST_D3;
13903e95b9abSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
1391054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, parm);
1392054d867eSTakashi Iwai 		update_power_state(codec, 0x37, parm);
139311890956SLydia Wang 	}
13943e95b9abSLydia Wang 
139511890956SLydia Wang 	if (spec->codec_type == VT1802) {
139611890956SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
139711890956SLydia Wang 		parm = AC_PWRST_D3;
139811890956SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
1399054d867eSTakashi Iwai 		update_power_state(codec, 0x15, parm);
1400054d867eSTakashi Iwai 		update_power_state(codec, 0x35, parm);
140111890956SLydia Wang 	} else {
14023e95b9abSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
14033e95b9abSLydia Wang 		parm = AC_PWRST_D3;
14043e95b9abSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
1405054d867eSTakashi Iwai 		update_power_state(codec, 0x19, parm);
1406054d867eSTakashi Iwai 		update_power_state(codec, 0x35, parm);
140711890956SLydia Wang 	}
14083e95b9abSLydia Wang 
1409b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
1410054d867eSTakashi Iwai 		update_power_state(codec, 0x9, AC_PWRST_D0);
14113e95b9abSLydia Wang 
14123e95b9abSLydia Wang 	/* Class-D */
14133e95b9abSLydia Wang 	/* PW0 (24h), MW0(18h/14h), MUX0(34h) */
14143e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
14153e95b9abSLydia Wang 
14163e95b9abSLydia Wang 	parm = AC_PWRST_D3;
14173e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
14183e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
141911890956SLydia Wang 	if (spec->codec_type == VT1802)
1420054d867eSTakashi Iwai 		update_power_state(codec, 0x14, parm);
142111890956SLydia Wang 	else
1422054d867eSTakashi Iwai 		update_power_state(codec, 0x18, parm);
1423054d867eSTakashi Iwai 	update_power_state(codec, 0x34, parm);
14243e95b9abSLydia Wang 
14253e95b9abSLydia Wang 	/* Mono Out */
14263e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x26);
14273e95b9abSLydia Wang 
14283e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
142911890956SLydia Wang 	if (spec->codec_type == VT1802) {
143011890956SLydia Wang 		/* PW15 (33h), MW8(1ch), MUX8(3ch) */
1431054d867eSTakashi Iwai 		update_power_state(codec, 0x33, parm);
1432054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, parm);
1433054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, parm);
143411890956SLydia Wang 	} else {
14353e95b9abSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
1436054d867eSTakashi Iwai 		update_power_state(codec, 0x31, parm);
1437054d867eSTakashi Iwai 		update_power_state(codec, 0x17, parm);
1438054d867eSTakashi Iwai 		update_power_state(codec, 0x3b, parm);
143911890956SLydia Wang 	}
14403e95b9abSLydia Wang 	/* MW9 (21h) */
14413e95b9abSLydia Wang 	if (imux_is_smixer || !is_aa_path_mute(codec))
1442054d867eSTakashi Iwai 		update_power_state(codec, 0x21, AC_PWRST_D0);
14433e95b9abSLydia Wang 	else
1444054d867eSTakashi Iwai 		update_power_state(codec, 0x21, AC_PWRST_D3);
14453e95b9abSLydia Wang }
144625eaba2fSLydia Wang 
14474b527b65SDavid Henningsson /*
14484b527b65SDavid Henningsson  * pin fix-up
14494b527b65SDavid Henningsson  */
14504b527b65SDavid Henningsson enum {
14514b527b65SDavid Henningsson 	VIA_FIXUP_INTMIC_BOOST,
1452d5266125STakashi Iwai 	VIA_FIXUP_ASUS_G75,
14534b527b65SDavid Henningsson };
14544b527b65SDavid Henningsson 
14554b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec,
14564b527b65SDavid Henningsson 				  const struct hda_fixup *fix, int action)
14574b527b65SDavid Henningsson {
14584b527b65SDavid Henningsson 	if (action == HDA_FIXUP_ACT_PRE_PROBE)
14594b527b65SDavid Henningsson 		override_mic_boost(codec, 0x30, 0, 2, 40);
14604b527b65SDavid Henningsson }
14614b527b65SDavid Henningsson 
14624b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = {
14634b527b65SDavid Henningsson 	[VIA_FIXUP_INTMIC_BOOST] = {
14644b527b65SDavid Henningsson 		.type = HDA_FIXUP_FUNC,
14654b527b65SDavid Henningsson 		.v.func = via_fixup_intmic_boost,
14664b527b65SDavid Henningsson 	},
1467d5266125STakashi Iwai 	[VIA_FIXUP_ASUS_G75] = {
1468d5266125STakashi Iwai 		.type = HDA_FIXUP_PINS,
1469d5266125STakashi Iwai 		.v.pins = (const struct hda_pintbl[]) {
1470d5266125STakashi Iwai 			/* set 0x24 and 0x33 as speakers */
1471d5266125STakashi Iwai 			{ 0x24, 0x991301f0 },
1472d5266125STakashi Iwai 			{ 0x33, 0x991301f1 }, /* subwoofer */
1473d5266125STakashi Iwai 			{ }
1474d5266125STakashi Iwai 		}
1475d5266125STakashi Iwai 	},
14764b527b65SDavid Henningsson };
14774b527b65SDavid Henningsson 
14784b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = {
1479d5266125STakashi Iwai 	SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
14804b527b65SDavid Henningsson 	SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
14814b527b65SDavid Henningsson 	{}
14824b527b65SDavid Henningsson };
14834b527b65SDavid Henningsson 
1484ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
1485ef4da458STakashi Iwai  * Replace this with mixer NID 0x1c
1486ef4da458STakashi Iwai  */
1487ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec)
1488ef4da458STakashi Iwai {
1489ef4da458STakashi Iwai 	static hda_nid_t conn_24[] = { 0x14, 0x1c };
1490ef4da458STakashi Iwai 	static hda_nid_t conn_33[] = { 0x1c };
1491ef4da458STakashi Iwai 
1492ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
1493ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
1494ef4da458STakashi Iwai }
1495ef4da458STakashi Iwai 
149625eaba2fSLydia Wang /* patch for vt2002P */
149725eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
149825eaba2fSLydia Wang {
149925eaba2fSLydia Wang 	struct via_spec *spec;
150025eaba2fSLydia Wang 	int err;
150125eaba2fSLydia Wang 
150225eaba2fSLydia Wang 	/* create a codec specific record */
15035b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
150425eaba2fSLydia Wang 	if (spec == NULL)
150525eaba2fSLydia Wang 		return -ENOMEM;
150625eaba2fSLydia Wang 
1507b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1508d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1509d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
1510ef4da458STakashi Iwai 	if (spec->codec_type == VT1802)
1511ef4da458STakashi Iwai 		fix_vt1802_connections(codec);
151230b45033STakashi Iwai 	add_secret_dac_path(codec);
1513620e2b28STakashi Iwai 
15144b527b65SDavid Henningsson 	snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
15154b527b65SDavid Henningsson 	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
15164b527b65SDavid Henningsson 
151725eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
151812daef65STakashi Iwai 	err = via_parse_auto_config(codec);
151925eaba2fSLydia Wang 	if (err < 0) {
152025eaba2fSLydia Wang 		via_free(codec);
152125eaba2fSLydia Wang 		return err;
152225eaba2fSLydia Wang 	}
152325eaba2fSLydia Wang 
152411890956SLydia Wang 	if (spec->codec_type == VT1802)
15254a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
152611890956SLydia Wang 	else
15274a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
152811890956SLydia Wang 
152925eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
153025eaba2fSLydia Wang 
15313e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt2002P;
153225eaba2fSLydia Wang 	return 0;
153325eaba2fSLydia Wang }
1534ab6734e7SLydia Wang 
1535ab6734e7SLydia Wang /* for vt1812 */
1536ab6734e7SLydia Wang 
1537096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = {
1538ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
1539ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
1540ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
1541ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
1542ab6734e7SLydia Wang 	{ }
1543ab6734e7SLydia Wang };
1544ab6734e7SLydia Wang 
15453e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec)
15463e95b9abSLydia Wang {
15473e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
15483e95b9abSLydia Wang 	unsigned int parm;
15493e95b9abSLydia Wang 	unsigned int present;
15503e95b9abSLydia Wang 	/* inputs */
15513e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
15523e95b9abSLydia Wang 	parm = AC_PWRST_D3;
15533e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
15543e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
15553e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
15563e95b9abSLydia Wang 	parm = AC_PWRST_D0;
15573e95b9abSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
1558054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
1559054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
1560054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1561054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
15623e95b9abSLydia Wang 
15633e95b9abSLydia Wang 	/* outputs */
15643e95b9abSLydia Wang 	/* AOW0 (8h)*/
1565054d867eSTakashi Iwai 	update_power_state(codec, 0x8, AC_PWRST_D0);
15663e95b9abSLydia Wang 
15673e95b9abSLydia Wang 	/* PW4 (28h), MW4 (18h), MUX4(38h) */
15683e95b9abSLydia Wang 	parm = AC_PWRST_D3;
15693e95b9abSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
1570054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
1571054d867eSTakashi Iwai 	update_power_state(codec, 0x38, parm);
15723e95b9abSLydia Wang 
15733e95b9abSLydia Wang 	/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
15743e95b9abSLydia Wang 	parm = AC_PWRST_D3;
15753e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
1576054d867eSTakashi Iwai 	update_power_state(codec, 0x15, parm);
1577054d867eSTakashi Iwai 	update_power_state(codec, 0x35, parm);
1578b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
1579054d867eSTakashi Iwai 		update_power_state(codec, 0x9, AC_PWRST_D0);
15803e95b9abSLydia Wang 
15813e95b9abSLydia Wang 	/* Internal Speaker */
15823e95b9abSLydia Wang 	/* PW0 (24h), MW0(14h), MUX0(34h) */
15833e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
15843e95b9abSLydia Wang 
15853e95b9abSLydia Wang 	parm = AC_PWRST_D3;
15863e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
15873e95b9abSLydia Wang 	if (present) {
1588054d867eSTakashi Iwai 		update_power_state(codec, 0x14, AC_PWRST_D3);
1589054d867eSTakashi Iwai 		update_power_state(codec, 0x34, AC_PWRST_D3);
15903e95b9abSLydia Wang 	} else {
1591054d867eSTakashi Iwai 		update_power_state(codec, 0x14, AC_PWRST_D0);
1592054d867eSTakashi Iwai 		update_power_state(codec, 0x34, AC_PWRST_D0);
15933e95b9abSLydia Wang 	}
15943e95b9abSLydia Wang 
15953e95b9abSLydia Wang 
15963e95b9abSLydia Wang 	/* Mono Out */
15973e95b9abSLydia Wang 	/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
15983e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x28);
15993e95b9abSLydia Wang 
16003e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16013e95b9abSLydia Wang 	set_pin_power_state(codec, 0x31, &parm);
16023e95b9abSLydia Wang 	if (present) {
1603054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, AC_PWRST_D3);
1604054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, AC_PWRST_D3);
1605054d867eSTakashi Iwai 		update_power_state(codec, 0x3e, AC_PWRST_D3);
16063e95b9abSLydia Wang 	} else {
1607054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, AC_PWRST_D0);
1608054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, AC_PWRST_D0);
1609054d867eSTakashi Iwai 		update_power_state(codec, 0x3e, AC_PWRST_D0);
16103e95b9abSLydia Wang 	}
16113e95b9abSLydia Wang 
16123e95b9abSLydia Wang 	/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
16133e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16143e95b9abSLydia Wang 	set_pin_power_state(codec, 0x33, &parm);
1615054d867eSTakashi Iwai 	update_power_state(codec, 0x1d, parm);
1616054d867eSTakashi Iwai 	update_power_state(codec, 0x3d, parm);
16173e95b9abSLydia Wang 
16183e95b9abSLydia Wang }
1619ab6734e7SLydia Wang 
1620ab6734e7SLydia Wang /* patch for vt1812 */
1621ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
1622ab6734e7SLydia Wang {
1623ab6734e7SLydia Wang 	struct via_spec *spec;
1624ab6734e7SLydia Wang 	int err;
1625ab6734e7SLydia Wang 
1626ab6734e7SLydia Wang 	/* create a codec specific record */
16275b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1628ab6734e7SLydia Wang 	if (spec == NULL)
1629ab6734e7SLydia Wang 		return -ENOMEM;
1630ab6734e7SLydia Wang 
1631b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1632d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1633d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
163430b45033STakashi Iwai 	add_secret_dac_path(codec);
1635620e2b28STakashi Iwai 
1636ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
163712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1638ab6734e7SLydia Wang 	if (err < 0) {
1639ab6734e7SLydia Wang 		via_free(codec);
1640ab6734e7SLydia Wang 		return err;
1641ab6734e7SLydia Wang 	}
1642ab6734e7SLydia Wang 
1643096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1812_init_verbs;
1644ab6734e7SLydia Wang 
1645ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
1646ab6734e7SLydia Wang 
16473e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1812;
1648ab6734e7SLydia Wang 	return 0;
1649ab6734e7SLydia Wang }
1650ab6734e7SLydia Wang 
165143737e0aSLydia Wang /* patch for vt3476 */
165243737e0aSLydia Wang 
165343737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = {
165443737e0aSLydia Wang 	/* Enable DMic 8/16/32K */
165543737e0aSLydia Wang 	{0x1, 0xF7B, 0x30},
165643737e0aSLydia Wang 	/* Enable Boost Volume backdoor */
165743737e0aSLydia Wang 	{0x1, 0xFB9, 0x20},
165843737e0aSLydia Wang 	/* Enable AOW-MW9 path */
165943737e0aSLydia Wang 	{0x1, 0xFB8, 0x10},
166043737e0aSLydia Wang 	{ }
166143737e0aSLydia Wang };
166243737e0aSLydia Wang 
166343737e0aSLydia Wang static void set_widgets_power_state_vt3476(struct hda_codec *codec)
166443737e0aSLydia Wang {
166543737e0aSLydia Wang 	struct via_spec *spec = codec->spec;
166643737e0aSLydia Wang 	int imux_is_smixer;
166743737e0aSLydia Wang 	unsigned int parm, parm2;
166843737e0aSLydia Wang 	/* MUX10 (1eh) = stereo mixer */
166943737e0aSLydia Wang 	imux_is_smixer =
167043737e0aSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 4;
167143737e0aSLydia Wang 	/* inputs */
167243737e0aSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
167343737e0aSLydia Wang 	parm = AC_PWRST_D3;
167443737e0aSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
167543737e0aSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
167643737e0aSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
167743737e0aSLydia Wang 	if (imux_is_smixer)
167843737e0aSLydia Wang 		parm = AC_PWRST_D0;
167943737e0aSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
168043737e0aSLydia Wang 	update_power_state(codec, 0x1e, parm);
168143737e0aSLydia Wang 	update_power_state(codec, 0x1f, parm);
168243737e0aSLydia Wang 	update_power_state(codec, 0x10, parm);
168343737e0aSLydia Wang 	update_power_state(codec, 0x11, parm);
168443737e0aSLydia Wang 
168543737e0aSLydia Wang 	/* outputs */
168643737e0aSLydia Wang 	/* PW3 (27h), MW3(37h), AOW3 (bh) */
168743737e0aSLydia Wang 	if (spec->codec_type == VT1705CF) {
168843737e0aSLydia Wang 		parm = AC_PWRST_D3;
168943737e0aSLydia Wang 		update_power_state(codec, 0x27, parm);
169043737e0aSLydia Wang 		update_power_state(codec, 0x37, parm);
169143737e0aSLydia Wang 	}	else {
169243737e0aSLydia Wang 		parm = AC_PWRST_D3;
169343737e0aSLydia Wang 		set_pin_power_state(codec, 0x27, &parm);
169443737e0aSLydia Wang 		update_power_state(codec, 0x37, parm);
169543737e0aSLydia Wang 	}
169643737e0aSLydia Wang 
169743737e0aSLydia Wang 	/* PW2 (26h), MW2(36h), AOW2 (ah) */
169843737e0aSLydia Wang 	parm = AC_PWRST_D3;
169943737e0aSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
170043737e0aSLydia Wang 	update_power_state(codec, 0x36, parm);
1701b3f6008fSTakashi Iwai 	if (smart51_enabled(codec)) {
170243737e0aSLydia Wang 		/* PW7(2bh), MW7(3bh), MUX7(1Bh) */
170343737e0aSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
170443737e0aSLydia Wang 		update_power_state(codec, 0x3b, parm);
170543737e0aSLydia Wang 		update_power_state(codec, 0x1b, parm);
170643737e0aSLydia Wang 	}
170743737e0aSLydia Wang 	update_conv_power_state(codec, 0xa, parm, 2);
170843737e0aSLydia Wang 
170943737e0aSLydia Wang 	/* PW1 (25h), MW1(35h), AOW1 (9h) */
171043737e0aSLydia Wang 	parm = AC_PWRST_D3;
171143737e0aSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
171243737e0aSLydia Wang 	update_power_state(codec, 0x35, parm);
1713b3f6008fSTakashi Iwai 	if (smart51_enabled(codec)) {
171443737e0aSLydia Wang 		/* PW6(2ah), MW6(3ah), MUX6(1ah) */
171543737e0aSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
171643737e0aSLydia Wang 		update_power_state(codec, 0x3a, parm);
171743737e0aSLydia Wang 		update_power_state(codec, 0x1a, parm);
171843737e0aSLydia Wang 	}
171943737e0aSLydia Wang 	update_conv_power_state(codec, 0x9, parm, 1);
172043737e0aSLydia Wang 
172143737e0aSLydia Wang 	/* PW4 (28h), MW4 (38h), MUX4(18h), AOW3(bh)/AOW0(8h) */
172243737e0aSLydia Wang 	parm = AC_PWRST_D3;
172343737e0aSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
172443737e0aSLydia Wang 	update_power_state(codec, 0x38, parm);
172543737e0aSLydia Wang 	update_power_state(codec, 0x18, parm);
1726b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
172743737e0aSLydia Wang 		update_conv_power_state(codec, 0xb, parm, 3);
172843737e0aSLydia Wang 	parm2 = parm; /* for pin 0x0b */
172943737e0aSLydia Wang 
173043737e0aSLydia Wang 	/* PW0 (24h), MW0(34h), MW9(3fh), AOW0 (8h) */
173143737e0aSLydia Wang 	parm = AC_PWRST_D3;
173243737e0aSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
173343737e0aSLydia Wang 	update_power_state(codec, 0x34, parm);
1734b3f6008fSTakashi Iwai 	if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3)
173543737e0aSLydia Wang 		parm = parm2;
173643737e0aSLydia Wang 	update_conv_power_state(codec, 0x8, parm, 0);
173743737e0aSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
173843737e0aSLydia Wang 	update_power_state(codec, 0x3f, imux_is_smixer ? AC_PWRST_D0 : parm);
173943737e0aSLydia Wang }
174043737e0aSLydia Wang 
174143737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec)
174243737e0aSLydia Wang {
174343737e0aSLydia Wang 	struct via_spec *spec;
174443737e0aSLydia Wang 	int err;
174543737e0aSLydia Wang 
174643737e0aSLydia Wang 	/* create a codec specific record */
174743737e0aSLydia Wang 	spec = via_new_spec(codec);
174843737e0aSLydia Wang 	if (spec == NULL)
174943737e0aSLydia Wang 		return -ENOMEM;
175043737e0aSLydia Wang 
1751b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x3f;
175243737e0aSLydia Wang 	add_secret_dac_path(codec);
175343737e0aSLydia Wang 
175443737e0aSLydia Wang 	/* automatic parse from the BIOS config */
175543737e0aSLydia Wang 	err = via_parse_auto_config(codec);
175643737e0aSLydia Wang 	if (err < 0) {
175743737e0aSLydia Wang 		via_free(codec);
175843737e0aSLydia Wang 		return err;
175943737e0aSLydia Wang 	}
176043737e0aSLydia Wang 
176143737e0aSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs;
176243737e0aSLydia Wang 
176343737e0aSLydia Wang 	codec->patch_ops = via_patch_ops;
176443737e0aSLydia Wang 
176543737e0aSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt3476;
176643737e0aSLydia Wang 
176743737e0aSLydia Wang 	return 0;
176843737e0aSLydia Wang }
176943737e0aSLydia Wang 
1770c577b8a1SJoseph Chan /*
1771c577b8a1SJoseph Chan  * patch entries
1772c577b8a1SJoseph Chan  */
177390dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = {
17743218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
17753218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
17763218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
17773218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
17783218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
1779ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17803218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
1781ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17823218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
1783ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17843218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
1785ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17863218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
1787ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17883218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
1789ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17903218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
1791ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17923218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
1793ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17943218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
1795ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
17963218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
1797ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
17983218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
1799ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18003218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
1801ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18023218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
1803ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18043218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
1805ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18063218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
1807ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18083218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
1809ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18103218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
1811d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18123218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
1813d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18143218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
1815d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18163218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
1817d949cac1SHarald Welte 	  .patch = patch_vt1708S},
1818bc92df7fSLydia Wang 	{ .id = 0x11064397, .name = "VT1705",
1819d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18203218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
1821d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18223218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
1823d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18243218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
1825d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18263218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
1827d949cac1SHarald Welte 	  .patch = patch_vt1702},
18283218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
1829d949cac1SHarald Welte 	  .patch = patch_vt1702},
18303218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
1831d949cac1SHarald Welte 	  .patch = patch_vt1702},
18323218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
1833d949cac1SHarald Welte 	  .patch = patch_vt1702},
18343218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
1835d949cac1SHarald Welte 	  .patch = patch_vt1702},
18363218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
1837d949cac1SHarald Welte 	  .patch = patch_vt1702},
18383218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
1839d949cac1SHarald Welte 	  .patch = patch_vt1702},
18403218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
1841d949cac1SHarald Welte 	  .patch = patch_vt1702},
1842eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
1843eb7188caSLydia Wang 	  .patch = patch_vt1718S},
1844eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
1845eb7188caSLydia Wang 	  .patch = patch_vt1718S},
1846bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
1847bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
1848bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
1849bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
1850f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
1851f3db423dSLydia Wang 	  .patch = patch_vt1716S},
1852f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
1853f3db423dSLydia Wang 	  .patch = patch_vt1716S},
185425eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
185525eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
1856ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
185736dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
185836dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
185911890956SLydia Wang 	{ .id = 0x11060446, .name = "VT1802",
186011890956SLydia Wang 		.patch = patch_vt2002P},
186111890956SLydia Wang 	{ .id = 0x11068446, .name = "VT1802",
186211890956SLydia Wang 		.patch = patch_vt2002P},
186343737e0aSLydia Wang 	{ .id = 0x11064760, .name = "VT1705CF",
186443737e0aSLydia Wang 		.patch = patch_vt3476},
18656121b84aSLydia Wang 	{ .id = 0x11064761, .name = "VT1708SCE",
18666121b84aSLydia Wang 		.patch = patch_vt3476},
18676121b84aSLydia Wang 	{ .id = 0x11064762, .name = "VT1808",
18686121b84aSLydia Wang 		.patch = patch_vt3476},
1869c577b8a1SJoseph Chan 	{} /* terminator */
1870c577b8a1SJoseph Chan };
18711289e9e8STakashi Iwai 
18721289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
18731289e9e8STakashi Iwai 
18741289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
18751289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
18761289e9e8STakashi Iwai 	.owner = THIS_MODULE,
18771289e9e8STakashi Iwai };
18781289e9e8STakashi Iwai 
18791289e9e8STakashi Iwai MODULE_LICENSE("GPL");
18801289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
18811289e9e8STakashi Iwai 
18821289e9e8STakashi Iwai static int __init patch_via_init(void)
18831289e9e8STakashi Iwai {
18841289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
18851289e9e8STakashi Iwai }
18861289e9e8STakashi Iwai 
18871289e9e8STakashi Iwai static void __exit patch_via_exit(void)
18881289e9e8STakashi Iwai {
18891289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
18901289e9e8STakashi Iwai }
18911289e9e8STakashi Iwai 
18921289e9e8STakashi Iwai module_init(patch_via_init)
18931289e9e8STakashi Iwai module_exit(patch_via_exit)
1894