xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision bbbc7e8502c95237dbd86cc4d0a12ca9a6b18c8f)
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 
1225b0cb1d8SJaroslav Kysela static struct via_spec *via_new_spec(struct hda_codec *codec)
1235b0cb1d8SJaroslav Kysela {
1245b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1255b0cb1d8SJaroslav Kysela 
1265b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1275b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1285b0cb1d8SJaroslav Kysela 		return NULL;
1295b0cb1d8SJaroslav Kysela 
1305b0cb1d8SJaroslav Kysela 	codec->spec = spec;
131b3f6008fSTakashi Iwai 	snd_hda_gen_spec_init(&spec->gen);
1320341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
1330341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
1340341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
1350341ccd7SLydia Wang 		spec->codec_type = VT1708S;
136b3f6008fSTakashi Iwai 	spec->no_pin_power_ctl = 1;
13713961170STakashi Iwai 	spec->gen.indep_hp = 1;
13805909d5cSTakashi Iwai 	spec->gen.keep_eapd_on = 1;
139b3f6008fSTakashi Iwai 	spec->gen.pcm_playback_hook = via_playback_pcm_hook;
14074f14b36STakashi Iwai 	spec->gen.add_stereo_mix_input = HDA_HINT_STEREO_MIX_AUTO;
1415b0cb1d8SJaroslav Kysela 	return spec;
1425b0cb1d8SJaroslav Kysela }
1435b0cb1d8SJaroslav Kysela 
144744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
145d7426329SHarald Welte {
146744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
147d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
148d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
149d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
150d7426329SHarald Welte 
151d7426329SHarald Welte 	/* get codec type */
152d7426329SHarald Welte 	if (ven_id != 0x1106)
153d7426329SHarald Welte 		codec_type = UNKNOWN;
154d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
155d7426329SHarald Welte 		codec_type = VT1708;
156d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
157d7426329SHarald Welte 		codec_type = VT1709_10CH;
158d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
159d7426329SHarald Welte 		codec_type = VT1709_6CH;
160518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
161d7426329SHarald Welte 		codec_type = VT1708B_8CH;
162518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
163518bf3baSLydia Wang 			codec_type = VT1708BCE;
164518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
165d7426329SHarald Welte 		codec_type = VT1708B_4CH;
166d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
167d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
168d7426329SHarald Welte 		codec_type = VT1708S;
169d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
170d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
171d7426329SHarald Welte 		codec_type = VT1702;
172eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
173eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
174eb7188caSLydia Wang 		codec_type = VT1718S;
175f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
176f3db423dSLydia Wang 		codec_type = VT1716S;
177bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
178bb3c6bfcSLydia Wang 		codec_type = VT1718S;
17925eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
18025eaba2fSLydia Wang 		codec_type = VT2002P;
181ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
182ab6734e7SLydia Wang 		codec_type = VT1812;
18336dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
18436dd5c4aSLydia Wang 		codec_type = VT1708S;
18511890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
18611890956SLydia Wang 		codec_type = VT1802;
18743737e0aSLydia Wang 	else if (dev_id == 0x4760)
18843737e0aSLydia Wang 		codec_type = VT1705CF;
1896121b84aSLydia Wang 	else if (dev_id == 0x4761 || dev_id == 0x4762)
1906121b84aSLydia Wang 		codec_type = VT1808;
191d7426329SHarald Welte 	else
192d7426329SHarald Welte 		codec_type = UNKNOWN;
193d7426329SHarald Welte 	return codec_type;
194d7426329SHarald Welte };
195d7426329SHarald Welte 
196ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec);
197ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec);
1981f2e99feSLydia Wang 
199187d333eSTakashi Iwai #define hp_detect_with_aa(codec) \
200187d333eSTakashi Iwai 	(snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
201187d333eSTakashi Iwai 	 !is_aa_path_mute(codec))
2021f2e99feSLydia Wang 
203b3f6008fSTakashi Iwai static void vt1708_stop_hp_work(struct hda_codec *codec)
2041f2e99feSLydia Wang {
205b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
206b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
2071f2e99feSLydia Wang 		return;
208187d333eSTakashi Iwai 	if (spec->hp_work_active) {
209b3f6008fSTakashi Iwai 		snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
2107eaa9161SWang Xingchao 		codec->jackpoll_interval = 0;
211b3f6008fSTakashi Iwai 		cancel_delayed_work_sync(&codec->jackpoll_work);
212b3f6008fSTakashi Iwai 		spec->hp_work_active = false;
213187d333eSTakashi Iwai 	}
214187d333eSTakashi Iwai }
215187d333eSTakashi Iwai 
216b3f6008fSTakashi Iwai static void vt1708_update_hp_work(struct hda_codec *codec)
217187d333eSTakashi Iwai {
218b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
219b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
220187d333eSTakashi Iwai 		return;
22105dc0fc9SDavid Henningsson 	if (spec->vt1708_jack_detect) {
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 {
235087c2e3bSTakashi Iwai #if 0 /* FIXME: the assumed connections don't match always with the
236087c2e3bSTakashi Iwai        * actual routes by the generic parser, so better to disable
237087c2e3bSTakashi Iwai        * the control for safety.
238087c2e3bSTakashi Iwai        */
2393e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
2403e95b9abSLydia Wang 	if (spec->set_widgets_power_state)
2413e95b9abSLydia Wang 		spec->set_widgets_power_state(codec);
242087c2e3bSTakashi Iwai #endif
2433e95b9abSLydia Wang }
24425eaba2fSLydia Wang 
245054d867eSTakashi Iwai static void update_power_state(struct hda_codec *codec, hda_nid_t nid,
246054d867eSTakashi Iwai 			       unsigned int parm)
247054d867eSTakashi Iwai {
2489040d102STakashi Iwai 	if (snd_hda_check_power_state(codec, nid, parm))
249054d867eSTakashi Iwai 		return;
250054d867eSTakashi Iwai 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
251054d867eSTakashi Iwai }
252054d867eSTakashi Iwai 
25343737e0aSLydia Wang static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid,
25443737e0aSLydia Wang 			       unsigned int parm, unsigned int index)
25543737e0aSLydia Wang {
25643737e0aSLydia Wang 	struct via_spec *spec = codec->spec;
25743737e0aSLydia Wang 	unsigned int format;
2589040d102STakashi Iwai 
2599040d102STakashi Iwai 	if (snd_hda_check_power_state(codec, nid, parm))
26043737e0aSLydia Wang 		return;
26143737e0aSLydia Wang 	format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
26243737e0aSLydia Wang 	if (format && (spec->dac_stream_tag[index] != format))
26343737e0aSLydia Wang 		spec->dac_stream_tag[index] = format;
26443737e0aSLydia Wang 
26543737e0aSLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
26643737e0aSLydia Wang 	if (parm == AC_PWRST_D0) {
26743737e0aSLydia Wang 		format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
26843737e0aSLydia Wang 		if (!format && (spec->dac_stream_tag[index] != format))
26943737e0aSLydia Wang 			snd_hda_codec_write(codec, nid, 0,
27043737e0aSLydia Wang 						  AC_VERB_SET_CHANNEL_STREAMID,
27143737e0aSLydia Wang 						  spec->dac_stream_tag[index]);
27243737e0aSLydia Wang 	}
27343737e0aSLydia Wang }
27443737e0aSLydia Wang 
275b3f6008fSTakashi Iwai static bool smart51_enabled(struct hda_codec *codec)
276b3f6008fSTakashi Iwai {
277b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
278b3f6008fSTakashi Iwai 	return spec->gen.ext_channel_count > 2;
279b3f6008fSTakashi Iwai }
280b3f6008fSTakashi Iwai 
281b3f6008fSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
282b3f6008fSTakashi Iwai {
283b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
284b3f6008fSTakashi Iwai 	int i;
285b3f6008fSTakashi Iwai 
286b3f6008fSTakashi Iwai 	for (i = 0; i < spec->gen.multi_ios; i++)
287b3f6008fSTakashi Iwai 		if (spec->gen.multi_io[i].pin == pin)
288b3f6008fSTakashi Iwai 			return true;
289b3f6008fSTakashi Iwai 	return false;
290b3f6008fSTakashi Iwai }
291b3f6008fSTakashi Iwai 
292f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
293f5271101SLydia Wang 				unsigned int *affected_parm)
294f5271101SLydia Wang {
295f5271101SLydia Wang 	unsigned parm;
296f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
297f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
298f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
299f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
3001564b287SLydia Wang 	struct via_spec *spec = codec->spec;
30124088a58STakashi Iwai 	unsigned present = 0;
30224088a58STakashi Iwai 
30324088a58STakashi Iwai 	no_presence |= spec->no_pin_power_ctl;
30424088a58STakashi Iwai 	if (!no_presence)
30524088a58STakashi Iwai 		present = snd_hda_jack_detect(codec, nid);
306b3f6008fSTakashi Iwai 	if ((smart51_enabled(codec) && is_smart51_pins(codec, nid))
3071564b287SLydia Wang 	    || ((no_presence || present)
3081564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
309f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
310f5271101SLydia Wang 		parm = AC_PWRST_D0;
311f5271101SLydia Wang 	} else
312f5271101SLydia Wang 		parm = AC_PWRST_D3;
313f5271101SLydia Wang 
314054d867eSTakashi Iwai 	update_power_state(codec, nid, parm);
315f5271101SLydia Wang }
316f5271101SLydia Wang 
31724088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
31824088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
31924088a58STakashi Iwai {
320dda415d4STakashi Iwai 	return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
32124088a58STakashi Iwai }
32224088a58STakashi Iwai 
32324088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
32424088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
32524088a58STakashi Iwai {
32624088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
32724088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
32824088a58STakashi Iwai 	ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
32924088a58STakashi Iwai 	return 0;
33024088a58STakashi Iwai }
33124088a58STakashi Iwai 
33224088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
33324088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
33424088a58STakashi Iwai {
33524088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
33624088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
33724088a58STakashi Iwai 	unsigned int val = !ucontrol->value.enumerated.item[0];
33824088a58STakashi Iwai 
33924088a58STakashi Iwai 	if (val == spec->no_pin_power_ctl)
34024088a58STakashi Iwai 		return 0;
34124088a58STakashi Iwai 	spec->no_pin_power_ctl = val;
34224088a58STakashi Iwai 	set_widgets_power_state(codec);
343e9d010c2STakashi Iwai 	analog_low_current_mode(codec);
34424088a58STakashi Iwai 	return 1;
34524088a58STakashi Iwai }
34624088a58STakashi Iwai 
347b3f6008fSTakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = {
348b3f6008fSTakashi Iwai 	{
34924088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
35024088a58STakashi Iwai 	.name = "Dynamic Power-Control",
35124088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
35224088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
35324088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
354b3f6008fSTakashi Iwai 	},
355b3f6008fSTakashi Iwai 	{} /* terminator */
35624088a58STakashi Iwai };
35724088a58STakashi Iwai 
35824088a58STakashi Iwai 
359f5271101SLydia Wang /* check AA path's mute status */
360ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec)
361ada509ecSTakashi Iwai {
362ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
363ada509ecSTakashi Iwai 	const struct hda_amp_list *p;
3640186f4f4STakashi Iwai 	int ch, v;
365ada509ecSTakashi Iwai 
3660186f4f4STakashi Iwai 	p = spec->gen.loopback.amplist;
3670186f4f4STakashi Iwai 	if (!p)
3680186f4f4STakashi Iwai 		return true;
3690186f4f4STakashi Iwai 	for (; p->nid; p++) {
370ada509ecSTakashi Iwai 		for (ch = 0; ch < 2; ch++) {
371ada509ecSTakashi Iwai 			v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
372ada509ecSTakashi Iwai 						   p->idx);
373ada509ecSTakashi Iwai 			if (!(v & HDA_AMP_MUTE) && v > 0)
374ada509ecSTakashi Iwai 				return false;
375f5271101SLydia Wang 		}
376f5271101SLydia Wang 	}
377ada509ecSTakashi Iwai 	return true;
378f5271101SLydia Wang }
379f5271101SLydia Wang 
380f5271101SLydia Wang /* enter/exit analog low-current mode */
381e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force)
382f5271101SLydia Wang {
383f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
384ada509ecSTakashi Iwai 	bool enable;
385ada509ecSTakashi Iwai 	unsigned int verb, parm;
386f5271101SLydia Wang 
387e9d010c2STakashi Iwai 	if (spec->no_pin_power_ctl)
388e9d010c2STakashi Iwai 		enable = false;
389e9d010c2STakashi Iwai 	else
390b3f6008fSTakashi Iwai 		enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
391e9d010c2STakashi Iwai 	if (enable == spec->alc_mode && !force)
392e9d010c2STakashi Iwai 		return;
393e9d010c2STakashi Iwai 	spec->alc_mode = enable;
394f5271101SLydia Wang 
395f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
396f5271101SLydia Wang 	switch (spec->codec_type) {
397f5271101SLydia Wang 	case VT1708B_8CH:
398f5271101SLydia Wang 	case VT1708B_4CH:
399f5271101SLydia Wang 		verb = 0xf70;
400f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
401f5271101SLydia Wang 		break;
402f5271101SLydia Wang 	case VT1708S:
403eb7188caSLydia Wang 	case VT1718S:
404f3db423dSLydia Wang 	case VT1716S:
405f5271101SLydia Wang 		verb = 0xf73;
406f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
407f5271101SLydia Wang 		break;
408f5271101SLydia Wang 	case VT1702:
409f5271101SLydia Wang 		verb = 0xf73;
410f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
411f5271101SLydia Wang 		break;
41225eaba2fSLydia Wang 	case VT2002P:
413ab6734e7SLydia Wang 	case VT1812:
41411890956SLydia Wang 	case VT1802:
41525eaba2fSLydia Wang 		verb = 0xf93;
41625eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
41725eaba2fSLydia Wang 		break;
41843737e0aSLydia Wang 	case VT1705CF:
4196121b84aSLydia Wang 	case VT1808:
42043737e0aSLydia Wang 		verb = 0xf82;
42143737e0aSLydia Wang 		parm = enable ? 0x00 : 0xe0;  /* 0x00: 4/40x, 0xe0: 1x */
42243737e0aSLydia Wang 		break;
423f5271101SLydia Wang 	default:
424f5271101SLydia Wang 		return;		/* other codecs are not supported */
425f5271101SLydia Wang 	}
426f5271101SLydia Wang 	/* send verb */
427f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
428f5271101SLydia Wang }
429f5271101SLydia Wang 
430e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec)
431e9d010c2STakashi Iwai {
432e9d010c2STakashi Iwai 	return __analog_low_current_mode(codec, false);
433e9d010c2STakashi Iwai }
434e9d010c2STakashi Iwai 
435c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
436c577b8a1SJoseph Chan {
437c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
4385b0cb1d8SJaroslav Kysela 	int err, i;
439c577b8a1SJoseph Chan 
440b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_controls(codec);
441b3f6008fSTakashi Iwai 	if (err < 0)
442b3f6008fSTakashi Iwai 		return err;
443b3f6008fSTakashi Iwai 
44424088a58STakashi Iwai 	if (spec->set_widgets_power_state)
445b3f6008fSTakashi Iwai 		spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum;
44624088a58STakashi Iwai 
447c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
448c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
449c577b8a1SJoseph Chan 		if (err < 0)
450c577b8a1SJoseph Chan 			return err;
451c577b8a1SJoseph Chan 	}
452c577b8a1SJoseph Chan 
453c577b8a1SJoseph Chan 	return 0;
454c577b8a1SJoseph Chan }
455c577b8a1SJoseph Chan 
456b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
457b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
458b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
459b3f6008fSTakashi Iwai 				  int action)
460c577b8a1SJoseph Chan {
461b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
462b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
463c577b8a1SJoseph Chan }
464c577b8a1SJoseph Chan 
465c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
466c577b8a1SJoseph Chan {
467b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
468a8dca460STakashi Iwai 	snd_hda_gen_free(codec);
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 	/* Fix pop noise on headphones */
4782c38d990STakashi Iwai 	if (spec->codec_type == VT1802)
4792c38d990STakashi Iwai 		snd_hda_shutup_pins(codec);
48094c142a1SDavid Henningsson 
4811f2e99feSLydia Wang 	return 0;
4821f2e99feSLydia Wang }
4831f2e99feSLydia Wang #endif
4841f2e99feSLydia Wang 
48583012a7cSTakashi Iwai #ifdef CONFIG_PM
486cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
487cb53c626STakashi Iwai {
488cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
489b3f6008fSTakashi Iwai 	set_widgets_power_state(codec);
490b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
491b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
492b3f6008fSTakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
493cb53c626STakashi Iwai }
494cb53c626STakashi Iwai #endif
495cb53c626STakashi Iwai 
496c577b8a1SJoseph Chan /*
497c577b8a1SJoseph Chan  */
4985d41762aSTakashi Iwai 
4995d41762aSTakashi Iwai static int via_init(struct hda_codec *codec);
5005d41762aSTakashi Iwai 
50190dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
502c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
503b3f6008fSTakashi Iwai 	.build_pcms = snd_hda_gen_build_pcms,
504c577b8a1SJoseph Chan 	.init = via_init,
505c577b8a1SJoseph Chan 	.free = via_free,
5064e2d16d3SDavid Henningsson 	.unsol_event = snd_hda_jack_unsol_event,
5072a43952aSTakashi Iwai #ifdef CONFIG_PM
5081f2e99feSLydia Wang 	.suspend = via_suspend,
509cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
510cb53c626STakashi Iwai #endif
511c577b8a1SJoseph Chan };
512c577b8a1SJoseph Chan 
5134a79616dSTakashi Iwai 
514b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = {
515b3f6008fSTakashi Iwai 	/* power down jack detect function */
516b3f6008fSTakashi Iwai 	{0x1, 0xf81, 0x1},
517b3f6008fSTakashi Iwai 	{ }
5184a79616dSTakashi Iwai };
51976d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
52076d9b0ddSHarald Welte {
52176d9b0ddSHarald Welte 	unsigned int def_conf;
52276d9b0ddSHarald Welte 	unsigned char seqassoc;
52376d9b0ddSHarald Welte 
5242f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
52576d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
52676d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
52782ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
52882ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
52976d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
5302f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
53176d9b0ddSHarald Welte 	}
53276d9b0ddSHarald Welte 
53376d9b0ddSHarald Welte 	return;
53476d9b0ddSHarald Welte }
53576d9b0ddSHarald Welte 
536e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
5371f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
5381f2e99feSLydia Wang {
5391f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
5401f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
5411f2e99feSLydia Wang 
5421f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
5431f2e99feSLydia Wang 		return 0;
544e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
5451f2e99feSLydia Wang 	return 0;
5461f2e99feSLydia Wang }
5471f2e99feSLydia Wang 
548e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
5491f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
5501f2e99feSLydia Wang {
5511f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
5521f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
553187d333eSTakashi Iwai 	int val;
5541f2e99feSLydia Wang 
5551f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
5561f2e99feSLydia Wang 		return 0;
557187d333eSTakashi Iwai 	val = !!ucontrol->value.integer.value[0];
558187d333eSTakashi Iwai 	if (spec->vt1708_jack_detect == val)
559187d333eSTakashi Iwai 		return 0;
560187d333eSTakashi Iwai 	spec->vt1708_jack_detect = val;
561b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
562187d333eSTakashi Iwai 	return 1;
5631f2e99feSLydia Wang }
5641f2e99feSLydia Wang 
565b3f6008fSTakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = {
566b3f6008fSTakashi Iwai 	{
5671f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5681f2e99feSLydia Wang 	.name = "Jack Detect",
5691f2e99feSLydia Wang 	.count = 1,
5701f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
571e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
572e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
573b3f6008fSTakashi Iwai 	},
574b3f6008fSTakashi Iwai 	{} /* terminator */
5751f2e99feSLydia Wang };
5761f2e99feSLydia Wang 
5771a4f69d5STakashi Iwai static void via_jack_powerstate_event(struct hda_codec *codec,
5781a4f69d5STakashi Iwai 				      struct hda_jack_callback *tbl)
5794e2d16d3SDavid Henningsson {
5804e2d16d3SDavid Henningsson 	set_widgets_power_state(codec);
5814e2d16d3SDavid Henningsson }
5824e2d16d3SDavid Henningsson 
583b3f6008fSTakashi Iwai static void via_set_jack_unsol_events(struct hda_codec *codec)
5844a918ffeSTakashi Iwai {
5854a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
586b3f6008fSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->gen.autocfg;
587b3f6008fSTakashi Iwai 	hda_nid_t pin;
5884a918ffeSTakashi Iwai 	int i;
5894a918ffeSTakashi Iwai 
5904a918ffeSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
591b3f6008fSTakashi Iwai 		pin = cfg->line_out_pins[i];
5927c3008c4STakashi Iwai 		if (pin && is_jack_detectable(codec, pin))
593b3f6008fSTakashi Iwai 			snd_hda_jack_detect_enable_callback(codec, pin,
5944e2d16d3SDavid Henningsson 							    via_jack_powerstate_event);
5954a918ffeSTakashi Iwai 	}
596b3f6008fSTakashi Iwai 
597b3f6008fSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
598b3f6008fSTakashi Iwai 		pin = cfg->line_out_pins[i];
5997c3008c4STakashi Iwai 		if (pin && is_jack_detectable(codec, pin))
600b3f6008fSTakashi Iwai 			snd_hda_jack_detect_enable_callback(codec, pin,
601b3f6008fSTakashi Iwai 							    via_jack_powerstate_event);
602b3f6008fSTakashi Iwai 	}
603b3f6008fSTakashi Iwai }
604b3f6008fSTakashi Iwai 
6054abdbd1cSTakashi Iwai static const struct badness_table via_main_out_badness = {
6064abdbd1cSTakashi Iwai 	.no_primary_dac = 0x10000,
6074abdbd1cSTakashi Iwai 	.no_dac = 0x4000,
6084abdbd1cSTakashi Iwai 	.shared_primary = 0x10000,
6094abdbd1cSTakashi Iwai 	.shared_surr = 0x20,
6104abdbd1cSTakashi Iwai 	.shared_clfe = 0x20,
6114abdbd1cSTakashi Iwai 	.shared_surr_main = 0x20,
6124abdbd1cSTakashi Iwai };
6134abdbd1cSTakashi Iwai static const struct badness_table via_extra_out_badness = {
6144abdbd1cSTakashi Iwai 	.no_primary_dac = 0x4000,
6154abdbd1cSTakashi Iwai 	.no_dac = 0x4000,
6164abdbd1cSTakashi Iwai 	.shared_primary = 0x12,
6174abdbd1cSTakashi Iwai 	.shared_surr = 0x20,
6184abdbd1cSTakashi Iwai 	.shared_clfe = 0x20,
6194abdbd1cSTakashi Iwai 	.shared_surr_main = 0x10,
6204abdbd1cSTakashi Iwai };
6214abdbd1cSTakashi Iwai 
622b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec)
623b3f6008fSTakashi Iwai {
624b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
625b3f6008fSTakashi Iwai 	int err;
626b3f6008fSTakashi Iwai 
6274abdbd1cSTakashi Iwai 	spec->gen.main_out_badness = &via_main_out_badness;
6284abdbd1cSTakashi Iwai 	spec->gen.extra_out_badness = &via_extra_out_badness;
6294abdbd1cSTakashi Iwai 
630b3f6008fSTakashi Iwai 	err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
631b3f6008fSTakashi Iwai 	if (err < 0)
632b3f6008fSTakashi Iwai 		return err;
633b3f6008fSTakashi Iwai 
634b3f6008fSTakashi Iwai 	err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
635b3f6008fSTakashi Iwai 	if (err < 0)
636b3f6008fSTakashi Iwai 		return err;
637b3f6008fSTakashi Iwai 
638b3f6008fSTakashi Iwai 	via_set_jack_unsol_events(codec);
639b3f6008fSTakashi Iwai 	return 0;
6404a918ffeSTakashi Iwai }
6414a918ffeSTakashi Iwai 
6425d41762aSTakashi Iwai static int via_init(struct hda_codec *codec)
6435d41762aSTakashi Iwai {
6445d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
6455d41762aSTakashi Iwai 	int i;
6465d41762aSTakashi Iwai 
6475d41762aSTakashi Iwai 	for (i = 0; i < spec->num_iverbs; i++)
6485d41762aSTakashi Iwai 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
6495d41762aSTakashi Iwai 
650e9d010c2STakashi Iwai 	/* init power states */
651e9d010c2STakashi Iwai 	set_widgets_power_state(codec);
652e9d010c2STakashi Iwai 	__analog_low_current_mode(codec, true);
653e9d010c2STakashi Iwai 
654b3f6008fSTakashi Iwai 	snd_hda_gen_init(codec);
65511890956SLydia Wang 
656b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
65725eaba2fSLydia Wang 
658c577b8a1SJoseph Chan 	return 0;
659c577b8a1SJoseph Chan }
660c577b8a1SJoseph Chan 
661f672f65aSDavid Henningsson static int vt1708_build_controls(struct hda_codec *codec)
662f672f65aSDavid Henningsson {
663f672f65aSDavid Henningsson 	/* In order not to create "Phantom Jack" controls,
664f672f65aSDavid Henningsson 	   temporary enable jackpoll */
665f672f65aSDavid Henningsson 	int err;
666f672f65aSDavid Henningsson 	int old_interval = codec->jackpoll_interval;
667f672f65aSDavid Henningsson 	codec->jackpoll_interval = msecs_to_jiffies(100);
668f672f65aSDavid Henningsson 	err = via_build_controls(codec);
669f672f65aSDavid Henningsson 	codec->jackpoll_interval = old_interval;
670f672f65aSDavid Henningsson 	return err;
671f672f65aSDavid Henningsson }
672f672f65aSDavid Henningsson 
673b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec)
674337b9d02STakashi Iwai {
675337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
676b3f6008fSTakashi Iwai 	int i, err;
677337b9d02STakashi Iwai 
678b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_pcms(codec);
679b3f6008fSTakashi Iwai 	if (err < 0 || codec->vendor_id != 0x11061708)
680b3f6008fSTakashi Iwai 		return err;
681b3f6008fSTakashi Iwai 
682b3f6008fSTakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
683b3f6008fSTakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
684b3f6008fSTakashi Iwai 	 * disable the 24bit format, so far.
685b3f6008fSTakashi Iwai 	 */
686*bbbc7e85STakashi Iwai 	for (i = 0; i < ARRAY_SIZE(spec->gen.pcm_rec); i++) {
687*bbbc7e85STakashi Iwai 		struct hda_pcm *info = spec->gen.pcm_rec[i];
688*bbbc7e85STakashi Iwai 		if (!info)
689*bbbc7e85STakashi Iwai 			continue;
690b3f6008fSTakashi Iwai 		if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
691b3f6008fSTakashi Iwai 		    info->pcm_type != HDA_PCM_TYPE_AUDIO)
692b3f6008fSTakashi Iwai 			continue;
693b3f6008fSTakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats =
694b3f6008fSTakashi Iwai 			SNDRV_PCM_FMTBIT_S16_LE;
695337b9d02STakashi Iwai 	}
696b3f6008fSTakashi Iwai 
6971c55d521STakashi Iwai 	return 0;
698337b9d02STakashi Iwai }
699337b9d02STakashi Iwai 
700c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
701c577b8a1SJoseph Chan {
702c577b8a1SJoseph Chan 	struct via_spec *spec;
703c577b8a1SJoseph Chan 	int err;
704c577b8a1SJoseph Chan 
705c577b8a1SJoseph Chan 	/* create a codec specific record */
7065b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
707c577b8a1SJoseph Chan 	if (spec == NULL)
708c577b8a1SJoseph Chan 		return -ENOMEM;
709c577b8a1SJoseph Chan 
710b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x17;
711b3f6008fSTakashi Iwai 
712b3f6008fSTakashi Iwai 	/* set jackpoll_interval while parsing the codec */
713b3f6008fSTakashi Iwai 	codec->jackpoll_interval = msecs_to_jiffies(100);
714b3f6008fSTakashi Iwai 	spec->vt1708_jack_detect = 1;
715b3f6008fSTakashi Iwai 
716b3f6008fSTakashi Iwai 	/* don't support the input jack switching due to lack of unsol event */
717b3f6008fSTakashi Iwai 	/* (it may work with polling, though, but it needs testing) */
718b3f6008fSTakashi Iwai 	spec->gen.suppress_auto_mic = 1;
719eb33ccf7STakashi Iwai 	/* Some machines show the broken speaker mute */
720eb33ccf7STakashi Iwai 	spec->gen.auto_mute_via_amp = 1;
721620e2b28STakashi Iwai 
72212daef65STakashi Iwai 	/* Add HP and CD pin config connect bit re-config action */
72312daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
72412daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
72512daef65STakashi Iwai 
726c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
72712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
728c577b8a1SJoseph Chan 	if (err < 0) {
729c577b8a1SJoseph Chan 		via_free(codec);
730c577b8a1SJoseph Chan 		return err;
731c577b8a1SJoseph Chan 	}
732c577b8a1SJoseph Chan 
73312daef65STakashi Iwai 	/* add jack detect on/off control */
734b3f6008fSTakashi Iwai 	spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl;
735c577b8a1SJoseph Chan 
736e322a36dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
737e322a36dSLydia Wang 
738c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
739f672f65aSDavid Henningsson 	codec->patch_ops.build_controls = vt1708_build_controls;
740b3f6008fSTakashi Iwai 	codec->patch_ops.build_pcms = vt1708_build_pcms;
741c577b8a1SJoseph Chan 
742b3f6008fSTakashi Iwai 	/* clear jackpoll_interval again; it's set dynamically */
743b3f6008fSTakashi Iwai 	codec->jackpoll_interval = 0;
744b3f6008fSTakashi Iwai 
745c577b8a1SJoseph Chan 	return 0;
746c577b8a1SJoseph Chan }
747c577b8a1SJoseph Chan 
748ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec)
749c577b8a1SJoseph Chan {
750c577b8a1SJoseph Chan 	struct via_spec *spec;
751c577b8a1SJoseph Chan 	int err;
752c577b8a1SJoseph Chan 
753c577b8a1SJoseph Chan 	/* create a codec specific record */
7545b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
755c577b8a1SJoseph Chan 	if (spec == NULL)
756c577b8a1SJoseph Chan 		return -ENOMEM;
757c577b8a1SJoseph Chan 
758b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x18;
759620e2b28STakashi Iwai 
76012daef65STakashi Iwai 	err = via_parse_auto_config(codec);
761c577b8a1SJoseph Chan 	if (err < 0) {
762c577b8a1SJoseph Chan 		via_free(codec);
763c577b8a1SJoseph Chan 		return err;
764c577b8a1SJoseph Chan 	}
765c577b8a1SJoseph Chan 
766c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
767c577b8a1SJoseph Chan 
768f7278fd0SJosepch Chan 	return 0;
769f7278fd0SJosepch Chan }
770f7278fd0SJosepch Chan 
7713e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
7723e95b9abSLydia Wang {
7733e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
7743e95b9abSLydia Wang 	int imux_is_smixer;
7753e95b9abSLydia Wang 	unsigned int parm;
7763e95b9abSLydia Wang 	int is_8ch = 0;
777bc92df7fSLydia Wang 	if ((spec->codec_type != VT1708B_4CH) &&
778bc92df7fSLydia Wang 	    (codec->vendor_id != 0x11064397))
7793e95b9abSLydia Wang 		is_8ch = 1;
7803e95b9abSLydia Wang 
7813e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
7823e95b9abSLydia Wang 	imux_is_smixer =
7833e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
7843e95b9abSLydia Wang 	 == ((spec->codec_type == VT1708S) ? 5 : 0));
7853e95b9abSLydia Wang 	/* inputs */
7863e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
7873e95b9abSLydia Wang 	parm = AC_PWRST_D3;
7883e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
7893e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
7903e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
7913e95b9abSLydia Wang 	if (imux_is_smixer)
7923e95b9abSLydia Wang 		parm = AC_PWRST_D0;
7933e95b9abSLydia Wang 	/* SW0 (17h), AIW 0/1 (13h/14h) */
794054d867eSTakashi Iwai 	update_power_state(codec, 0x17, parm);
795054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
796054d867eSTakashi Iwai 	update_power_state(codec, 0x14, parm);
7973e95b9abSLydia Wang 
7983e95b9abSLydia Wang 	/* outputs */
7993e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
8003e95b9abSLydia Wang 	parm = AC_PWRST_D3;
8013e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
802b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
8033e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
804054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
805054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
8063e95b9abSLydia Wang 
8073e95b9abSLydia Wang 	/* PW6 (22h), SW2 (26h), AOW2 (24h) */
8083e95b9abSLydia Wang 	if (is_8ch) {
8093e95b9abSLydia Wang 		parm = AC_PWRST_D3;
8103e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
811b3f6008fSTakashi Iwai 		if (smart51_enabled(codec))
8123e95b9abSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
813054d867eSTakashi Iwai 		update_power_state(codec, 0x26, parm);
814054d867eSTakashi Iwai 		update_power_state(codec, 0x24, parm);
815bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397) {
816bc92df7fSLydia Wang 		/* PW7(23h), SW2(27h), AOW2(25h) */
817bc92df7fSLydia Wang 		parm = AC_PWRST_D3;
818bc92df7fSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
819b3f6008fSTakashi Iwai 		if (smart51_enabled(codec))
820bc92df7fSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
821054d867eSTakashi Iwai 		update_power_state(codec, 0x27, parm);
822054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
8233e95b9abSLydia Wang 	}
8243e95b9abSLydia Wang 
8253e95b9abSLydia Wang 	/* PW 3/4/7 (1ch/1dh/23h) */
8263e95b9abSLydia Wang 	parm = AC_PWRST_D3;
8273e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
8283e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
8293e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
8303e95b9abSLydia Wang 	if (is_8ch)
8313e95b9abSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
8323e95b9abSLydia Wang 
8333e95b9abSLydia Wang 	/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
834054d867eSTakashi Iwai 	update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
835054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
8363e95b9abSLydia Wang 	if (is_8ch) {
837054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
838054d867eSTakashi Iwai 		update_power_state(codec, 0x27, parm);
839b3f6008fSTakashi Iwai 	} else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled)
840054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
8413e95b9abSLydia Wang }
8423e95b9abSLydia Wang 
843518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
844ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec)
845f7278fd0SJosepch Chan {
846f7278fd0SJosepch Chan 	struct via_spec *spec;
847f7278fd0SJosepch Chan 	int err;
848f7278fd0SJosepch Chan 
849518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
850518bf3baSLydia Wang 		return patch_vt1708S(codec);
851ddd304d8STakashi Iwai 
852f7278fd0SJosepch Chan 	/* create a codec specific record */
8535b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
854f7278fd0SJosepch Chan 	if (spec == NULL)
855f7278fd0SJosepch Chan 		return -ENOMEM;
856f7278fd0SJosepch Chan 
857b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
858620e2b28STakashi Iwai 
859f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
86012daef65STakashi Iwai 	err = via_parse_auto_config(codec);
861f7278fd0SJosepch Chan 	if (err < 0) {
862f7278fd0SJosepch Chan 		via_free(codec);
863f7278fd0SJosepch Chan 		return err;
864f7278fd0SJosepch Chan 	}
865f7278fd0SJosepch Chan 
866f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
867f7278fd0SJosepch Chan 
8683e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
8693e95b9abSLydia Wang 
870f7278fd0SJosepch Chan 	return 0;
871f7278fd0SJosepch Chan }
872f7278fd0SJosepch Chan 
873d949cac1SHarald Welte /* Patch for VT1708S */
874096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = {
875d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
876d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
877bc7e7e5cSLydia Wang 	/* don't bybass mixer */
878bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
879d949cac1SHarald Welte 	{ }
880d949cac1SHarald Welte };
881d949cac1SHarald Welte 
8826369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
8836369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
8846369bcfcSLydia Wang {
885d045c5dcSTakashi Iwai 	snd_hda_override_wcaps(codec, pin,
886d045c5dcSTakashi Iwai 			       get_wcaps(codec, pin) | AC_WCAP_IN_AMP);
8876369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
8886369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
8896369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
8906369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
8916369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
8926369bcfcSLydia Wang }
8936369bcfcSLydia Wang 
894d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
895d949cac1SHarald Welte {
896d949cac1SHarald Welte 	struct via_spec *spec;
897d949cac1SHarald Welte 	int err;
898d949cac1SHarald Welte 
899d949cac1SHarald Welte 	/* create a codec specific record */
9005b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
901d949cac1SHarald Welte 	if (spec == NULL)
902d949cac1SHarald Welte 		return -ENOMEM;
903d949cac1SHarald Welte 
904b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
905d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
906d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
907620e2b28STakashi Iwai 
908518bf3baSLydia Wang 	/* correct names for VT1708BCE */
909518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
910518bf3baSLydia Wang 		kfree(codec->chip_name);
911518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
9126efdd851STakashi Iwai 		snprintf(codec->card->mixername,
9136efdd851STakashi Iwai 			 sizeof(codec->card->mixername),
914518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
915970f630fSLydia Wang 	}
916bc92df7fSLydia Wang 	/* correct names for VT1705 */
917bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)	{
918bc92df7fSLydia Wang 		kfree(codec->chip_name);
919bc92df7fSLydia Wang 		codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
9206efdd851STakashi Iwai 		snprintf(codec->card->mixername,
9216efdd851STakashi Iwai 			 sizeof(codec->card->mixername),
922bc92df7fSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
923bc92df7fSLydia Wang 	}
924b3f6008fSTakashi Iwai 
925b3f6008fSTakashi Iwai 	/* automatic parse from the BIOS config */
926b3f6008fSTakashi Iwai 	err = via_parse_auto_config(codec);
927b3f6008fSTakashi Iwai 	if (err < 0) {
928b3f6008fSTakashi Iwai 		via_free(codec);
929b3f6008fSTakashi Iwai 		return err;
930b3f6008fSTakashi Iwai 	}
931b3f6008fSTakashi Iwai 
932b3f6008fSTakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
933b3f6008fSTakashi Iwai 
934b3f6008fSTakashi Iwai 	codec->patch_ops = via_patch_ops;
935b3f6008fSTakashi Iwai 
9363e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
937d949cac1SHarald Welte 	return 0;
938d949cac1SHarald Welte }
939d949cac1SHarald Welte 
940d949cac1SHarald Welte /* Patch for VT1702 */
941d949cac1SHarald Welte 
942096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = {
943bc7e7e5cSLydia Wang 	/* mixer enable */
944bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
945bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
946bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
947d949cac1SHarald Welte 	{ }
948d949cac1SHarald Welte };
949d949cac1SHarald Welte 
9503e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec)
9513e95b9abSLydia Wang {
9523e95b9abSLydia Wang 	int imux_is_smixer =
9533e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
9543e95b9abSLydia Wang 	unsigned int parm;
9553e95b9abSLydia Wang 	/* inputs */
9563e95b9abSLydia Wang 	/* PW 1/2/5 (14h/15h/18h) */
9573e95b9abSLydia Wang 	parm = AC_PWRST_D3;
9583e95b9abSLydia Wang 	set_pin_power_state(codec, 0x14, &parm);
9593e95b9abSLydia Wang 	set_pin_power_state(codec, 0x15, &parm);
9603e95b9abSLydia Wang 	set_pin_power_state(codec, 0x18, &parm);
9613e95b9abSLydia Wang 	if (imux_is_smixer)
9623e95b9abSLydia Wang 		parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
9633e95b9abSLydia Wang 	/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
964054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
965054d867eSTakashi Iwai 	update_power_state(codec, 0x12, parm);
966054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
967054d867eSTakashi Iwai 	update_power_state(codec, 0x20, parm);
9683e95b9abSLydia Wang 
9693e95b9abSLydia Wang 	/* outputs */
9703e95b9abSLydia Wang 	/* PW 3/4 (16h/17h) */
9713e95b9abSLydia Wang 	parm = AC_PWRST_D3;
9723e95b9abSLydia Wang 	set_pin_power_state(codec, 0x17, &parm);
9733e95b9abSLydia Wang 	set_pin_power_state(codec, 0x16, &parm);
9743e95b9abSLydia Wang 	/* MW0 (1ah), AOW 0/1 (10h/1dh) */
975054d867eSTakashi Iwai 	update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm);
976054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
977054d867eSTakashi Iwai 	update_power_state(codec, 0x1d, parm);
9783e95b9abSLydia Wang }
9793e95b9abSLydia Wang 
980d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
981d949cac1SHarald Welte {
982d949cac1SHarald Welte 	struct via_spec *spec;
983d949cac1SHarald Welte 	int err;
984d949cac1SHarald Welte 
985d949cac1SHarald Welte 	/* create a codec specific record */
9865b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
987d949cac1SHarald Welte 	if (spec == NULL)
988d949cac1SHarald Welte 		return -ENOMEM;
989d949cac1SHarald Welte 
990b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x1a;
991620e2b28STakashi Iwai 
99212daef65STakashi Iwai 	/* limit AA path volume to 0 dB */
99312daef65STakashi Iwai 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
99412daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
99512daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
99612daef65STakashi Iwai 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
99712daef65STakashi Iwai 				  (1 << AC_AMPCAP_MUTE_SHIFT));
99812daef65STakashi Iwai 
999d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
100012daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1001d949cac1SHarald Welte 	if (err < 0) {
1002d949cac1SHarald Welte 		via_free(codec);
1003d949cac1SHarald Welte 		return err;
1004d949cac1SHarald Welte 	}
1005d949cac1SHarald Welte 
1006096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
1007d949cac1SHarald Welte 
1008d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
1009d949cac1SHarald Welte 
10103e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
1011d949cac1SHarald Welte 	return 0;
1012d949cac1SHarald Welte }
1013d949cac1SHarald Welte 
1014eb7188caSLydia Wang /* Patch for VT1718S */
1015eb7188caSLydia Wang 
1016096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = {
10174ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
10184ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
1019eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
1020eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
10215d41762aSTakashi Iwai 
1022eb7188caSLydia Wang 	{ }
1023eb7188caSLydia Wang };
1024eb7188caSLydia Wang 
10253e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
10263e95b9abSLydia Wang {
10273e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
10283e95b9abSLydia Wang 	int imux_is_smixer;
10296162552bSTakashi Iwai 	unsigned int parm, parm2;
10303e95b9abSLydia Wang 	/* MUX6 (1eh) = stereo mixer */
10313e95b9abSLydia Wang 	imux_is_smixer =
10323e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
10333e95b9abSLydia Wang 	/* inputs */
10343e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
10353e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10363e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
10373e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
10383e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
10393e95b9abSLydia Wang 	if (imux_is_smixer)
10403e95b9abSLydia Wang 		parm = AC_PWRST_D0;
10413e95b9abSLydia Wang 	/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
1042054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
1043054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
1044054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1045054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
10463e95b9abSLydia Wang 
10473e95b9abSLydia Wang 	/* outputs */
10483e95b9abSLydia Wang 	/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
10493e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10503e95b9abSLydia Wang 	set_pin_power_state(codec, 0x27, &parm);
1051054d867eSTakashi Iwai 	update_power_state(codec, 0x1a, parm);
10526162552bSTakashi Iwai 	parm2 = parm; /* for pin 0x0b */
10533e95b9abSLydia Wang 
10543e95b9abSLydia Wang 	/* PW2 (26h), AOW2 (ah) */
10553e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10563e95b9abSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
1057b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
10583e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
1059054d867eSTakashi Iwai 	update_power_state(codec, 0xa, parm);
10603e95b9abSLydia Wang 
10613e95b9abSLydia Wang 	/* PW0 (24h), AOW0 (8h) */
10623e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10633e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
1064b3f6008fSTakashi Iwai 	if (!spec->gen.indep_hp_enabled) /* check for redirected HP */
10653e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
1066054d867eSTakashi Iwai 	update_power_state(codec, 0x8, parm);
1067b3f6008fSTakashi Iwai 	if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3)
10686162552bSTakashi Iwai 		parm = parm2;
10696162552bSTakashi Iwai 	update_power_state(codec, 0xb, parm);
10703e95b9abSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
1071054d867eSTakashi Iwai 	update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm);
10723e95b9abSLydia Wang 
10733e95b9abSLydia Wang 	/* PW1 (25h), AOW1 (9h) */
10743e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10753e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
1076b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
10773e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
1078054d867eSTakashi Iwai 	update_power_state(codec, 0x9, parm);
10793e95b9abSLydia Wang 
1080b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled) {
10813e95b9abSLydia Wang 		/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
10823e95b9abSLydia Wang 		parm = AC_PWRST_D3;
10833e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
1084054d867eSTakashi Iwai 		update_power_state(codec, 0x1b, parm);
1085054d867eSTakashi Iwai 		update_power_state(codec, 0x34, parm);
1086054d867eSTakashi Iwai 		update_power_state(codec, 0xc, parm);
10873e95b9abSLydia Wang 	}
10883e95b9abSLydia Wang }
10893e95b9abSLydia Wang 
109030b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs
109130b45033STakashi Iwai  * This isn't listed from the raw info, but the chip has a secret connection.
109230b45033STakashi Iwai  */
109330b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec)
109430b45033STakashi Iwai {
109530b45033STakashi Iwai 	struct via_spec *spec = codec->spec;
109630b45033STakashi Iwai 	int i, nums;
109730b45033STakashi Iwai 	hda_nid_t conn[8];
109830b45033STakashi Iwai 	hda_nid_t nid;
109930b45033STakashi Iwai 
1100b3f6008fSTakashi Iwai 	if (!spec->gen.mixer_nid)
110130b45033STakashi Iwai 		return 0;
1102b3f6008fSTakashi Iwai 	nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
110330b45033STakashi Iwai 				       ARRAY_SIZE(conn) - 1);
110430b45033STakashi Iwai 	for (i = 0; i < nums; i++) {
110530b45033STakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
110630b45033STakashi Iwai 			return 0;
110730b45033STakashi Iwai 	}
110830b45033STakashi Iwai 
110930b45033STakashi Iwai 	/* find the primary DAC and add to the connection list */
111030b45033STakashi Iwai 	nid = codec->start_nid;
111130b45033STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, nid++) {
111230b45033STakashi Iwai 		unsigned int caps = get_wcaps(codec, nid);
111330b45033STakashi Iwai 		if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
111430b45033STakashi Iwai 		    !(caps & AC_WCAP_DIGITAL)) {
111530b45033STakashi Iwai 			conn[nums++] = nid;
111630b45033STakashi Iwai 			return snd_hda_override_conn_list(codec,
1117b3f6008fSTakashi Iwai 							  spec->gen.mixer_nid,
111830b45033STakashi Iwai 							  nums, conn);
111930b45033STakashi Iwai 		}
112030b45033STakashi Iwai 	}
112130b45033STakashi Iwai 	return 0;
112230b45033STakashi Iwai }
112330b45033STakashi Iwai 
112430b45033STakashi Iwai 
1125eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
1126eb7188caSLydia Wang {
1127eb7188caSLydia Wang 	struct via_spec *spec;
1128eb7188caSLydia Wang 	int err;
1129eb7188caSLydia Wang 
1130eb7188caSLydia Wang 	/* create a codec specific record */
11315b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1132eb7188caSLydia Wang 	if (spec == NULL)
1133eb7188caSLydia Wang 		return -ENOMEM;
1134eb7188caSLydia Wang 
1135b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1136d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1137d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
113830b45033STakashi Iwai 	add_secret_dac_path(codec);
1139620e2b28STakashi Iwai 
1140eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
114112daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1142eb7188caSLydia Wang 	if (err < 0) {
1143eb7188caSLydia Wang 		via_free(codec);
1144eb7188caSLydia Wang 		return err;
1145eb7188caSLydia Wang 	}
1146eb7188caSLydia Wang 
1147096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
1148eb7188caSLydia Wang 
1149eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
1150eb7188caSLydia Wang 
11513e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1718S;
11523e95b9abSLydia Wang 
1153eb7188caSLydia Wang 	return 0;
1154eb7188caSLydia Wang }
1155f3db423dSLydia Wang 
1156f3db423dSLydia Wang /* Patch for VT1716S */
1157f3db423dSLydia Wang 
1158f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
1159f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
1160f3db423dSLydia Wang {
1161f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
1162f3db423dSLydia Wang 	uinfo->count = 1;
1163f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
1164f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
1165f3db423dSLydia Wang 	return 0;
1166f3db423dSLydia Wang }
1167f3db423dSLydia Wang 
1168f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
1169f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
1170f3db423dSLydia Wang {
1171f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1172f3db423dSLydia Wang 	int index = 0;
1173f3db423dSLydia Wang 
1174f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
1175f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
1176f3db423dSLydia Wang 	if (index != -1)
1177f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
1178f3db423dSLydia Wang 
1179f3db423dSLydia Wang 	return 0;
1180f3db423dSLydia Wang }
1181f3db423dSLydia Wang 
1182f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
1183f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
1184f3db423dSLydia Wang {
1185f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1186f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
1187f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
1188f3db423dSLydia Wang 
1189f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
1190f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
1191f3db423dSLydia Wang 	spec->dmic_enabled = index;
11923e95b9abSLydia Wang 	set_widgets_power_state(codec);
1193f3db423dSLydia Wang 	return 1;
1194f3db423dSLydia Wang }
1195f3db423dSLydia Wang 
119690dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
1197f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
1198f3db423dSLydia Wang 	{
1199f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1200f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
12015b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
1202f3db423dSLydia Wang 	 .count = 1,
1203f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
1204f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
1205f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
1206f3db423dSLydia Wang 	 },
1207f3db423dSLydia Wang 	{}			/* end */
1208f3db423dSLydia Wang };
1209f3db423dSLydia Wang 
1210f3db423dSLydia Wang 
1211f3db423dSLydia Wang /* mono-out mixer elements */
121290dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
1213f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
1214f3db423dSLydia Wang 	{ } /* end */
1215f3db423dSLydia Wang };
1216f3db423dSLydia Wang 
1217096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = {
1218f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
1219f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
1220f3db423dSLydia Wang 	/* don't bybass mixer */
1221f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
1222f3db423dSLydia Wang 	/* Enable mono output */
1223f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
1224f3db423dSLydia Wang 	{ }
1225f3db423dSLydia Wang };
1226f3db423dSLydia Wang 
12273e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
12283e95b9abSLydia Wang {
12293e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
12303e95b9abSLydia Wang 	int imux_is_smixer;
12313e95b9abSLydia Wang 	unsigned int parm;
12323e95b9abSLydia Wang 	unsigned int mono_out, present;
12333e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
12343e95b9abSLydia Wang 	imux_is_smixer =
12353e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0,
12363e95b9abSLydia Wang 			    AC_VERB_GET_CONNECT_SEL, 0x00) ==  5);
12373e95b9abSLydia Wang 	/* inputs */
12383e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
12393e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12403e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
12413e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
12423e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
12433e95b9abSLydia Wang 	if (imux_is_smixer)
12443e95b9abSLydia Wang 		parm = AC_PWRST_D0;
12453e95b9abSLydia Wang 	/* SW0 (17h), AIW0(13h) */
1246054d867eSTakashi Iwai 	update_power_state(codec, 0x17, parm);
1247054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
12483e95b9abSLydia Wang 
12493e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12503e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
12513e95b9abSLydia Wang 	/* PW11 (22h) */
12523e95b9abSLydia Wang 	if (spec->dmic_enabled)
12533e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
12543e95b9abSLydia Wang 	else
1255054d867eSTakashi Iwai 		update_power_state(codec, 0x22, AC_PWRST_D3);
12563e95b9abSLydia Wang 
12573e95b9abSLydia Wang 	/* SW2(26h), AIW1(14h) */
1258054d867eSTakashi Iwai 	update_power_state(codec, 0x26, parm);
1259054d867eSTakashi Iwai 	update_power_state(codec, 0x14, parm);
12603e95b9abSLydia Wang 
12613e95b9abSLydia Wang 	/* outputs */
12623e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
12633e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12643e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
12653e95b9abSLydia Wang 	/* Smart 5.1 PW2(1bh) */
1266b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
12673e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
1268054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
1269054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
12703e95b9abSLydia Wang 
12713e95b9abSLydia Wang 	/* PW7 (23h), SW3 (27h), AOW3 (25h) */
12723e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12733e95b9abSLydia Wang 	set_pin_power_state(codec, 0x23, &parm);
12743e95b9abSLydia Wang 	/* Smart 5.1 PW1(1ah) */
1275b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
12763e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
1277054d867eSTakashi Iwai 	update_power_state(codec, 0x27, parm);
12783e95b9abSLydia Wang 
12793e95b9abSLydia Wang 	/* Smart 5.1 PW5(1eh) */
1280b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
12813e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
1282054d867eSTakashi Iwai 	update_power_state(codec, 0x25, parm);
12833e95b9abSLydia Wang 
12843e95b9abSLydia Wang 	/* Mono out */
12853e95b9abSLydia Wang 	/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
12863e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x1c);
12873e95b9abSLydia Wang 
12883e95b9abSLydia Wang 	if (present)
12893e95b9abSLydia Wang 		mono_out = 0;
12903e95b9abSLydia Wang 	else {
12913e95b9abSLydia Wang 		present = snd_hda_jack_detect(codec, 0x1d);
1292b3f6008fSTakashi Iwai 		if (!spec->gen.indep_hp_enabled && present)
12933e95b9abSLydia Wang 			mono_out = 0;
12943e95b9abSLydia Wang 		else
12953e95b9abSLydia Wang 			mono_out = 1;
12963e95b9abSLydia Wang 	}
12973e95b9abSLydia Wang 	parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
1298054d867eSTakashi Iwai 	update_power_state(codec, 0x28, parm);
1299054d867eSTakashi Iwai 	update_power_state(codec, 0x29, parm);
1300054d867eSTakashi Iwai 	update_power_state(codec, 0x2a, parm);
13013e95b9abSLydia Wang 
13023e95b9abSLydia Wang 	/* PW 3/4 (1ch/1dh) */
13033e95b9abSLydia Wang 	parm = AC_PWRST_D3;
13043e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
13053e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
13063e95b9abSLydia Wang 	/* HP Independent Mode, power on AOW3 */
1307b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
1308054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
13093e95b9abSLydia Wang 
13103e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
13113e95b9abSLydia Wang 	/* MW0 (16h), AOW0 (10h) */
1312054d867eSTakashi Iwai 	update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
1313054d867eSTakashi Iwai 	update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm);
13143e95b9abSLydia Wang }
13153e95b9abSLydia Wang 
1316f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
1317f3db423dSLydia Wang {
1318f3db423dSLydia Wang 	struct via_spec *spec;
1319f3db423dSLydia Wang 	int err;
1320f3db423dSLydia Wang 
1321f3db423dSLydia Wang 	/* create a codec specific record */
13225b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1323f3db423dSLydia Wang 	if (spec == NULL)
1324f3db423dSLydia Wang 		return -ENOMEM;
1325f3db423dSLydia Wang 
1326b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
1327d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
1328d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
1329620e2b28STakashi Iwai 
1330f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
133112daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1332f3db423dSLydia Wang 	if (err < 0) {
1333f3db423dSLydia Wang 		via_free(codec);
1334f3db423dSLydia Wang 		return err;
1335f3db423dSLydia Wang 	}
1336f3db423dSLydia Wang 
1337096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_init_verbs;
1338f3db423dSLydia Wang 
1339b3f6008fSTakashi Iwai 	spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer;
1340f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
1341f3db423dSLydia Wang 
1342f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
1343f3db423dSLydia Wang 
13443e95b9abSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
1345f3db423dSLydia Wang 	return 0;
1346f3db423dSLydia Wang }
134725eaba2fSLydia Wang 
134825eaba2fSLydia Wang /* for vt2002P */
134925eaba2fSLydia Wang 
1350096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = {
1351eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
1352eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
1353eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
1354eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
135525eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
135625eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
135725eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
135825eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
135925eaba2fSLydia Wang 	{ }
136025eaba2fSLydia Wang };
13614a918ffeSTakashi Iwai 
1362096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = {
136311890956SLydia Wang 	/* Enable Boost Volume backdoor */
136411890956SLydia Wang 	{0x1, 0xfb9, 0x24},
136511890956SLydia Wang 	/* Enable AOW0 to MW9 */
136611890956SLydia Wang 	{0x1, 0xfb8, 0x88},
136711890956SLydia Wang 	{ }
136811890956SLydia Wang };
136925eaba2fSLydia Wang 
13703e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
13713e95b9abSLydia Wang {
13723e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
13733e95b9abSLydia Wang 	int imux_is_smixer;
13743e95b9abSLydia Wang 	unsigned int parm;
13753e95b9abSLydia Wang 	unsigned int present;
13763e95b9abSLydia Wang 	/* MUX9 (1eh) = stereo mixer */
13773e95b9abSLydia Wang 	imux_is_smixer =
13783e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
13793e95b9abSLydia Wang 	/* inputs */
13803e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
13813e95b9abSLydia Wang 	parm = AC_PWRST_D3;
13823e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
13833e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
13843e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
13853e95b9abSLydia Wang 	parm = AC_PWRST_D0;
13863e95b9abSLydia Wang 	/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
1387054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
1388054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
1389054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1390054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
13913e95b9abSLydia Wang 
13923e95b9abSLydia Wang 	/* outputs */
13933e95b9abSLydia Wang 	/* AOW0 (8h)*/
1394054d867eSTakashi Iwai 	update_power_state(codec, 0x8, parm);
13953e95b9abSLydia Wang 
139611890956SLydia Wang 	if (spec->codec_type == VT1802) {
139711890956SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
139811890956SLydia Wang 		parm = AC_PWRST_D3;
139911890956SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
1400054d867eSTakashi Iwai 		update_power_state(codec, 0x18, parm);
1401054d867eSTakashi Iwai 		update_power_state(codec, 0x38, parm);
140211890956SLydia Wang 	} else {
14033e95b9abSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
14043e95b9abSLydia Wang 		parm = AC_PWRST_D3;
14053e95b9abSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
1406054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, parm);
1407054d867eSTakashi Iwai 		update_power_state(codec, 0x37, parm);
140811890956SLydia Wang 	}
14093e95b9abSLydia Wang 
141011890956SLydia Wang 	if (spec->codec_type == VT1802) {
141111890956SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
141211890956SLydia Wang 		parm = AC_PWRST_D3;
141311890956SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
1414054d867eSTakashi Iwai 		update_power_state(codec, 0x15, parm);
1415054d867eSTakashi Iwai 		update_power_state(codec, 0x35, parm);
141611890956SLydia Wang 	} else {
14173e95b9abSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
14183e95b9abSLydia Wang 		parm = AC_PWRST_D3;
14193e95b9abSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
1420054d867eSTakashi Iwai 		update_power_state(codec, 0x19, parm);
1421054d867eSTakashi Iwai 		update_power_state(codec, 0x35, parm);
142211890956SLydia Wang 	}
14233e95b9abSLydia Wang 
1424b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
1425054d867eSTakashi Iwai 		update_power_state(codec, 0x9, AC_PWRST_D0);
14263e95b9abSLydia Wang 
14273e95b9abSLydia Wang 	/* Class-D */
14283e95b9abSLydia Wang 	/* PW0 (24h), MW0(18h/14h), MUX0(34h) */
14293e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
14303e95b9abSLydia Wang 
14313e95b9abSLydia Wang 	parm = AC_PWRST_D3;
14323e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
14333e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
143411890956SLydia Wang 	if (spec->codec_type == VT1802)
1435054d867eSTakashi Iwai 		update_power_state(codec, 0x14, parm);
143611890956SLydia Wang 	else
1437054d867eSTakashi Iwai 		update_power_state(codec, 0x18, parm);
1438054d867eSTakashi Iwai 	update_power_state(codec, 0x34, parm);
14393e95b9abSLydia Wang 
14403e95b9abSLydia Wang 	/* Mono Out */
14413e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x26);
14423e95b9abSLydia Wang 
14433e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
144411890956SLydia Wang 	if (spec->codec_type == VT1802) {
144511890956SLydia Wang 		/* PW15 (33h), MW8(1ch), MUX8(3ch) */
1446054d867eSTakashi Iwai 		update_power_state(codec, 0x33, parm);
1447054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, parm);
1448054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, parm);
144911890956SLydia Wang 	} else {
14503e95b9abSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
1451054d867eSTakashi Iwai 		update_power_state(codec, 0x31, parm);
1452054d867eSTakashi Iwai 		update_power_state(codec, 0x17, parm);
1453054d867eSTakashi Iwai 		update_power_state(codec, 0x3b, parm);
145411890956SLydia Wang 	}
14553e95b9abSLydia Wang 	/* MW9 (21h) */
14563e95b9abSLydia Wang 	if (imux_is_smixer || !is_aa_path_mute(codec))
1457054d867eSTakashi Iwai 		update_power_state(codec, 0x21, AC_PWRST_D0);
14583e95b9abSLydia Wang 	else
1459054d867eSTakashi Iwai 		update_power_state(codec, 0x21, AC_PWRST_D3);
14603e95b9abSLydia Wang }
146125eaba2fSLydia Wang 
14624b527b65SDavid Henningsson /*
14634b527b65SDavid Henningsson  * pin fix-up
14644b527b65SDavid Henningsson  */
14654b527b65SDavid Henningsson enum {
14664b527b65SDavid Henningsson 	VIA_FIXUP_INTMIC_BOOST,
1467d5266125STakashi Iwai 	VIA_FIXUP_ASUS_G75,
14684b527b65SDavid Henningsson };
14694b527b65SDavid Henningsson 
14704b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec,
14714b527b65SDavid Henningsson 				  const struct hda_fixup *fix, int action)
14724b527b65SDavid Henningsson {
14734b527b65SDavid Henningsson 	if (action == HDA_FIXUP_ACT_PRE_PROBE)
14744b527b65SDavid Henningsson 		override_mic_boost(codec, 0x30, 0, 2, 40);
14754b527b65SDavid Henningsson }
14764b527b65SDavid Henningsson 
14774b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = {
14784b527b65SDavid Henningsson 	[VIA_FIXUP_INTMIC_BOOST] = {
14794b527b65SDavid Henningsson 		.type = HDA_FIXUP_FUNC,
14804b527b65SDavid Henningsson 		.v.func = via_fixup_intmic_boost,
14814b527b65SDavid Henningsson 	},
1482d5266125STakashi Iwai 	[VIA_FIXUP_ASUS_G75] = {
1483d5266125STakashi Iwai 		.type = HDA_FIXUP_PINS,
1484d5266125STakashi Iwai 		.v.pins = (const struct hda_pintbl[]) {
1485d5266125STakashi Iwai 			/* set 0x24 and 0x33 as speakers */
1486d5266125STakashi Iwai 			{ 0x24, 0x991301f0 },
1487d5266125STakashi Iwai 			{ 0x33, 0x991301f1 }, /* subwoofer */
1488d5266125STakashi Iwai 			{ }
1489d5266125STakashi Iwai 		}
1490d5266125STakashi Iwai 	},
14914b527b65SDavid Henningsson };
14924b527b65SDavid Henningsson 
14934b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = {
1494d5266125STakashi Iwai 	SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
14954b527b65SDavid Henningsson 	SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
14964b527b65SDavid Henningsson 	{}
14974b527b65SDavid Henningsson };
14984b527b65SDavid Henningsson 
1499ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
1500ef4da458STakashi Iwai  * Replace this with mixer NID 0x1c
1501ef4da458STakashi Iwai  */
1502ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec)
1503ef4da458STakashi Iwai {
1504ef4da458STakashi Iwai 	static hda_nid_t conn_24[] = { 0x14, 0x1c };
1505ef4da458STakashi Iwai 	static hda_nid_t conn_33[] = { 0x1c };
1506ef4da458STakashi Iwai 
1507ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
1508ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
1509ef4da458STakashi Iwai }
1510ef4da458STakashi Iwai 
151125eaba2fSLydia Wang /* patch for vt2002P */
151225eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
151325eaba2fSLydia Wang {
151425eaba2fSLydia Wang 	struct via_spec *spec;
151525eaba2fSLydia Wang 	int err;
151625eaba2fSLydia Wang 
151725eaba2fSLydia Wang 	/* create a codec specific record */
15185b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
151925eaba2fSLydia Wang 	if (spec == NULL)
152025eaba2fSLydia Wang 		return -ENOMEM;
152125eaba2fSLydia Wang 
1522b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1523d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1524d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
1525ef4da458STakashi Iwai 	if (spec->codec_type == VT1802)
1526ef4da458STakashi Iwai 		fix_vt1802_connections(codec);
152730b45033STakashi Iwai 	add_secret_dac_path(codec);
1528620e2b28STakashi Iwai 
15294b527b65SDavid Henningsson 	snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
15304b527b65SDavid Henningsson 	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
15314b527b65SDavid Henningsson 
153225eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
153312daef65STakashi Iwai 	err = via_parse_auto_config(codec);
153425eaba2fSLydia Wang 	if (err < 0) {
153525eaba2fSLydia Wang 		via_free(codec);
153625eaba2fSLydia Wang 		return err;
153725eaba2fSLydia Wang 	}
153825eaba2fSLydia Wang 
153911890956SLydia Wang 	if (spec->codec_type == VT1802)
15404a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
154111890956SLydia Wang 	else
15424a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
154311890956SLydia Wang 
154425eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
154525eaba2fSLydia Wang 
15463e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt2002P;
154725eaba2fSLydia Wang 	return 0;
154825eaba2fSLydia Wang }
1549ab6734e7SLydia Wang 
1550ab6734e7SLydia Wang /* for vt1812 */
1551ab6734e7SLydia Wang 
1552096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = {
1553ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
1554ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
1555ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
1556ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
1557ab6734e7SLydia Wang 	{ }
1558ab6734e7SLydia Wang };
1559ab6734e7SLydia Wang 
15603e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec)
15613e95b9abSLydia Wang {
15623e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
15633e95b9abSLydia Wang 	unsigned int parm;
15643e95b9abSLydia Wang 	unsigned int present;
15653e95b9abSLydia Wang 	/* inputs */
15663e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
15673e95b9abSLydia Wang 	parm = AC_PWRST_D3;
15683e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
15693e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
15703e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
15713e95b9abSLydia Wang 	parm = AC_PWRST_D0;
15723e95b9abSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
1573054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
1574054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
1575054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1576054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
15773e95b9abSLydia Wang 
15783e95b9abSLydia Wang 	/* outputs */
15793e95b9abSLydia Wang 	/* AOW0 (8h)*/
1580054d867eSTakashi Iwai 	update_power_state(codec, 0x8, AC_PWRST_D0);
15813e95b9abSLydia Wang 
15823e95b9abSLydia Wang 	/* PW4 (28h), MW4 (18h), MUX4(38h) */
15833e95b9abSLydia Wang 	parm = AC_PWRST_D3;
15843e95b9abSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
1585054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
1586054d867eSTakashi Iwai 	update_power_state(codec, 0x38, parm);
15873e95b9abSLydia Wang 
15883e95b9abSLydia Wang 	/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
15893e95b9abSLydia Wang 	parm = AC_PWRST_D3;
15903e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
1591054d867eSTakashi Iwai 	update_power_state(codec, 0x15, parm);
1592054d867eSTakashi Iwai 	update_power_state(codec, 0x35, parm);
1593b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
1594054d867eSTakashi Iwai 		update_power_state(codec, 0x9, AC_PWRST_D0);
15953e95b9abSLydia Wang 
15963e95b9abSLydia Wang 	/* Internal Speaker */
15973e95b9abSLydia Wang 	/* PW0 (24h), MW0(14h), MUX0(34h) */
15983e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
15993e95b9abSLydia Wang 
16003e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16013e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
16023e95b9abSLydia Wang 	if (present) {
1603054d867eSTakashi Iwai 		update_power_state(codec, 0x14, AC_PWRST_D3);
1604054d867eSTakashi Iwai 		update_power_state(codec, 0x34, AC_PWRST_D3);
16053e95b9abSLydia Wang 	} else {
1606054d867eSTakashi Iwai 		update_power_state(codec, 0x14, AC_PWRST_D0);
1607054d867eSTakashi Iwai 		update_power_state(codec, 0x34, AC_PWRST_D0);
16083e95b9abSLydia Wang 	}
16093e95b9abSLydia Wang 
16103e95b9abSLydia Wang 
16113e95b9abSLydia Wang 	/* Mono Out */
16123e95b9abSLydia Wang 	/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
16133e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x28);
16143e95b9abSLydia Wang 
16153e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16163e95b9abSLydia Wang 	set_pin_power_state(codec, 0x31, &parm);
16173e95b9abSLydia Wang 	if (present) {
1618054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, AC_PWRST_D3);
1619054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, AC_PWRST_D3);
1620054d867eSTakashi Iwai 		update_power_state(codec, 0x3e, AC_PWRST_D3);
16213e95b9abSLydia Wang 	} else {
1622054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, AC_PWRST_D0);
1623054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, AC_PWRST_D0);
1624054d867eSTakashi Iwai 		update_power_state(codec, 0x3e, AC_PWRST_D0);
16253e95b9abSLydia Wang 	}
16263e95b9abSLydia Wang 
16273e95b9abSLydia Wang 	/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
16283e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16293e95b9abSLydia Wang 	set_pin_power_state(codec, 0x33, &parm);
1630054d867eSTakashi Iwai 	update_power_state(codec, 0x1d, parm);
1631054d867eSTakashi Iwai 	update_power_state(codec, 0x3d, parm);
16323e95b9abSLydia Wang 
16333e95b9abSLydia Wang }
1634ab6734e7SLydia Wang 
1635ab6734e7SLydia Wang /* patch for vt1812 */
1636ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
1637ab6734e7SLydia Wang {
1638ab6734e7SLydia Wang 	struct via_spec *spec;
1639ab6734e7SLydia Wang 	int err;
1640ab6734e7SLydia Wang 
1641ab6734e7SLydia Wang 	/* create a codec specific record */
16425b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1643ab6734e7SLydia Wang 	if (spec == NULL)
1644ab6734e7SLydia Wang 		return -ENOMEM;
1645ab6734e7SLydia Wang 
1646b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1647d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1648d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
164930b45033STakashi Iwai 	add_secret_dac_path(codec);
1650620e2b28STakashi Iwai 
1651ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
165212daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1653ab6734e7SLydia Wang 	if (err < 0) {
1654ab6734e7SLydia Wang 		via_free(codec);
1655ab6734e7SLydia Wang 		return err;
1656ab6734e7SLydia Wang 	}
1657ab6734e7SLydia Wang 
1658096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1812_init_verbs;
1659ab6734e7SLydia Wang 
1660ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
1661ab6734e7SLydia Wang 
16623e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1812;
1663ab6734e7SLydia Wang 	return 0;
1664ab6734e7SLydia Wang }
1665ab6734e7SLydia Wang 
166643737e0aSLydia Wang /* patch for vt3476 */
166743737e0aSLydia Wang 
166843737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = {
166943737e0aSLydia Wang 	/* Enable DMic 8/16/32K */
167043737e0aSLydia Wang 	{0x1, 0xF7B, 0x30},
167143737e0aSLydia Wang 	/* Enable Boost Volume backdoor */
167243737e0aSLydia Wang 	{0x1, 0xFB9, 0x20},
167343737e0aSLydia Wang 	/* Enable AOW-MW9 path */
167443737e0aSLydia Wang 	{0x1, 0xFB8, 0x10},
167543737e0aSLydia Wang 	{ }
167643737e0aSLydia Wang };
167743737e0aSLydia Wang 
167843737e0aSLydia Wang static void set_widgets_power_state_vt3476(struct hda_codec *codec)
167943737e0aSLydia Wang {
168043737e0aSLydia Wang 	struct via_spec *spec = codec->spec;
168143737e0aSLydia Wang 	int imux_is_smixer;
168243737e0aSLydia Wang 	unsigned int parm, parm2;
168343737e0aSLydia Wang 	/* MUX10 (1eh) = stereo mixer */
168443737e0aSLydia Wang 	imux_is_smixer =
168543737e0aSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 4;
168643737e0aSLydia Wang 	/* inputs */
168743737e0aSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
168843737e0aSLydia Wang 	parm = AC_PWRST_D3;
168943737e0aSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
169043737e0aSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
169143737e0aSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
169243737e0aSLydia Wang 	if (imux_is_smixer)
169343737e0aSLydia Wang 		parm = AC_PWRST_D0;
169443737e0aSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
169543737e0aSLydia Wang 	update_power_state(codec, 0x1e, parm);
169643737e0aSLydia Wang 	update_power_state(codec, 0x1f, parm);
169743737e0aSLydia Wang 	update_power_state(codec, 0x10, parm);
169843737e0aSLydia Wang 	update_power_state(codec, 0x11, parm);
169943737e0aSLydia Wang 
170043737e0aSLydia Wang 	/* outputs */
170143737e0aSLydia Wang 	/* PW3 (27h), MW3(37h), AOW3 (bh) */
170243737e0aSLydia Wang 	if (spec->codec_type == VT1705CF) {
170343737e0aSLydia Wang 		parm = AC_PWRST_D3;
170443737e0aSLydia Wang 		update_power_state(codec, 0x27, parm);
170543737e0aSLydia Wang 		update_power_state(codec, 0x37, parm);
170643737e0aSLydia Wang 	}	else {
170743737e0aSLydia Wang 		parm = AC_PWRST_D3;
170843737e0aSLydia Wang 		set_pin_power_state(codec, 0x27, &parm);
170943737e0aSLydia Wang 		update_power_state(codec, 0x37, parm);
171043737e0aSLydia Wang 	}
171143737e0aSLydia Wang 
171243737e0aSLydia Wang 	/* PW2 (26h), MW2(36h), AOW2 (ah) */
171343737e0aSLydia Wang 	parm = AC_PWRST_D3;
171443737e0aSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
171543737e0aSLydia Wang 	update_power_state(codec, 0x36, parm);
1716b3f6008fSTakashi Iwai 	if (smart51_enabled(codec)) {
171743737e0aSLydia Wang 		/* PW7(2bh), MW7(3bh), MUX7(1Bh) */
171843737e0aSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
171943737e0aSLydia Wang 		update_power_state(codec, 0x3b, parm);
172043737e0aSLydia Wang 		update_power_state(codec, 0x1b, parm);
172143737e0aSLydia Wang 	}
172243737e0aSLydia Wang 	update_conv_power_state(codec, 0xa, parm, 2);
172343737e0aSLydia Wang 
172443737e0aSLydia Wang 	/* PW1 (25h), MW1(35h), AOW1 (9h) */
172543737e0aSLydia Wang 	parm = AC_PWRST_D3;
172643737e0aSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
172743737e0aSLydia Wang 	update_power_state(codec, 0x35, parm);
1728b3f6008fSTakashi Iwai 	if (smart51_enabled(codec)) {
172943737e0aSLydia Wang 		/* PW6(2ah), MW6(3ah), MUX6(1ah) */
173043737e0aSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
173143737e0aSLydia Wang 		update_power_state(codec, 0x3a, parm);
173243737e0aSLydia Wang 		update_power_state(codec, 0x1a, parm);
173343737e0aSLydia Wang 	}
173443737e0aSLydia Wang 	update_conv_power_state(codec, 0x9, parm, 1);
173543737e0aSLydia Wang 
173643737e0aSLydia Wang 	/* PW4 (28h), MW4 (38h), MUX4(18h), AOW3(bh)/AOW0(8h) */
173743737e0aSLydia Wang 	parm = AC_PWRST_D3;
173843737e0aSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
173943737e0aSLydia Wang 	update_power_state(codec, 0x38, parm);
174043737e0aSLydia Wang 	update_power_state(codec, 0x18, parm);
1741b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
174243737e0aSLydia Wang 		update_conv_power_state(codec, 0xb, parm, 3);
174343737e0aSLydia Wang 	parm2 = parm; /* for pin 0x0b */
174443737e0aSLydia Wang 
174543737e0aSLydia Wang 	/* PW0 (24h), MW0(34h), MW9(3fh), AOW0 (8h) */
174643737e0aSLydia Wang 	parm = AC_PWRST_D3;
174743737e0aSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
174843737e0aSLydia Wang 	update_power_state(codec, 0x34, parm);
1749b3f6008fSTakashi Iwai 	if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3)
175043737e0aSLydia Wang 		parm = parm2;
175143737e0aSLydia Wang 	update_conv_power_state(codec, 0x8, parm, 0);
175243737e0aSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
175343737e0aSLydia Wang 	update_power_state(codec, 0x3f, imux_is_smixer ? AC_PWRST_D0 : parm);
175443737e0aSLydia Wang }
175543737e0aSLydia Wang 
175643737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec)
175743737e0aSLydia Wang {
175843737e0aSLydia Wang 	struct via_spec *spec;
175943737e0aSLydia Wang 	int err;
176043737e0aSLydia Wang 
176143737e0aSLydia Wang 	/* create a codec specific record */
176243737e0aSLydia Wang 	spec = via_new_spec(codec);
176343737e0aSLydia Wang 	if (spec == NULL)
176443737e0aSLydia Wang 		return -ENOMEM;
176543737e0aSLydia Wang 
1766b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x3f;
176743737e0aSLydia Wang 	add_secret_dac_path(codec);
176843737e0aSLydia Wang 
176943737e0aSLydia Wang 	/* automatic parse from the BIOS config */
177043737e0aSLydia Wang 	err = via_parse_auto_config(codec);
177143737e0aSLydia Wang 	if (err < 0) {
177243737e0aSLydia Wang 		via_free(codec);
177343737e0aSLydia Wang 		return err;
177443737e0aSLydia Wang 	}
177543737e0aSLydia Wang 
177643737e0aSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs;
177743737e0aSLydia Wang 
177843737e0aSLydia Wang 	codec->patch_ops = via_patch_ops;
177943737e0aSLydia Wang 
178043737e0aSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt3476;
178143737e0aSLydia Wang 
178243737e0aSLydia Wang 	return 0;
178343737e0aSLydia Wang }
178443737e0aSLydia Wang 
1785c577b8a1SJoseph Chan /*
1786c577b8a1SJoseph Chan  * patch entries
1787c577b8a1SJoseph Chan  */
178890dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = {
17893218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
17903218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
17913218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
17923218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
17933218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
1794ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17953218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
1796ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17973218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
1798ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
17993218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
1800ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18013218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
1802ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18033218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
1804ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18053218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
1806ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18073218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
1808ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18093218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
1810ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18113218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
1812ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18133218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
1814ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18153218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
1816ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18173218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
1818ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18193218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
1820ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18213218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
1822ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18233218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
1824ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18253218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
1826d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18273218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
1828d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18293218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
1830d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18313218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
1832d949cac1SHarald Welte 	  .patch = patch_vt1708S},
1833bc92df7fSLydia Wang 	{ .id = 0x11064397, .name = "VT1705",
1834d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18353218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
1836d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18373218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
1838d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18393218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
1840d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18413218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
1842d949cac1SHarald Welte 	  .patch = patch_vt1702},
18433218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
1844d949cac1SHarald Welte 	  .patch = patch_vt1702},
18453218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
1846d949cac1SHarald Welte 	  .patch = patch_vt1702},
18473218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
1848d949cac1SHarald Welte 	  .patch = patch_vt1702},
18493218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
1850d949cac1SHarald Welte 	  .patch = patch_vt1702},
18513218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
1852d949cac1SHarald Welte 	  .patch = patch_vt1702},
18533218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
1854d949cac1SHarald Welte 	  .patch = patch_vt1702},
18553218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
1856d949cac1SHarald Welte 	  .patch = patch_vt1702},
1857eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
1858eb7188caSLydia Wang 	  .patch = patch_vt1718S},
1859eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
1860eb7188caSLydia Wang 	  .patch = patch_vt1718S},
1861bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
1862bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
1863bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
1864bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
1865f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
1866f3db423dSLydia Wang 	  .patch = patch_vt1716S},
1867f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
1868f3db423dSLydia Wang 	  .patch = patch_vt1716S},
186925eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
187025eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
1871ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
187236dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
187336dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
187411890956SLydia Wang 	{ .id = 0x11060446, .name = "VT1802",
187511890956SLydia Wang 		.patch = patch_vt2002P},
187611890956SLydia Wang 	{ .id = 0x11068446, .name = "VT1802",
187711890956SLydia Wang 		.patch = patch_vt2002P},
187843737e0aSLydia Wang 	{ .id = 0x11064760, .name = "VT1705CF",
187943737e0aSLydia Wang 		.patch = patch_vt3476},
18806121b84aSLydia Wang 	{ .id = 0x11064761, .name = "VT1708SCE",
18816121b84aSLydia Wang 		.patch = patch_vt3476},
18826121b84aSLydia Wang 	{ .id = 0x11064762, .name = "VT1808",
18836121b84aSLydia Wang 		.patch = patch_vt3476},
1884c577b8a1SJoseph Chan 	{} /* terminator */
1885c577b8a1SJoseph Chan };
18861289e9e8STakashi Iwai 
18871289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
18881289e9e8STakashi Iwai 
1889d8a766a1STakashi Iwai static struct hda_codec_driver via_driver = {
18901289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
18911289e9e8STakashi Iwai };
18921289e9e8STakashi Iwai 
18931289e9e8STakashi Iwai MODULE_LICENSE("GPL");
18941289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
18951289e9e8STakashi Iwai 
1896d8a766a1STakashi Iwai module_hda_codec_driver(via_driver);
1897