xref: /openbmc/linux/sound/pci/hda/patch_via.c (revision a8dca4604a13469892c77c25638691d7757e7a47)
1c577b8a1SJoseph Chan /*
2c577b8a1SJoseph Chan  * Universal Interface for Intel High Definition Audio Codec
3c577b8a1SJoseph Chan  *
48e86597fSLydia Wang  * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
5c577b8a1SJoseph Chan  *
68e86597fSLydia Wang  *  (C) 2006-2009 VIA Technology, Inc.
78e86597fSLydia Wang  *  (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
8c577b8a1SJoseph Chan  *
9c577b8a1SJoseph Chan  *  This driver is free software; you can redistribute it and/or modify
10c577b8a1SJoseph Chan  *  it under the terms of the GNU General Public License as published by
11c577b8a1SJoseph Chan  *  the Free Software Foundation; either version 2 of the License, or
12c577b8a1SJoseph Chan  *  (at your option) any later version.
13c577b8a1SJoseph Chan  *
14c577b8a1SJoseph Chan  *  This driver is distributed in the hope that it will be useful,
15c577b8a1SJoseph Chan  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16c577b8a1SJoseph Chan  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17c577b8a1SJoseph Chan  *  GNU General Public License for more details.
18c577b8a1SJoseph Chan  *
19c577b8a1SJoseph Chan  *  You should have received a copy of the GNU General Public License
20c577b8a1SJoseph Chan  *  along with this program; if not, write to the Free Software
21c577b8a1SJoseph Chan  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22c577b8a1SJoseph Chan  */
23c577b8a1SJoseph Chan 
24c577b8a1SJoseph Chan /* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
25c577b8a1SJoseph Chan /*									     */
26c577b8a1SJoseph Chan /* 2006-03-03  Lydia Wang  Create the basic patch to support VT1708 codec    */
27c577b8a1SJoseph Chan /* 2006-03-14  Lydia Wang  Modify hard code for some pin widget nid	     */
28c577b8a1SJoseph Chan /* 2006-08-02  Lydia Wang  Add support to VT1709 codec			     */
29c577b8a1SJoseph Chan /* 2006-09-08  Lydia Wang  Fix internal loopback recording source select bug */
30f7278fd0SJosepch Chan /* 2007-09-12  Lydia Wang  Add EAPD enable during driver initialization	     */
31f7278fd0SJosepch Chan /* 2007-09-17  Lydia Wang  Add VT1708B codec support			    */
3276d9b0ddSHarald Welte /* 2007-11-14  Lydia Wang  Add VT1708A codec HP and CD pin connect config    */
33fb4cb772SHarald Welte /* 2008-02-03  Lydia Wang  Fix Rear channels and Back channels inverse issue */
34d949cac1SHarald Welte /* 2008-03-06  Lydia Wang  Add VT1702 codec and VT1708S codec support	     */
3569e52a80SHarald Welte /* 2008-04-09  Lydia Wang  Add mute front speaker when HP plugin	     */
360aa62aefSHarald Welte /* 2008-04-09  Lydia Wang  Add Independent HP feature			     */
3798aa34c0SHarald Welte /* 2008-05-28  Lydia Wang  Add second S/PDIF Out support for VT1702	     */
38d7426329SHarald Welte /* 2008-09-15  Logan Li	   Add VT1708S Mic Boost workaround/backdoor	     */
398e86597fSLydia Wang /* 2009-02-16  Logan Li	   Add support for VT1718S			     */
408e86597fSLydia Wang /* 2009-03-13  Logan Li	   Add support for VT1716S			     */
418e86597fSLydia Wang /* 2009-04-14  Lydai Wang  Add support for VT1828S and VT2020		     */
428e86597fSLydia Wang /* 2009-07-08  Lydia Wang  Add support for VT2002P			     */
438e86597fSLydia Wang /* 2009-07-21  Lydia Wang  Add support for VT1812			     */
4436dd5c4aSLydia Wang /* 2009-09-19  Lydia Wang  Add support for VT1818S			     */
45c577b8a1SJoseph Chan /*									     */
46c577b8a1SJoseph Chan /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47c577b8a1SJoseph Chan 
48c577b8a1SJoseph Chan 
49c577b8a1SJoseph Chan #include <linux/init.h>
50c577b8a1SJoseph Chan #include <linux/delay.h>
51c577b8a1SJoseph Chan #include <linux/slab.h>
52da155d5bSPaul Gortmaker #include <linux/module.h>
53c577b8a1SJoseph Chan #include <sound/core.h>
540aa62aefSHarald Welte #include <sound/asoundef.h>
55c577b8a1SJoseph Chan #include "hda_codec.h"
56c577b8a1SJoseph Chan #include "hda_local.h"
57128bc4baSTakashi Iwai #include "hda_auto_parser.h"
581835a0f9STakashi Iwai #include "hda_jack.h"
59b3f6008fSTakashi Iwai #include "hda_generic.h"
60c577b8a1SJoseph Chan 
61c577b8a1SJoseph Chan /* Pin Widget NID */
6276d9b0ddSHarald Welte #define VT1708_HP_PIN_NID	0x20
6376d9b0ddSHarald Welte #define VT1708_CD_PIN_NID	0x24
64c577b8a1SJoseph Chan 
65d7426329SHarald Welte enum VIA_HDA_CODEC {
66d7426329SHarald Welte 	UNKNOWN = -1,
67d7426329SHarald Welte 	VT1708,
68d7426329SHarald Welte 	VT1709_10CH,
69d7426329SHarald Welte 	VT1709_6CH,
70d7426329SHarald Welte 	VT1708B_8CH,
71d7426329SHarald Welte 	VT1708B_4CH,
72d7426329SHarald Welte 	VT1708S,
73518bf3baSLydia Wang 	VT1708BCE,
74d7426329SHarald Welte 	VT1702,
75eb7188caSLydia Wang 	VT1718S,
76f3db423dSLydia Wang 	VT1716S,
7725eaba2fSLydia Wang 	VT2002P,
78ab6734e7SLydia Wang 	VT1812,
7911890956SLydia Wang 	VT1802,
8043737e0aSLydia Wang 	VT1705CF,
816121b84aSLydia Wang 	VT1808,
82d7426329SHarald Welte 	CODEC_TYPES,
83d7426329SHarald Welte };
84d7426329SHarald Welte 
8511890956SLydia Wang #define VT2002P_COMPATIBLE(spec) \
8611890956SLydia Wang 	((spec)->codec_type == VT2002P ||\
8711890956SLydia Wang 	 (spec)->codec_type == VT1812 ||\
8811890956SLydia Wang 	 (spec)->codec_type == VT1802)
8911890956SLydia Wang 
901f2e99feSLydia Wang struct via_spec {
91b3f6008fSTakashi Iwai 	struct hda_gen_spec gen;
92b3f6008fSTakashi Iwai 
931f2e99feSLydia Wang 	/* codec parameterization */
9490dd48a1STakashi Iwai 	const struct snd_kcontrol_new *mixers[6];
951f2e99feSLydia Wang 	unsigned int num_mixers;
961f2e99feSLydia Wang 
9790dd48a1STakashi Iwai 	const struct hda_verb *init_verbs[5];
981f2e99feSLydia Wang 	unsigned int num_iverbs;
991f2e99feSLydia Wang 
1001f2e99feSLydia Wang 	/* HP mode source */
101f3db423dSLydia Wang 	unsigned int dmic_enabled;
10224088a58STakashi Iwai 	unsigned int no_pin_power_ctl;
1031f2e99feSLydia Wang 	enum VIA_HDA_CODEC codec_type;
1041f2e99feSLydia Wang 
105e9d010c2STakashi Iwai 	/* analog low-power control */
106e9d010c2STakashi Iwai 	bool alc_mode;
107e9d010c2STakashi Iwai 
1081f2e99feSLydia Wang 	/* work to check hp jack state */
109187d333eSTakashi Iwai 	int hp_work_active;
110e06e5a29STakashi Iwai 	int vt1708_jack_detect;
1113e95b9abSLydia Wang 
1123e95b9abSLydia Wang 	void (*set_widgets_power_state)(struct hda_codec *codec);
11343737e0aSLydia Wang 	unsigned int dac_stream_tag[4];
1141f2e99feSLydia Wang };
1151f2e99feSLydia Wang 
1160341ccd7SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
117b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
118b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
119b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
120b3f6008fSTakashi Iwai 				  int action);
121b3f6008fSTakashi Iwai static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl);
122b3f6008fSTakashi Iwai 
1235b0cb1d8SJaroslav Kysela static struct via_spec *via_new_spec(struct hda_codec *codec)
1245b0cb1d8SJaroslav Kysela {
1255b0cb1d8SJaroslav Kysela 	struct via_spec *spec;
1265b0cb1d8SJaroslav Kysela 
1275b0cb1d8SJaroslav Kysela 	spec = kzalloc(sizeof(*spec), GFP_KERNEL);
1285b0cb1d8SJaroslav Kysela 	if (spec == NULL)
1295b0cb1d8SJaroslav Kysela 		return NULL;
1305b0cb1d8SJaroslav Kysela 
1315b0cb1d8SJaroslav Kysela 	codec->spec = spec;
132b3f6008fSTakashi Iwai 	snd_hda_gen_spec_init(&spec->gen);
1330341ccd7SLydia Wang 	spec->codec_type = get_codec_type(codec);
1340341ccd7SLydia Wang 	/* VT1708BCE & VT1708S are almost same */
1350341ccd7SLydia Wang 	if (spec->codec_type == VT1708BCE)
1360341ccd7SLydia Wang 		spec->codec_type = VT1708S;
137b3f6008fSTakashi Iwai 	spec->no_pin_power_ctl = 1;
13813961170STakashi Iwai 	spec->gen.indep_hp = 1;
13905909d5cSTakashi Iwai 	spec->gen.keep_eapd_on = 1;
140b3f6008fSTakashi Iwai 	spec->gen.pcm_playback_hook = via_playback_pcm_hook;
141f1e762ddSTakashi Iwai 	spec->gen.add_stereo_mix_input = 1;
1425b0cb1d8SJaroslav Kysela 	return spec;
1435b0cb1d8SJaroslav Kysela }
1445b0cb1d8SJaroslav Kysela 
145744ff5f4SLydia Wang static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
146d7426329SHarald Welte {
147744ff5f4SLydia Wang 	u32 vendor_id = codec->vendor_id;
148d7426329SHarald Welte 	u16 ven_id = vendor_id >> 16;
149d7426329SHarald Welte 	u16 dev_id = vendor_id & 0xffff;
150d7426329SHarald Welte 	enum VIA_HDA_CODEC codec_type;
151d7426329SHarald Welte 
152d7426329SHarald Welte 	/* get codec type */
153d7426329SHarald Welte 	if (ven_id != 0x1106)
154d7426329SHarald Welte 		codec_type = UNKNOWN;
155d7426329SHarald Welte 	else if (dev_id >= 0x1708 && dev_id <= 0x170b)
156d7426329SHarald Welte 		codec_type = VT1708;
157d7426329SHarald Welte 	else if (dev_id >= 0xe710 && dev_id <= 0xe713)
158d7426329SHarald Welte 		codec_type = VT1709_10CH;
159d7426329SHarald Welte 	else if (dev_id >= 0xe714 && dev_id <= 0xe717)
160d7426329SHarald Welte 		codec_type = VT1709_6CH;
161518bf3baSLydia Wang 	else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
162d7426329SHarald Welte 		codec_type = VT1708B_8CH;
163518bf3baSLydia Wang 		if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
164518bf3baSLydia Wang 			codec_type = VT1708BCE;
165518bf3baSLydia Wang 	} else if (dev_id >= 0xe724 && dev_id <= 0xe727)
166d7426329SHarald Welte 		codec_type = VT1708B_4CH;
167d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x397
168d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
169d7426329SHarald Welte 		codec_type = VT1708S;
170d7426329SHarald Welte 	else if ((dev_id & 0xfff) == 0x398
171d7426329SHarald Welte 		 && (dev_id >> 12) < 8)
172d7426329SHarald Welte 		codec_type = VT1702;
173eb7188caSLydia Wang 	else if ((dev_id & 0xfff) == 0x428
174eb7188caSLydia Wang 		 && (dev_id >> 12) < 8)
175eb7188caSLydia Wang 		codec_type = VT1718S;
176f3db423dSLydia Wang 	else if (dev_id == 0x0433 || dev_id == 0xa721)
177f3db423dSLydia Wang 		codec_type = VT1716S;
178bb3c6bfcSLydia Wang 	else if (dev_id == 0x0441 || dev_id == 0x4441)
179bb3c6bfcSLydia Wang 		codec_type = VT1718S;
18025eaba2fSLydia Wang 	else if (dev_id == 0x0438 || dev_id == 0x4438)
18125eaba2fSLydia Wang 		codec_type = VT2002P;
182ab6734e7SLydia Wang 	else if (dev_id == 0x0448)
183ab6734e7SLydia Wang 		codec_type = VT1812;
18436dd5c4aSLydia Wang 	else if (dev_id == 0x0440)
18536dd5c4aSLydia Wang 		codec_type = VT1708S;
18611890956SLydia Wang 	else if ((dev_id & 0xfff) == 0x446)
18711890956SLydia Wang 		codec_type = VT1802;
18843737e0aSLydia Wang 	else if (dev_id == 0x4760)
18943737e0aSLydia Wang 		codec_type = VT1705CF;
1906121b84aSLydia Wang 	else if (dev_id == 0x4761 || dev_id == 0x4762)
1916121b84aSLydia Wang 		codec_type = VT1808;
192d7426329SHarald Welte 	else
193d7426329SHarald Welte 		codec_type = UNKNOWN;
194d7426329SHarald Welte 	return codec_type;
195d7426329SHarald Welte };
196d7426329SHarald Welte 
197ada509ecSTakashi Iwai static void analog_low_current_mode(struct hda_codec *codec);
198ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec);
1991f2e99feSLydia Wang 
200187d333eSTakashi Iwai #define hp_detect_with_aa(codec) \
201187d333eSTakashi Iwai 	(snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
202187d333eSTakashi Iwai 	 !is_aa_path_mute(codec))
2031f2e99feSLydia Wang 
204b3f6008fSTakashi Iwai static void vt1708_stop_hp_work(struct hda_codec *codec)
2051f2e99feSLydia Wang {
206b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
207b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
2081f2e99feSLydia Wang 		return;
209187d333eSTakashi Iwai 	if (spec->hp_work_active) {
210b3f6008fSTakashi Iwai 		snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
2117eaa9161SWang Xingchao 		codec->jackpoll_interval = 0;
212b3f6008fSTakashi Iwai 		cancel_delayed_work_sync(&codec->jackpoll_work);
213b3f6008fSTakashi Iwai 		spec->hp_work_active = false;
214187d333eSTakashi Iwai 	}
215187d333eSTakashi Iwai }
216187d333eSTakashi Iwai 
217b3f6008fSTakashi Iwai static void vt1708_update_hp_work(struct hda_codec *codec)
218187d333eSTakashi Iwai {
219b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
220b3f6008fSTakashi Iwai 	if (spec->codec_type != VT1708 || !spec->gen.autocfg.hp_outs)
221187d333eSTakashi Iwai 		return;
22205dc0fc9SDavid Henningsson 	if (spec->vt1708_jack_detect) {
223187d333eSTakashi Iwai 		if (!spec->hp_work_active) {
224b3f6008fSTakashi Iwai 			codec->jackpoll_interval = msecs_to_jiffies(100);
225b3f6008fSTakashi Iwai 			snd_hda_codec_write(codec, 0x1, 0, 0xf81, 0);
226b3f6008fSTakashi Iwai 			queue_delayed_work(codec->bus->workq,
227b3f6008fSTakashi Iwai 					   &codec->jackpoll_work, 0);
228b3f6008fSTakashi Iwai 			spec->hp_work_active = true;
229187d333eSTakashi Iwai 		}
230b3f6008fSTakashi Iwai 	} else if (!hp_detect_with_aa(codec))
231b3f6008fSTakashi Iwai 		vt1708_stop_hp_work(codec);
2321f2e99feSLydia Wang }
233f5271101SLydia Wang 
2343e95b9abSLydia Wang static void set_widgets_power_state(struct hda_codec *codec)
2353e95b9abSLydia Wang {
236087c2e3bSTakashi Iwai #if 0 /* FIXME: the assumed connections don't match always with the
237087c2e3bSTakashi Iwai        * actual routes by the generic parser, so better to disable
238087c2e3bSTakashi Iwai        * the control for safety.
239087c2e3bSTakashi Iwai        */
2403e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
2413e95b9abSLydia Wang 	if (spec->set_widgets_power_state)
2423e95b9abSLydia Wang 		spec->set_widgets_power_state(codec);
243087c2e3bSTakashi Iwai #endif
2443e95b9abSLydia Wang }
24525eaba2fSLydia Wang 
246054d867eSTakashi Iwai static void update_power_state(struct hda_codec *codec, hda_nid_t nid,
247054d867eSTakashi Iwai 			       unsigned int parm)
248054d867eSTakashi Iwai {
2499040d102STakashi Iwai 	if (snd_hda_check_power_state(codec, nid, parm))
250054d867eSTakashi Iwai 		return;
251054d867eSTakashi Iwai 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
252054d867eSTakashi Iwai }
253054d867eSTakashi Iwai 
25443737e0aSLydia Wang static void update_conv_power_state(struct hda_codec *codec, hda_nid_t nid,
25543737e0aSLydia Wang 			       unsigned int parm, unsigned int index)
25643737e0aSLydia Wang {
25743737e0aSLydia Wang 	struct via_spec *spec = codec->spec;
25843737e0aSLydia Wang 	unsigned int format;
2599040d102STakashi Iwai 
2609040d102STakashi Iwai 	if (snd_hda_check_power_state(codec, nid, parm))
26143737e0aSLydia Wang 		return;
26243737e0aSLydia Wang 	format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
26343737e0aSLydia Wang 	if (format && (spec->dac_stream_tag[index] != format))
26443737e0aSLydia Wang 		spec->dac_stream_tag[index] = format;
26543737e0aSLydia Wang 
26643737e0aSLydia Wang 	snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
26743737e0aSLydia Wang 	if (parm == AC_PWRST_D0) {
26843737e0aSLydia Wang 		format = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
26943737e0aSLydia Wang 		if (!format && (spec->dac_stream_tag[index] != format))
27043737e0aSLydia Wang 			snd_hda_codec_write(codec, nid, 0,
27143737e0aSLydia Wang 						  AC_VERB_SET_CHANNEL_STREAMID,
27243737e0aSLydia Wang 						  spec->dac_stream_tag[index]);
27343737e0aSLydia Wang 	}
27443737e0aSLydia Wang }
27543737e0aSLydia Wang 
276b3f6008fSTakashi Iwai static bool smart51_enabled(struct hda_codec *codec)
277b3f6008fSTakashi Iwai {
278b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
279b3f6008fSTakashi Iwai 	return spec->gen.ext_channel_count > 2;
280b3f6008fSTakashi Iwai }
281b3f6008fSTakashi Iwai 
282b3f6008fSTakashi Iwai static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
283b3f6008fSTakashi Iwai {
284b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
285b3f6008fSTakashi Iwai 	int i;
286b3f6008fSTakashi Iwai 
287b3f6008fSTakashi Iwai 	for (i = 0; i < spec->gen.multi_ios; i++)
288b3f6008fSTakashi Iwai 		if (spec->gen.multi_io[i].pin == pin)
289b3f6008fSTakashi Iwai 			return true;
290b3f6008fSTakashi Iwai 	return false;
291b3f6008fSTakashi Iwai }
292b3f6008fSTakashi Iwai 
293f5271101SLydia Wang static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
294f5271101SLydia Wang 				unsigned int *affected_parm)
295f5271101SLydia Wang {
296f5271101SLydia Wang 	unsigned parm;
297f5271101SLydia Wang 	unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
298f5271101SLydia Wang 	unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
299f5271101SLydia Wang 		>> AC_DEFCFG_MISC_SHIFT
300f5271101SLydia Wang 		& AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
3011564b287SLydia Wang 	struct via_spec *spec = codec->spec;
30224088a58STakashi Iwai 	unsigned present = 0;
30324088a58STakashi Iwai 
30424088a58STakashi Iwai 	no_presence |= spec->no_pin_power_ctl;
30524088a58STakashi Iwai 	if (!no_presence)
30624088a58STakashi Iwai 		present = snd_hda_jack_detect(codec, nid);
307b3f6008fSTakashi Iwai 	if ((smart51_enabled(codec) && is_smart51_pins(codec, nid))
3081564b287SLydia Wang 	    || ((no_presence || present)
3091564b287SLydia Wang 		&& get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
310f5271101SLydia Wang 		*affected_parm = AC_PWRST_D0; /* if it's connected */
311f5271101SLydia Wang 		parm = AC_PWRST_D0;
312f5271101SLydia Wang 	} else
313f5271101SLydia Wang 		parm = AC_PWRST_D3;
314f5271101SLydia Wang 
315054d867eSTakashi Iwai 	update_power_state(codec, nid, parm);
316f5271101SLydia Wang }
317f5271101SLydia Wang 
31824088a58STakashi Iwai static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
31924088a58STakashi Iwai 				  struct snd_ctl_elem_info *uinfo)
32024088a58STakashi Iwai {
321dda415d4STakashi Iwai 	return snd_hda_enum_bool_helper_info(kcontrol, uinfo);
32224088a58STakashi Iwai }
32324088a58STakashi Iwai 
32424088a58STakashi Iwai static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
32524088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
32624088a58STakashi Iwai {
32724088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
32824088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
32924088a58STakashi Iwai 	ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
33024088a58STakashi Iwai 	return 0;
33124088a58STakashi Iwai }
33224088a58STakashi Iwai 
33324088a58STakashi Iwai static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
33424088a58STakashi Iwai 				 struct snd_ctl_elem_value *ucontrol)
33524088a58STakashi Iwai {
33624088a58STakashi Iwai 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
33724088a58STakashi Iwai 	struct via_spec *spec = codec->spec;
33824088a58STakashi Iwai 	unsigned int val = !ucontrol->value.enumerated.item[0];
33924088a58STakashi Iwai 
34024088a58STakashi Iwai 	if (val == spec->no_pin_power_ctl)
34124088a58STakashi Iwai 		return 0;
34224088a58STakashi Iwai 	spec->no_pin_power_ctl = val;
34324088a58STakashi Iwai 	set_widgets_power_state(codec);
344e9d010c2STakashi Iwai 	analog_low_current_mode(codec);
34524088a58STakashi Iwai 	return 1;
34624088a58STakashi Iwai }
34724088a58STakashi Iwai 
348b3f6008fSTakashi Iwai static const struct snd_kcontrol_new via_pin_power_ctl_enum[] = {
349b3f6008fSTakashi Iwai 	{
35024088a58STakashi Iwai 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
35124088a58STakashi Iwai 	.name = "Dynamic Power-Control",
35224088a58STakashi Iwai 	.info = via_pin_power_ctl_info,
35324088a58STakashi Iwai 	.get = via_pin_power_ctl_get,
35424088a58STakashi Iwai 	.put = via_pin_power_ctl_put,
355b3f6008fSTakashi Iwai 	},
356b3f6008fSTakashi Iwai 	{} /* terminator */
35724088a58STakashi Iwai };
35824088a58STakashi Iwai 
35924088a58STakashi Iwai 
360f5271101SLydia Wang /* check AA path's mute status */
361ada509ecSTakashi Iwai static bool is_aa_path_mute(struct hda_codec *codec)
362ada509ecSTakashi Iwai {
363ada509ecSTakashi Iwai 	struct via_spec *spec = codec->spec;
364ada509ecSTakashi Iwai 	const struct hda_amp_list *p;
3650186f4f4STakashi Iwai 	int ch, v;
366ada509ecSTakashi Iwai 
3670186f4f4STakashi Iwai 	p = spec->gen.loopback.amplist;
3680186f4f4STakashi Iwai 	if (!p)
3690186f4f4STakashi Iwai 		return true;
3700186f4f4STakashi Iwai 	for (; p->nid; p++) {
371ada509ecSTakashi Iwai 		for (ch = 0; ch < 2; ch++) {
372ada509ecSTakashi Iwai 			v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
373ada509ecSTakashi Iwai 						   p->idx);
374ada509ecSTakashi Iwai 			if (!(v & HDA_AMP_MUTE) && v > 0)
375ada509ecSTakashi Iwai 				return false;
376f5271101SLydia Wang 		}
377f5271101SLydia Wang 	}
378ada509ecSTakashi Iwai 	return true;
379f5271101SLydia Wang }
380f5271101SLydia Wang 
381f5271101SLydia Wang /* enter/exit analog low-current mode */
382e9d010c2STakashi Iwai static void __analog_low_current_mode(struct hda_codec *codec, bool force)
383f5271101SLydia Wang {
384f5271101SLydia Wang 	struct via_spec *spec = codec->spec;
385ada509ecSTakashi Iwai 	bool enable;
386ada509ecSTakashi Iwai 	unsigned int verb, parm;
387f5271101SLydia Wang 
388e9d010c2STakashi Iwai 	if (spec->no_pin_power_ctl)
389e9d010c2STakashi Iwai 		enable = false;
390e9d010c2STakashi Iwai 	else
391b3f6008fSTakashi Iwai 		enable = is_aa_path_mute(codec) && !spec->gen.active_streams;
392e9d010c2STakashi Iwai 	if (enable == spec->alc_mode && !force)
393e9d010c2STakashi Iwai 		return;
394e9d010c2STakashi Iwai 	spec->alc_mode = enable;
395f5271101SLydia Wang 
396f5271101SLydia Wang 	/* decide low current mode's verb & parameter */
397f5271101SLydia Wang 	switch (spec->codec_type) {
398f5271101SLydia Wang 	case VT1708B_8CH:
399f5271101SLydia Wang 	case VT1708B_4CH:
400f5271101SLydia Wang 		verb = 0xf70;
401f5271101SLydia Wang 		parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
402f5271101SLydia Wang 		break;
403f5271101SLydia Wang 	case VT1708S:
404eb7188caSLydia Wang 	case VT1718S:
405f3db423dSLydia Wang 	case VT1716S:
406f5271101SLydia Wang 		verb = 0xf73;
407f5271101SLydia Wang 		parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
408f5271101SLydia Wang 		break;
409f5271101SLydia Wang 	case VT1702:
410f5271101SLydia Wang 		verb = 0xf73;
411f5271101SLydia Wang 		parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
412f5271101SLydia Wang 		break;
41325eaba2fSLydia Wang 	case VT2002P:
414ab6734e7SLydia Wang 	case VT1812:
41511890956SLydia Wang 	case VT1802:
41625eaba2fSLydia Wang 		verb = 0xf93;
41725eaba2fSLydia Wang 		parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
41825eaba2fSLydia Wang 		break;
41943737e0aSLydia Wang 	case VT1705CF:
4206121b84aSLydia Wang 	case VT1808:
42143737e0aSLydia Wang 		verb = 0xf82;
42243737e0aSLydia Wang 		parm = enable ? 0x00 : 0xe0;  /* 0x00: 4/40x, 0xe0: 1x */
42343737e0aSLydia Wang 		break;
424f5271101SLydia Wang 	default:
425f5271101SLydia Wang 		return;		/* other codecs are not supported */
426f5271101SLydia Wang 	}
427f5271101SLydia Wang 	/* send verb */
428f5271101SLydia Wang 	snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
429f5271101SLydia Wang }
430f5271101SLydia Wang 
431e9d010c2STakashi Iwai static void analog_low_current_mode(struct hda_codec *codec)
432e9d010c2STakashi Iwai {
433e9d010c2STakashi Iwai 	return __analog_low_current_mode(codec, false);
434e9d010c2STakashi Iwai }
435e9d010c2STakashi Iwai 
436c577b8a1SJoseph Chan static int via_build_controls(struct hda_codec *codec)
437c577b8a1SJoseph Chan {
438c577b8a1SJoseph Chan 	struct via_spec *spec = codec->spec;
4395b0cb1d8SJaroslav Kysela 	int err, i;
440c577b8a1SJoseph Chan 
441b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_controls(codec);
442b3f6008fSTakashi Iwai 	if (err < 0)
443b3f6008fSTakashi Iwai 		return err;
444b3f6008fSTakashi Iwai 
44524088a58STakashi Iwai 	if (spec->set_widgets_power_state)
446b3f6008fSTakashi Iwai 		spec->mixers[spec->num_mixers++] = via_pin_power_ctl_enum;
44724088a58STakashi Iwai 
448c577b8a1SJoseph Chan 	for (i = 0; i < spec->num_mixers; i++) {
449c577b8a1SJoseph Chan 		err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
450c577b8a1SJoseph Chan 		if (err < 0)
451c577b8a1SJoseph Chan 			return err;
452c577b8a1SJoseph Chan 	}
453c577b8a1SJoseph Chan 
454c577b8a1SJoseph Chan 	return 0;
455c577b8a1SJoseph Chan }
456c577b8a1SJoseph Chan 
457b3f6008fSTakashi Iwai static void via_playback_pcm_hook(struct hda_pcm_stream *hinfo,
458b3f6008fSTakashi Iwai 				  struct hda_codec *codec,
459b3f6008fSTakashi Iwai 				  struct snd_pcm_substream *substream,
460b3f6008fSTakashi Iwai 				  int action)
461c577b8a1SJoseph Chan {
462b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
463b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
464c577b8a1SJoseph Chan }
465c577b8a1SJoseph Chan 
466c577b8a1SJoseph Chan static void via_free(struct hda_codec *codec)
467c577b8a1SJoseph Chan {
468b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
469*a8dca460STakashi Iwai 	snd_hda_gen_free(codec);
470c577b8a1SJoseph Chan }
471c577b8a1SJoseph Chan 
4722a43952aSTakashi Iwai #ifdef CONFIG_PM
47368cb2b55STakashi Iwai static int via_suspend(struct hda_codec *codec)
4741f2e99feSLydia Wang {
4751f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
476b3f6008fSTakashi Iwai 	vt1708_stop_hp_work(codec);
47794c142a1SDavid Henningsson 
47894c142a1SDavid Henningsson 	/* Fix pop noise on headphones */
4792c38d990STakashi Iwai 	if (spec->codec_type == VT1802)
4802c38d990STakashi Iwai 		snd_hda_shutup_pins(codec);
48194c142a1SDavid Henningsson 
4821f2e99feSLydia Wang 	return 0;
4831f2e99feSLydia Wang }
4841f2e99feSLydia Wang #endif
4851f2e99feSLydia Wang 
48683012a7cSTakashi Iwai #ifdef CONFIG_PM
487cb53c626STakashi Iwai static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
488cb53c626STakashi Iwai {
489cb53c626STakashi Iwai 	struct via_spec *spec = codec->spec;
490b3f6008fSTakashi Iwai 	set_widgets_power_state(codec);
491b3f6008fSTakashi Iwai 	analog_low_current_mode(codec);
492b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
493b3f6008fSTakashi Iwai 	return snd_hda_check_amp_list_power(codec, &spec->gen.loopback, nid);
494cb53c626STakashi Iwai }
495cb53c626STakashi Iwai #endif
496cb53c626STakashi Iwai 
497c577b8a1SJoseph Chan /*
498c577b8a1SJoseph Chan  */
4995d41762aSTakashi Iwai 
5005d41762aSTakashi Iwai static int via_init(struct hda_codec *codec);
5015d41762aSTakashi Iwai 
50290dd48a1STakashi Iwai static const struct hda_codec_ops via_patch_ops = {
503c577b8a1SJoseph Chan 	.build_controls = via_build_controls,
504b3f6008fSTakashi Iwai 	.build_pcms = snd_hda_gen_build_pcms,
505c577b8a1SJoseph Chan 	.init = via_init,
506c577b8a1SJoseph Chan 	.free = via_free,
5074e2d16d3SDavid Henningsson 	.unsol_event = snd_hda_jack_unsol_event,
5082a43952aSTakashi Iwai #ifdef CONFIG_PM
5091f2e99feSLydia Wang 	.suspend = via_suspend,
510cb53c626STakashi Iwai 	.check_power_status = via_check_power_status,
511cb53c626STakashi Iwai #endif
512c577b8a1SJoseph Chan };
513c577b8a1SJoseph Chan 
5144a79616dSTakashi Iwai 
515b3f6008fSTakashi Iwai static const struct hda_verb vt1708_init_verbs[] = {
516b3f6008fSTakashi Iwai 	/* power down jack detect function */
517b3f6008fSTakashi Iwai 	{0x1, 0xf81, 0x1},
518b3f6008fSTakashi Iwai 	{ }
5194a79616dSTakashi Iwai };
52076d9b0ddSHarald Welte static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
52176d9b0ddSHarald Welte {
52276d9b0ddSHarald Welte 	unsigned int def_conf;
52376d9b0ddSHarald Welte 	unsigned char seqassoc;
52476d9b0ddSHarald Welte 
5252f334f92STakashi Iwai 	def_conf = snd_hda_codec_get_pincfg(codec, nid);
52676d9b0ddSHarald Welte 	seqassoc = (unsigned char) get_defcfg_association(def_conf);
52776d9b0ddSHarald Welte 	seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
52882ef9e45SLydia Wang 	if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
52982ef9e45SLydia Wang 	    && (seqassoc == 0xf0 || seqassoc == 0xff)) {
53076d9b0ddSHarald Welte 		def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
5312f334f92STakashi Iwai 		snd_hda_codec_set_pincfg(codec, nid, def_conf);
53276d9b0ddSHarald Welte 	}
53376d9b0ddSHarald Welte 
53476d9b0ddSHarald Welte 	return;
53576d9b0ddSHarald Welte }
53676d9b0ddSHarald Welte 
537e06e5a29STakashi Iwai static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
5381f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
5391f2e99feSLydia Wang {
5401f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
5411f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
5421f2e99feSLydia Wang 
5431f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
5441f2e99feSLydia Wang 		return 0;
545e06e5a29STakashi Iwai 	ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
5461f2e99feSLydia Wang 	return 0;
5471f2e99feSLydia Wang }
5481f2e99feSLydia Wang 
549e06e5a29STakashi Iwai static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
5501f2e99feSLydia Wang 				     struct snd_ctl_elem_value *ucontrol)
5511f2e99feSLydia Wang {
5521f2e99feSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
5531f2e99feSLydia Wang 	struct via_spec *spec = codec->spec;
554187d333eSTakashi Iwai 	int val;
5551f2e99feSLydia Wang 
5561f2e99feSLydia Wang 	if (spec->codec_type != VT1708)
5571f2e99feSLydia Wang 		return 0;
558187d333eSTakashi Iwai 	val = !!ucontrol->value.integer.value[0];
559187d333eSTakashi Iwai 	if (spec->vt1708_jack_detect == val)
560187d333eSTakashi Iwai 		return 0;
561187d333eSTakashi Iwai 	spec->vt1708_jack_detect = val;
562b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
563187d333eSTakashi Iwai 	return 1;
5641f2e99feSLydia Wang }
5651f2e99feSLydia Wang 
566b3f6008fSTakashi Iwai static const struct snd_kcontrol_new vt1708_jack_detect_ctl[] = {
567b3f6008fSTakashi Iwai 	{
5681f2e99feSLydia Wang 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
5691f2e99feSLydia Wang 	.name = "Jack Detect",
5701f2e99feSLydia Wang 	.count = 1,
5711f2e99feSLydia Wang 	.info = snd_ctl_boolean_mono_info,
572e06e5a29STakashi Iwai 	.get = vt1708_jack_detect_get,
573e06e5a29STakashi Iwai 	.put = vt1708_jack_detect_put,
574b3f6008fSTakashi Iwai 	},
575b3f6008fSTakashi Iwai 	{} /* terminator */
5761f2e99feSLydia Wang };
5771f2e99feSLydia Wang 
578b3f6008fSTakashi Iwai static void via_hp_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl)
5794e2d16d3SDavid Henningsson {
5804e2d16d3SDavid Henningsson 	set_widgets_power_state(codec);
581b3f6008fSTakashi Iwai 	snd_hda_gen_hp_automute(codec, tbl);
582b3f6008fSTakashi Iwai }
583b3f6008fSTakashi Iwai 
584b3f6008fSTakashi Iwai static void via_line_automute(struct hda_codec *codec, struct hda_jack_tbl *tbl)
585b3f6008fSTakashi Iwai {
586b3f6008fSTakashi Iwai 	set_widgets_power_state(codec);
587b3f6008fSTakashi Iwai 	snd_hda_gen_line_automute(codec, tbl);
5884e2d16d3SDavid Henningsson }
5894e2d16d3SDavid Henningsson 
5904e2d16d3SDavid Henningsson static void via_jack_powerstate_event(struct hda_codec *codec, struct hda_jack_tbl *tbl)
5914e2d16d3SDavid Henningsson {
5924e2d16d3SDavid Henningsson 	set_widgets_power_state(codec);
5934e2d16d3SDavid Henningsson }
5944e2d16d3SDavid Henningsson 
595b3f6008fSTakashi Iwai #define VIA_JACK_EVENT	(HDA_GEN_LAST_EVENT + 1)
596b3f6008fSTakashi Iwai 
597b3f6008fSTakashi Iwai static void via_set_jack_unsol_events(struct hda_codec *codec)
5984a918ffeSTakashi Iwai {
5994a918ffeSTakashi Iwai 	struct via_spec *spec = codec->spec;
600b3f6008fSTakashi Iwai 	struct auto_pin_cfg *cfg = &spec->gen.autocfg;
601b3f6008fSTakashi Iwai 	hda_nid_t pin;
6024a918ffeSTakashi Iwai 	int i;
6034a918ffeSTakashi Iwai 
604b3f6008fSTakashi Iwai 	spec->gen.hp_automute_hook = via_hp_automute;
6054a918ffeSTakashi Iwai 	if (cfg->speaker_pins[0])
606b3f6008fSTakashi Iwai 		spec->gen.line_automute_hook = via_line_automute;
6074e2d16d3SDavid Henningsson 
6084a918ffeSTakashi Iwai 	for (i = 0; i < cfg->line_outs; i++) {
609b3f6008fSTakashi Iwai 		pin = cfg->line_out_pins[i];
610b3f6008fSTakashi Iwai 		if (pin && !snd_hda_jack_tbl_get(codec, pin) &&
611b3f6008fSTakashi Iwai 		    is_jack_detectable(codec, pin))
612b3f6008fSTakashi Iwai 			snd_hda_jack_detect_enable_callback(codec, pin,
6134e2d16d3SDavid Henningsson 							    VIA_JACK_EVENT,
6144e2d16d3SDavid Henningsson 							    via_jack_powerstate_event);
6154a918ffeSTakashi Iwai 	}
616b3f6008fSTakashi Iwai 
617b3f6008fSTakashi Iwai 	for (i = 0; i < cfg->num_inputs; i++) {
618b3f6008fSTakashi Iwai 		pin = cfg->line_out_pins[i];
619b3f6008fSTakashi Iwai 		if (pin && !snd_hda_jack_tbl_get(codec, pin) &&
620b3f6008fSTakashi Iwai 		    is_jack_detectable(codec, pin))
621b3f6008fSTakashi Iwai 			snd_hda_jack_detect_enable_callback(codec, pin,
622b3f6008fSTakashi Iwai 							    VIA_JACK_EVENT,
623b3f6008fSTakashi Iwai 							    via_jack_powerstate_event);
624b3f6008fSTakashi Iwai 	}
625b3f6008fSTakashi Iwai }
626b3f6008fSTakashi Iwai 
6274abdbd1cSTakashi Iwai static const struct badness_table via_main_out_badness = {
6284abdbd1cSTakashi Iwai 	.no_primary_dac = 0x10000,
6294abdbd1cSTakashi Iwai 	.no_dac = 0x4000,
6304abdbd1cSTakashi Iwai 	.shared_primary = 0x10000,
6314abdbd1cSTakashi Iwai 	.shared_surr = 0x20,
6324abdbd1cSTakashi Iwai 	.shared_clfe = 0x20,
6334abdbd1cSTakashi Iwai 	.shared_surr_main = 0x20,
6344abdbd1cSTakashi Iwai };
6354abdbd1cSTakashi Iwai static const struct badness_table via_extra_out_badness = {
6364abdbd1cSTakashi Iwai 	.no_primary_dac = 0x4000,
6374abdbd1cSTakashi Iwai 	.no_dac = 0x4000,
6384abdbd1cSTakashi Iwai 	.shared_primary = 0x12,
6394abdbd1cSTakashi Iwai 	.shared_surr = 0x20,
6404abdbd1cSTakashi Iwai 	.shared_clfe = 0x20,
6414abdbd1cSTakashi Iwai 	.shared_surr_main = 0x10,
6424abdbd1cSTakashi Iwai };
6434abdbd1cSTakashi Iwai 
644b3f6008fSTakashi Iwai static int via_parse_auto_config(struct hda_codec *codec)
645b3f6008fSTakashi Iwai {
646b3f6008fSTakashi Iwai 	struct via_spec *spec = codec->spec;
647b3f6008fSTakashi Iwai 	int err;
648b3f6008fSTakashi Iwai 
6494abdbd1cSTakashi Iwai 	spec->gen.main_out_badness = &via_main_out_badness;
6504abdbd1cSTakashi Iwai 	spec->gen.extra_out_badness = &via_extra_out_badness;
6514abdbd1cSTakashi Iwai 
652b3f6008fSTakashi Iwai 	err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
653b3f6008fSTakashi Iwai 	if (err < 0)
654b3f6008fSTakashi Iwai 		return err;
655b3f6008fSTakashi Iwai 
656b3f6008fSTakashi Iwai 	err = snd_hda_gen_parse_auto_config(codec, &spec->gen.autocfg);
657b3f6008fSTakashi Iwai 	if (err < 0)
658b3f6008fSTakashi Iwai 		return err;
659b3f6008fSTakashi Iwai 
660b3f6008fSTakashi Iwai 	via_set_jack_unsol_events(codec);
661b3f6008fSTakashi Iwai 	return 0;
6624a918ffeSTakashi Iwai }
6634a918ffeSTakashi Iwai 
6645d41762aSTakashi Iwai static int via_init(struct hda_codec *codec)
6655d41762aSTakashi Iwai {
6665d41762aSTakashi Iwai 	struct via_spec *spec = codec->spec;
6675d41762aSTakashi Iwai 	int i;
6685d41762aSTakashi Iwai 
6695d41762aSTakashi Iwai 	for (i = 0; i < spec->num_iverbs; i++)
6705d41762aSTakashi Iwai 		snd_hda_sequence_write(codec, spec->init_verbs[i]);
6715d41762aSTakashi Iwai 
672e9d010c2STakashi Iwai 	/* init power states */
673e9d010c2STakashi Iwai 	set_widgets_power_state(codec);
674e9d010c2STakashi Iwai 	__analog_low_current_mode(codec, true);
675e9d010c2STakashi Iwai 
676b3f6008fSTakashi Iwai 	snd_hda_gen_init(codec);
67711890956SLydia Wang 
678b3f6008fSTakashi Iwai 	vt1708_update_hp_work(codec);
67925eaba2fSLydia Wang 
680c577b8a1SJoseph Chan 	return 0;
681c577b8a1SJoseph Chan }
682c577b8a1SJoseph Chan 
683f672f65aSDavid Henningsson static int vt1708_build_controls(struct hda_codec *codec)
684f672f65aSDavid Henningsson {
685f672f65aSDavid Henningsson 	/* In order not to create "Phantom Jack" controls,
686f672f65aSDavid Henningsson 	   temporary enable jackpoll */
687f672f65aSDavid Henningsson 	int err;
688f672f65aSDavid Henningsson 	int old_interval = codec->jackpoll_interval;
689f672f65aSDavid Henningsson 	codec->jackpoll_interval = msecs_to_jiffies(100);
690f672f65aSDavid Henningsson 	err = via_build_controls(codec);
691f672f65aSDavid Henningsson 	codec->jackpoll_interval = old_interval;
692f672f65aSDavid Henningsson 	return err;
693f672f65aSDavid Henningsson }
694f672f65aSDavid Henningsson 
695b3f6008fSTakashi Iwai static int vt1708_build_pcms(struct hda_codec *codec)
696337b9d02STakashi Iwai {
697337b9d02STakashi Iwai 	struct via_spec *spec = codec->spec;
698b3f6008fSTakashi Iwai 	int i, err;
699337b9d02STakashi Iwai 
700b3f6008fSTakashi Iwai 	err = snd_hda_gen_build_pcms(codec);
701b3f6008fSTakashi Iwai 	if (err < 0 || codec->vendor_id != 0x11061708)
702b3f6008fSTakashi Iwai 		return err;
703b3f6008fSTakashi Iwai 
704b3f6008fSTakashi Iwai 	/* We got noisy outputs on the right channel on VT1708 when
705b3f6008fSTakashi Iwai 	 * 24bit samples are used.  Until any workaround is found,
706b3f6008fSTakashi Iwai 	 * disable the 24bit format, so far.
707b3f6008fSTakashi Iwai 	 */
708b3f6008fSTakashi Iwai 	for (i = 0; i < codec->num_pcms; i++) {
709b3f6008fSTakashi Iwai 		struct hda_pcm *info = &spec->gen.pcm_rec[i];
710b3f6008fSTakashi Iwai 		if (!info->stream[SNDRV_PCM_STREAM_PLAYBACK].substreams ||
711b3f6008fSTakashi Iwai 		    info->pcm_type != HDA_PCM_TYPE_AUDIO)
712b3f6008fSTakashi Iwai 			continue;
713b3f6008fSTakashi Iwai 		info->stream[SNDRV_PCM_STREAM_PLAYBACK].formats =
714b3f6008fSTakashi Iwai 			SNDRV_PCM_FMTBIT_S16_LE;
715337b9d02STakashi Iwai 	}
716b3f6008fSTakashi Iwai 
7171c55d521STakashi Iwai 	return 0;
718337b9d02STakashi Iwai }
719337b9d02STakashi Iwai 
720c577b8a1SJoseph Chan static int patch_vt1708(struct hda_codec *codec)
721c577b8a1SJoseph Chan {
722c577b8a1SJoseph Chan 	struct via_spec *spec;
723c577b8a1SJoseph Chan 	int err;
724c577b8a1SJoseph Chan 
725c577b8a1SJoseph Chan 	/* create a codec specific record */
7265b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
727c577b8a1SJoseph Chan 	if (spec == NULL)
728c577b8a1SJoseph Chan 		return -ENOMEM;
729c577b8a1SJoseph Chan 
730b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x17;
731b3f6008fSTakashi Iwai 
732b3f6008fSTakashi Iwai 	/* set jackpoll_interval while parsing the codec */
733b3f6008fSTakashi Iwai 	codec->jackpoll_interval = msecs_to_jiffies(100);
734b3f6008fSTakashi Iwai 	spec->vt1708_jack_detect = 1;
735b3f6008fSTakashi Iwai 
736b3f6008fSTakashi Iwai 	/* don't support the input jack switching due to lack of unsol event */
737b3f6008fSTakashi Iwai 	/* (it may work with polling, though, but it needs testing) */
738b3f6008fSTakashi Iwai 	spec->gen.suppress_auto_mic = 1;
739eb33ccf7STakashi Iwai 	/* Some machines show the broken speaker mute */
740eb33ccf7STakashi Iwai 	spec->gen.auto_mute_via_amp = 1;
741620e2b28STakashi Iwai 
74212daef65STakashi Iwai 	/* Add HP and CD pin config connect bit re-config action */
74312daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
74412daef65STakashi Iwai 	vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
74512daef65STakashi Iwai 
746c577b8a1SJoseph Chan 	/* automatic parse from the BIOS config */
74712daef65STakashi Iwai 	err = via_parse_auto_config(codec);
748c577b8a1SJoseph Chan 	if (err < 0) {
749c577b8a1SJoseph Chan 		via_free(codec);
750c577b8a1SJoseph Chan 		return err;
751c577b8a1SJoseph Chan 	}
752c577b8a1SJoseph Chan 
75312daef65STakashi Iwai 	/* add jack detect on/off control */
754b3f6008fSTakashi Iwai 	spec->mixers[spec->num_mixers++] = vt1708_jack_detect_ctl;
755c577b8a1SJoseph Chan 
756e322a36dSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
757e322a36dSLydia Wang 
758c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
759f672f65aSDavid Henningsson 	codec->patch_ops.build_controls = vt1708_build_controls;
760b3f6008fSTakashi Iwai 	codec->patch_ops.build_pcms = vt1708_build_pcms;
761c577b8a1SJoseph Chan 
762b3f6008fSTakashi Iwai 	/* clear jackpoll_interval again; it's set dynamically */
763b3f6008fSTakashi Iwai 	codec->jackpoll_interval = 0;
764b3f6008fSTakashi Iwai 
765c577b8a1SJoseph Chan 	return 0;
766c577b8a1SJoseph Chan }
767c577b8a1SJoseph Chan 
768ddd304d8STakashi Iwai static int patch_vt1709(struct hda_codec *codec)
769c577b8a1SJoseph Chan {
770c577b8a1SJoseph Chan 	struct via_spec *spec;
771c577b8a1SJoseph Chan 	int err;
772c577b8a1SJoseph Chan 
773c577b8a1SJoseph Chan 	/* create a codec specific record */
7745b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
775c577b8a1SJoseph Chan 	if (spec == NULL)
776c577b8a1SJoseph Chan 		return -ENOMEM;
777c577b8a1SJoseph Chan 
778b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x18;
779620e2b28STakashi Iwai 
78012daef65STakashi Iwai 	err = via_parse_auto_config(codec);
781c577b8a1SJoseph Chan 	if (err < 0) {
782c577b8a1SJoseph Chan 		via_free(codec);
783c577b8a1SJoseph Chan 		return err;
784c577b8a1SJoseph Chan 	}
785c577b8a1SJoseph Chan 
786c577b8a1SJoseph Chan 	codec->patch_ops = via_patch_ops;
787c577b8a1SJoseph Chan 
788f7278fd0SJosepch Chan 	return 0;
789f7278fd0SJosepch Chan }
790f7278fd0SJosepch Chan 
7913e95b9abSLydia Wang static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
7923e95b9abSLydia Wang {
7933e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
7943e95b9abSLydia Wang 	int imux_is_smixer;
7953e95b9abSLydia Wang 	unsigned int parm;
7963e95b9abSLydia Wang 	int is_8ch = 0;
797bc92df7fSLydia Wang 	if ((spec->codec_type != VT1708B_4CH) &&
798bc92df7fSLydia Wang 	    (codec->vendor_id != 0x11064397))
7993e95b9abSLydia Wang 		is_8ch = 1;
8003e95b9abSLydia Wang 
8013e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
8023e95b9abSLydia Wang 	imux_is_smixer =
8033e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
8043e95b9abSLydia Wang 	 == ((spec->codec_type == VT1708S) ? 5 : 0));
8053e95b9abSLydia Wang 	/* inputs */
8063e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
8073e95b9abSLydia Wang 	parm = AC_PWRST_D3;
8083e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
8093e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
8103e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
8113e95b9abSLydia Wang 	if (imux_is_smixer)
8123e95b9abSLydia Wang 		parm = AC_PWRST_D0;
8133e95b9abSLydia Wang 	/* SW0 (17h), AIW 0/1 (13h/14h) */
814054d867eSTakashi Iwai 	update_power_state(codec, 0x17, parm);
815054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
816054d867eSTakashi Iwai 	update_power_state(codec, 0x14, parm);
8173e95b9abSLydia Wang 
8183e95b9abSLydia Wang 	/* outputs */
8193e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
8203e95b9abSLydia Wang 	parm = AC_PWRST_D3;
8213e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
822b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
8233e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
824054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
825054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
8263e95b9abSLydia Wang 
8273e95b9abSLydia Wang 	/* PW6 (22h), SW2 (26h), AOW2 (24h) */
8283e95b9abSLydia Wang 	if (is_8ch) {
8293e95b9abSLydia Wang 		parm = AC_PWRST_D3;
8303e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
831b3f6008fSTakashi Iwai 		if (smart51_enabled(codec))
8323e95b9abSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
833054d867eSTakashi Iwai 		update_power_state(codec, 0x26, parm);
834054d867eSTakashi Iwai 		update_power_state(codec, 0x24, parm);
835bc92df7fSLydia Wang 	} else if (codec->vendor_id == 0x11064397) {
836bc92df7fSLydia Wang 		/* PW7(23h), SW2(27h), AOW2(25h) */
837bc92df7fSLydia Wang 		parm = AC_PWRST_D3;
838bc92df7fSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
839b3f6008fSTakashi Iwai 		if (smart51_enabled(codec))
840bc92df7fSLydia Wang 			set_pin_power_state(codec, 0x1a, &parm);
841054d867eSTakashi Iwai 		update_power_state(codec, 0x27, parm);
842054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
8433e95b9abSLydia Wang 	}
8443e95b9abSLydia Wang 
8453e95b9abSLydia Wang 	/* PW 3/4/7 (1ch/1dh/23h) */
8463e95b9abSLydia Wang 	parm = AC_PWRST_D3;
8473e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
8483e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
8493e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
8503e95b9abSLydia Wang 	if (is_8ch)
8513e95b9abSLydia Wang 		set_pin_power_state(codec, 0x23, &parm);
8523e95b9abSLydia Wang 
8533e95b9abSLydia Wang 	/* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
854054d867eSTakashi Iwai 	update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
855054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
8563e95b9abSLydia Wang 	if (is_8ch) {
857054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
858054d867eSTakashi Iwai 		update_power_state(codec, 0x27, parm);
859b3f6008fSTakashi Iwai 	} else if (codec->vendor_id == 0x11064397 && spec->gen.indep_hp_enabled)
860054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
8613e95b9abSLydia Wang }
8623e95b9abSLydia Wang 
863518bf3baSLydia Wang static int patch_vt1708S(struct hda_codec *codec);
864ddd304d8STakashi Iwai static int patch_vt1708B(struct hda_codec *codec)
865f7278fd0SJosepch Chan {
866f7278fd0SJosepch Chan 	struct via_spec *spec;
867f7278fd0SJosepch Chan 	int err;
868f7278fd0SJosepch Chan 
869518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)
870518bf3baSLydia Wang 		return patch_vt1708S(codec);
871ddd304d8STakashi Iwai 
872f7278fd0SJosepch Chan 	/* create a codec specific record */
8735b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
874f7278fd0SJosepch Chan 	if (spec == NULL)
875f7278fd0SJosepch Chan 		return -ENOMEM;
876f7278fd0SJosepch Chan 
877b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
878620e2b28STakashi Iwai 
879f7278fd0SJosepch Chan 	/* automatic parse from the BIOS config */
88012daef65STakashi Iwai 	err = via_parse_auto_config(codec);
881f7278fd0SJosepch Chan 	if (err < 0) {
882f7278fd0SJosepch Chan 		via_free(codec);
883f7278fd0SJosepch Chan 		return err;
884f7278fd0SJosepch Chan 	}
885f7278fd0SJosepch Chan 
886f7278fd0SJosepch Chan 	codec->patch_ops = via_patch_ops;
887f7278fd0SJosepch Chan 
8883e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
8893e95b9abSLydia Wang 
890f7278fd0SJosepch Chan 	return 0;
891f7278fd0SJosepch Chan }
892f7278fd0SJosepch Chan 
893d949cac1SHarald Welte /* Patch for VT1708S */
894096a8854STakashi Iwai static const struct hda_verb vt1708S_init_verbs[] = {
895d7426329SHarald Welte 	/* Enable Mic Boost Volume backdoor */
896d7426329SHarald Welte 	{0x1, 0xf98, 0x1},
897bc7e7e5cSLydia Wang 	/* don't bybass mixer */
898bc7e7e5cSLydia Wang 	{0x1, 0xf88, 0xc0},
899d949cac1SHarald Welte 	{ }
900d949cac1SHarald Welte };
901d949cac1SHarald Welte 
9026369bcfcSLydia Wang static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
9036369bcfcSLydia Wang 			       int offset, int num_steps, int step_size)
9046369bcfcSLydia Wang {
905d045c5dcSTakashi Iwai 	snd_hda_override_wcaps(codec, pin,
906d045c5dcSTakashi Iwai 			       get_wcaps(codec, pin) | AC_WCAP_IN_AMP);
9076369bcfcSLydia Wang 	snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
9086369bcfcSLydia Wang 				  (offset << AC_AMPCAP_OFFSET_SHIFT) |
9096369bcfcSLydia Wang 				  (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
9106369bcfcSLydia Wang 				  (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
9116369bcfcSLydia Wang 				  (0 << AC_AMPCAP_MUTE_SHIFT));
9126369bcfcSLydia Wang }
9136369bcfcSLydia Wang 
914d949cac1SHarald Welte static int patch_vt1708S(struct hda_codec *codec)
915d949cac1SHarald Welte {
916d949cac1SHarald Welte 	struct via_spec *spec;
917d949cac1SHarald Welte 	int err;
918d949cac1SHarald Welte 
919d949cac1SHarald Welte 	/* create a codec specific record */
9205b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
921d949cac1SHarald Welte 	if (spec == NULL)
922d949cac1SHarald Welte 		return -ENOMEM;
923d949cac1SHarald Welte 
924b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
925d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
926d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
927620e2b28STakashi Iwai 
928518bf3baSLydia Wang 	/* correct names for VT1708BCE */
929518bf3baSLydia Wang 	if (get_codec_type(codec) == VT1708BCE)	{
930518bf3baSLydia Wang 		kfree(codec->chip_name);
931518bf3baSLydia Wang 		codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
932518bf3baSLydia Wang 		snprintf(codec->bus->card->mixername,
933518bf3baSLydia Wang 			 sizeof(codec->bus->card->mixername),
934518bf3baSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
935970f630fSLydia Wang 	}
936bc92df7fSLydia Wang 	/* correct names for VT1705 */
937bc92df7fSLydia Wang 	if (codec->vendor_id == 0x11064397)	{
938bc92df7fSLydia Wang 		kfree(codec->chip_name);
939bc92df7fSLydia Wang 		codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
940bc92df7fSLydia Wang 		snprintf(codec->bus->card->mixername,
941bc92df7fSLydia Wang 			 sizeof(codec->bus->card->mixername),
942bc92df7fSLydia Wang 			 "%s %s", codec->vendor_name, codec->chip_name);
943bc92df7fSLydia Wang 	}
944b3f6008fSTakashi Iwai 
945b3f6008fSTakashi Iwai 	/* automatic parse from the BIOS config */
946b3f6008fSTakashi Iwai 	err = via_parse_auto_config(codec);
947b3f6008fSTakashi Iwai 	if (err < 0) {
948b3f6008fSTakashi Iwai 		via_free(codec);
949b3f6008fSTakashi Iwai 		return err;
950b3f6008fSTakashi Iwai 	}
951b3f6008fSTakashi Iwai 
952b3f6008fSTakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
953b3f6008fSTakashi Iwai 
954b3f6008fSTakashi Iwai 	codec->patch_ops = via_patch_ops;
955b3f6008fSTakashi Iwai 
9563e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1708B;
957d949cac1SHarald Welte 	return 0;
958d949cac1SHarald Welte }
959d949cac1SHarald Welte 
960d949cac1SHarald Welte /* Patch for VT1702 */
961d949cac1SHarald Welte 
962096a8854STakashi Iwai static const struct hda_verb vt1702_init_verbs[] = {
963bc7e7e5cSLydia Wang 	/* mixer enable */
964bc7e7e5cSLydia Wang 	{0x1, 0xF88, 0x3},
965bc7e7e5cSLydia Wang 	/* GPIO 0~2 */
966bc7e7e5cSLydia Wang 	{0x1, 0xF82, 0x3F},
967d949cac1SHarald Welte 	{ }
968d949cac1SHarald Welte };
969d949cac1SHarald Welte 
9703e95b9abSLydia Wang static void set_widgets_power_state_vt1702(struct hda_codec *codec)
9713e95b9abSLydia Wang {
9723e95b9abSLydia Wang 	int imux_is_smixer =
9733e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
9743e95b9abSLydia Wang 	unsigned int parm;
9753e95b9abSLydia Wang 	/* inputs */
9763e95b9abSLydia Wang 	/* PW 1/2/5 (14h/15h/18h) */
9773e95b9abSLydia Wang 	parm = AC_PWRST_D3;
9783e95b9abSLydia Wang 	set_pin_power_state(codec, 0x14, &parm);
9793e95b9abSLydia Wang 	set_pin_power_state(codec, 0x15, &parm);
9803e95b9abSLydia Wang 	set_pin_power_state(codec, 0x18, &parm);
9813e95b9abSLydia Wang 	if (imux_is_smixer)
9823e95b9abSLydia Wang 		parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
9833e95b9abSLydia Wang 	/* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
984054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
985054d867eSTakashi Iwai 	update_power_state(codec, 0x12, parm);
986054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
987054d867eSTakashi Iwai 	update_power_state(codec, 0x20, parm);
9883e95b9abSLydia Wang 
9893e95b9abSLydia Wang 	/* outputs */
9903e95b9abSLydia Wang 	/* PW 3/4 (16h/17h) */
9913e95b9abSLydia Wang 	parm = AC_PWRST_D3;
9923e95b9abSLydia Wang 	set_pin_power_state(codec, 0x17, &parm);
9933e95b9abSLydia Wang 	set_pin_power_state(codec, 0x16, &parm);
9943e95b9abSLydia Wang 	/* MW0 (1ah), AOW 0/1 (10h/1dh) */
995054d867eSTakashi Iwai 	update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm);
996054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
997054d867eSTakashi Iwai 	update_power_state(codec, 0x1d, parm);
9983e95b9abSLydia Wang }
9993e95b9abSLydia Wang 
1000d949cac1SHarald Welte static int patch_vt1702(struct hda_codec *codec)
1001d949cac1SHarald Welte {
1002d949cac1SHarald Welte 	struct via_spec *spec;
1003d949cac1SHarald Welte 	int err;
1004d949cac1SHarald Welte 
1005d949cac1SHarald Welte 	/* create a codec specific record */
10065b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1007d949cac1SHarald Welte 	if (spec == NULL)
1008d949cac1SHarald Welte 		return -ENOMEM;
1009d949cac1SHarald Welte 
1010b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x1a;
1011620e2b28STakashi Iwai 
101212daef65STakashi Iwai 	/* limit AA path volume to 0 dB */
101312daef65STakashi Iwai 	snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
101412daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
101512daef65STakashi Iwai 				  (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
101612daef65STakashi Iwai 				  (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
101712daef65STakashi Iwai 				  (1 << AC_AMPCAP_MUTE_SHIFT));
101812daef65STakashi Iwai 
1019d949cac1SHarald Welte 	/* automatic parse from the BIOS config */
102012daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1021d949cac1SHarald Welte 	if (err < 0) {
1022d949cac1SHarald Welte 		via_free(codec);
1023d949cac1SHarald Welte 		return err;
1024d949cac1SHarald Welte 	}
1025d949cac1SHarald Welte 
1026096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
1027d949cac1SHarald Welte 
1028d949cac1SHarald Welte 	codec->patch_ops = via_patch_ops;
1029d949cac1SHarald Welte 
10303e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1702;
1031d949cac1SHarald Welte 	return 0;
1032d949cac1SHarald Welte }
1033d949cac1SHarald Welte 
1034eb7188caSLydia Wang /* Patch for VT1718S */
1035eb7188caSLydia Wang 
1036096a8854STakashi Iwai static const struct hda_verb vt1718S_init_verbs[] = {
10374ab2d53aSLydia Wang 	/* Enable MW0 adjust Gain 5 */
10384ab2d53aSLydia Wang 	{0x1, 0xfb2, 0x10},
1039eb7188caSLydia Wang 	/* Enable Boost Volume backdoor */
1040eb7188caSLydia Wang 	{0x1, 0xf88, 0x8},
10415d41762aSTakashi Iwai 
1042eb7188caSLydia Wang 	{ }
1043eb7188caSLydia Wang };
1044eb7188caSLydia Wang 
10453e95b9abSLydia Wang static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
10463e95b9abSLydia Wang {
10473e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
10483e95b9abSLydia Wang 	int imux_is_smixer;
10496162552bSTakashi Iwai 	unsigned int parm, parm2;
10503e95b9abSLydia Wang 	/* MUX6 (1eh) = stereo mixer */
10513e95b9abSLydia Wang 	imux_is_smixer =
10523e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
10533e95b9abSLydia Wang 	/* inputs */
10543e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
10553e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10563e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
10573e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
10583e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
10593e95b9abSLydia Wang 	if (imux_is_smixer)
10603e95b9abSLydia Wang 		parm = AC_PWRST_D0;
10613e95b9abSLydia Wang 	/* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
1062054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
1063054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
1064054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1065054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
10663e95b9abSLydia Wang 
10673e95b9abSLydia Wang 	/* outputs */
10683e95b9abSLydia Wang 	/* PW3 (27h), MW2 (1ah), AOW3 (bh) */
10693e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10703e95b9abSLydia Wang 	set_pin_power_state(codec, 0x27, &parm);
1071054d867eSTakashi Iwai 	update_power_state(codec, 0x1a, parm);
10726162552bSTakashi Iwai 	parm2 = parm; /* for pin 0x0b */
10733e95b9abSLydia Wang 
10743e95b9abSLydia Wang 	/* PW2 (26h), AOW2 (ah) */
10753e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10763e95b9abSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
1077b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
10783e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
1079054d867eSTakashi Iwai 	update_power_state(codec, 0xa, parm);
10803e95b9abSLydia Wang 
10813e95b9abSLydia Wang 	/* PW0 (24h), AOW0 (8h) */
10823e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10833e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
1084b3f6008fSTakashi Iwai 	if (!spec->gen.indep_hp_enabled) /* check for redirected HP */
10853e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
1086054d867eSTakashi Iwai 	update_power_state(codec, 0x8, parm);
1087b3f6008fSTakashi Iwai 	if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3)
10886162552bSTakashi Iwai 		parm = parm2;
10896162552bSTakashi Iwai 	update_power_state(codec, 0xb, parm);
10903e95b9abSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
1091054d867eSTakashi Iwai 	update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm);
10923e95b9abSLydia Wang 
10933e95b9abSLydia Wang 	/* PW1 (25h), AOW1 (9h) */
10943e95b9abSLydia Wang 	parm = AC_PWRST_D3;
10953e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
1096b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
10973e95b9abSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
1098054d867eSTakashi Iwai 	update_power_state(codec, 0x9, parm);
10993e95b9abSLydia Wang 
1100b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled) {
11013e95b9abSLydia Wang 		/* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
11023e95b9abSLydia Wang 		parm = AC_PWRST_D3;
11033e95b9abSLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
1104054d867eSTakashi Iwai 		update_power_state(codec, 0x1b, parm);
1105054d867eSTakashi Iwai 		update_power_state(codec, 0x34, parm);
1106054d867eSTakashi Iwai 		update_power_state(codec, 0xc, parm);
11073e95b9abSLydia Wang 	}
11083e95b9abSLydia Wang }
11093e95b9abSLydia Wang 
111030b45033STakashi Iwai /* Add a connection to the primary DAC from AA-mixer for some codecs
111130b45033STakashi Iwai  * This isn't listed from the raw info, but the chip has a secret connection.
111230b45033STakashi Iwai  */
111330b45033STakashi Iwai static int add_secret_dac_path(struct hda_codec *codec)
111430b45033STakashi Iwai {
111530b45033STakashi Iwai 	struct via_spec *spec = codec->spec;
111630b45033STakashi Iwai 	int i, nums;
111730b45033STakashi Iwai 	hda_nid_t conn[8];
111830b45033STakashi Iwai 	hda_nid_t nid;
111930b45033STakashi Iwai 
1120b3f6008fSTakashi Iwai 	if (!spec->gen.mixer_nid)
112130b45033STakashi Iwai 		return 0;
1122b3f6008fSTakashi Iwai 	nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
112330b45033STakashi Iwai 				       ARRAY_SIZE(conn) - 1);
112430b45033STakashi Iwai 	for (i = 0; i < nums; i++) {
112530b45033STakashi Iwai 		if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
112630b45033STakashi Iwai 			return 0;
112730b45033STakashi Iwai 	}
112830b45033STakashi Iwai 
112930b45033STakashi Iwai 	/* find the primary DAC and add to the connection list */
113030b45033STakashi Iwai 	nid = codec->start_nid;
113130b45033STakashi Iwai 	for (i = 0; i < codec->num_nodes; i++, nid++) {
113230b45033STakashi Iwai 		unsigned int caps = get_wcaps(codec, nid);
113330b45033STakashi Iwai 		if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
113430b45033STakashi Iwai 		    !(caps & AC_WCAP_DIGITAL)) {
113530b45033STakashi Iwai 			conn[nums++] = nid;
113630b45033STakashi Iwai 			return snd_hda_override_conn_list(codec,
1137b3f6008fSTakashi Iwai 							  spec->gen.mixer_nid,
113830b45033STakashi Iwai 							  nums, conn);
113930b45033STakashi Iwai 		}
114030b45033STakashi Iwai 	}
114130b45033STakashi Iwai 	return 0;
114230b45033STakashi Iwai }
114330b45033STakashi Iwai 
114430b45033STakashi Iwai 
1145eb7188caSLydia Wang static int patch_vt1718S(struct hda_codec *codec)
1146eb7188caSLydia Wang {
1147eb7188caSLydia Wang 	struct via_spec *spec;
1148eb7188caSLydia Wang 	int err;
1149eb7188caSLydia Wang 
1150eb7188caSLydia Wang 	/* create a codec specific record */
11515b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1152eb7188caSLydia Wang 	if (spec == NULL)
1153eb7188caSLydia Wang 		return -ENOMEM;
1154eb7188caSLydia Wang 
1155b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1156d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1157d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
115830b45033STakashi Iwai 	add_secret_dac_path(codec);
1159620e2b28STakashi Iwai 
1160eb7188caSLydia Wang 	/* automatic parse from the BIOS config */
116112daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1162eb7188caSLydia Wang 	if (err < 0) {
1163eb7188caSLydia Wang 		via_free(codec);
1164eb7188caSLydia Wang 		return err;
1165eb7188caSLydia Wang 	}
1166eb7188caSLydia Wang 
1167096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
1168eb7188caSLydia Wang 
1169eb7188caSLydia Wang 	codec->patch_ops = via_patch_ops;
1170eb7188caSLydia Wang 
11713e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1718S;
11723e95b9abSLydia Wang 
1173eb7188caSLydia Wang 	return 0;
1174eb7188caSLydia Wang }
1175f3db423dSLydia Wang 
1176f3db423dSLydia Wang /* Patch for VT1716S */
1177f3db423dSLydia Wang 
1178f3db423dSLydia Wang static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
1179f3db423dSLydia Wang 			    struct snd_ctl_elem_info *uinfo)
1180f3db423dSLydia Wang {
1181f3db423dSLydia Wang 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
1182f3db423dSLydia Wang 	uinfo->count = 1;
1183f3db423dSLydia Wang 	uinfo->value.integer.min = 0;
1184f3db423dSLydia Wang 	uinfo->value.integer.max = 1;
1185f3db423dSLydia Wang 	return 0;
1186f3db423dSLydia Wang }
1187f3db423dSLydia Wang 
1188f3db423dSLydia Wang static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
1189f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
1190f3db423dSLydia Wang {
1191f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1192f3db423dSLydia Wang 	int index = 0;
1193f3db423dSLydia Wang 
1194f3db423dSLydia Wang 	index = snd_hda_codec_read(codec, 0x26, 0,
1195f3db423dSLydia Wang 					       AC_VERB_GET_CONNECT_SEL, 0);
1196f3db423dSLydia Wang 	if (index != -1)
1197f3db423dSLydia Wang 		*ucontrol->value.integer.value = index;
1198f3db423dSLydia Wang 
1199f3db423dSLydia Wang 	return 0;
1200f3db423dSLydia Wang }
1201f3db423dSLydia Wang 
1202f3db423dSLydia Wang static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
1203f3db423dSLydia Wang 			   struct snd_ctl_elem_value *ucontrol)
1204f3db423dSLydia Wang {
1205f3db423dSLydia Wang 	struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1206f3db423dSLydia Wang 	struct via_spec *spec = codec->spec;
1207f3db423dSLydia Wang 	int index = *ucontrol->value.integer.value;
1208f3db423dSLydia Wang 
1209f3db423dSLydia Wang 	snd_hda_codec_write(codec, 0x26, 0,
1210f3db423dSLydia Wang 					       AC_VERB_SET_CONNECT_SEL, index);
1211f3db423dSLydia Wang 	spec->dmic_enabled = index;
12123e95b9abSLydia Wang 	set_widgets_power_state(codec);
1213f3db423dSLydia Wang 	return 1;
1214f3db423dSLydia Wang }
1215f3db423dSLydia Wang 
121690dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
1217f3db423dSLydia Wang 	HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
1218f3db423dSLydia Wang 	{
1219f3db423dSLydia Wang 	 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1220f3db423dSLydia Wang 	 .name = "Digital Mic Capture Switch",
12215b0cb1d8SJaroslav Kysela 	 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
1222f3db423dSLydia Wang 	 .count = 1,
1223f3db423dSLydia Wang 	 .info = vt1716s_dmic_info,
1224f3db423dSLydia Wang 	 .get = vt1716s_dmic_get,
1225f3db423dSLydia Wang 	 .put = vt1716s_dmic_put,
1226f3db423dSLydia Wang 	 },
1227f3db423dSLydia Wang 	{}			/* end */
1228f3db423dSLydia Wang };
1229f3db423dSLydia Wang 
1230f3db423dSLydia Wang 
1231f3db423dSLydia Wang /* mono-out mixer elements */
123290dd48a1STakashi Iwai static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
1233f3db423dSLydia Wang 	HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
1234f3db423dSLydia Wang 	{ } /* end */
1235f3db423dSLydia Wang };
1236f3db423dSLydia Wang 
1237096a8854STakashi Iwai static const struct hda_verb vt1716S_init_verbs[] = {
1238f3db423dSLydia Wang 	/* Enable Boost Volume backdoor */
1239f3db423dSLydia Wang 	{0x1, 0xf8a, 0x80},
1240f3db423dSLydia Wang 	/* don't bybass mixer */
1241f3db423dSLydia Wang 	{0x1, 0xf88, 0xc0},
1242f3db423dSLydia Wang 	/* Enable mono output */
1243f3db423dSLydia Wang 	{0x1, 0xf90, 0x08},
1244f3db423dSLydia Wang 	{ }
1245f3db423dSLydia Wang };
1246f3db423dSLydia Wang 
12473e95b9abSLydia Wang static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
12483e95b9abSLydia Wang {
12493e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
12503e95b9abSLydia Wang 	int imux_is_smixer;
12513e95b9abSLydia Wang 	unsigned int parm;
12523e95b9abSLydia Wang 	unsigned int mono_out, present;
12533e95b9abSLydia Wang 	/* SW0 (17h) = stereo mixer */
12543e95b9abSLydia Wang 	imux_is_smixer =
12553e95b9abSLydia Wang 	(snd_hda_codec_read(codec, 0x17, 0,
12563e95b9abSLydia Wang 			    AC_VERB_GET_CONNECT_SEL, 0x00) ==  5);
12573e95b9abSLydia Wang 	/* inputs */
12583e95b9abSLydia Wang 	/* PW 1/2/5 (1ah/1bh/1eh) */
12593e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12603e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1a, &parm);
12613e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1b, &parm);
12623e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
12633e95b9abSLydia Wang 	if (imux_is_smixer)
12643e95b9abSLydia Wang 		parm = AC_PWRST_D0;
12653e95b9abSLydia Wang 	/* SW0 (17h), AIW0(13h) */
1266054d867eSTakashi Iwai 	update_power_state(codec, 0x17, parm);
1267054d867eSTakashi Iwai 	update_power_state(codec, 0x13, parm);
12683e95b9abSLydia Wang 
12693e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12703e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1e, &parm);
12713e95b9abSLydia Wang 	/* PW11 (22h) */
12723e95b9abSLydia Wang 	if (spec->dmic_enabled)
12733e95b9abSLydia Wang 		set_pin_power_state(codec, 0x22, &parm);
12743e95b9abSLydia Wang 	else
1275054d867eSTakashi Iwai 		update_power_state(codec, 0x22, AC_PWRST_D3);
12763e95b9abSLydia Wang 
12773e95b9abSLydia Wang 	/* SW2(26h), AIW1(14h) */
1278054d867eSTakashi Iwai 	update_power_state(codec, 0x26, parm);
1279054d867eSTakashi Iwai 	update_power_state(codec, 0x14, parm);
12803e95b9abSLydia Wang 
12813e95b9abSLydia Wang 	/* outputs */
12823e95b9abSLydia Wang 	/* PW0 (19h), SW1 (18h), AOW1 (11h) */
12833e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12843e95b9abSLydia Wang 	set_pin_power_state(codec, 0x19, &parm);
12853e95b9abSLydia Wang 	/* Smart 5.1 PW2(1bh) */
1286b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
12873e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1b, &parm);
1288054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
1289054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
12903e95b9abSLydia Wang 
12913e95b9abSLydia Wang 	/* PW7 (23h), SW3 (27h), AOW3 (25h) */
12923e95b9abSLydia Wang 	parm = AC_PWRST_D3;
12933e95b9abSLydia Wang 	set_pin_power_state(codec, 0x23, &parm);
12943e95b9abSLydia Wang 	/* Smart 5.1 PW1(1ah) */
1295b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
12963e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1a, &parm);
1297054d867eSTakashi Iwai 	update_power_state(codec, 0x27, parm);
12983e95b9abSLydia Wang 
12993e95b9abSLydia Wang 	/* Smart 5.1 PW5(1eh) */
1300b3f6008fSTakashi Iwai 	if (smart51_enabled(codec))
13013e95b9abSLydia Wang 		set_pin_power_state(codec, 0x1e, &parm);
1302054d867eSTakashi Iwai 	update_power_state(codec, 0x25, parm);
13033e95b9abSLydia Wang 
13043e95b9abSLydia Wang 	/* Mono out */
13053e95b9abSLydia Wang 	/* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
13063e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x1c);
13073e95b9abSLydia Wang 
13083e95b9abSLydia Wang 	if (present)
13093e95b9abSLydia Wang 		mono_out = 0;
13103e95b9abSLydia Wang 	else {
13113e95b9abSLydia Wang 		present = snd_hda_jack_detect(codec, 0x1d);
1312b3f6008fSTakashi Iwai 		if (!spec->gen.indep_hp_enabled && present)
13133e95b9abSLydia Wang 			mono_out = 0;
13143e95b9abSLydia Wang 		else
13153e95b9abSLydia Wang 			mono_out = 1;
13163e95b9abSLydia Wang 	}
13173e95b9abSLydia Wang 	parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
1318054d867eSTakashi Iwai 	update_power_state(codec, 0x28, parm);
1319054d867eSTakashi Iwai 	update_power_state(codec, 0x29, parm);
1320054d867eSTakashi Iwai 	update_power_state(codec, 0x2a, parm);
13213e95b9abSLydia Wang 
13223e95b9abSLydia Wang 	/* PW 3/4 (1ch/1dh) */
13233e95b9abSLydia Wang 	parm = AC_PWRST_D3;
13243e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1c, &parm);
13253e95b9abSLydia Wang 	set_pin_power_state(codec, 0x1d, &parm);
13263e95b9abSLydia Wang 	/* HP Independent Mode, power on AOW3 */
1327b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
1328054d867eSTakashi Iwai 		update_power_state(codec, 0x25, parm);
13293e95b9abSLydia Wang 
13303e95b9abSLydia Wang 	/* force to D0 for internal Speaker */
13313e95b9abSLydia Wang 	/* MW0 (16h), AOW0 (10h) */
1332054d867eSTakashi Iwai 	update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
1333054d867eSTakashi Iwai 	update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm);
13343e95b9abSLydia Wang }
13353e95b9abSLydia Wang 
1336f3db423dSLydia Wang static int patch_vt1716S(struct hda_codec *codec)
1337f3db423dSLydia Wang {
1338f3db423dSLydia Wang 	struct via_spec *spec;
1339f3db423dSLydia Wang 	int err;
1340f3db423dSLydia Wang 
1341f3db423dSLydia Wang 	/* create a codec specific record */
13425b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1343f3db423dSLydia Wang 	if (spec == NULL)
1344f3db423dSLydia Wang 		return -ENOMEM;
1345f3db423dSLydia Wang 
1346b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x16;
1347d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1a, 0, 3, 40);
1348d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x1e, 0, 3, 40);
1349620e2b28STakashi Iwai 
1350f3db423dSLydia Wang 	/* automatic parse from the BIOS config */
135112daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1352f3db423dSLydia Wang 	if (err < 0) {
1353f3db423dSLydia Wang 		via_free(codec);
1354f3db423dSLydia Wang 		return err;
1355f3db423dSLydia Wang 	}
1356f3db423dSLydia Wang 
1357096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1716S_init_verbs;
1358f3db423dSLydia Wang 
1359b3f6008fSTakashi Iwai 	spec->mixers[spec->num_mixers++] = vt1716s_dmic_mixer;
1360f3db423dSLydia Wang 	spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
1361f3db423dSLydia Wang 
1362f3db423dSLydia Wang 	codec->patch_ops = via_patch_ops;
1363f3db423dSLydia Wang 
13643e95b9abSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
1365f3db423dSLydia Wang 	return 0;
1366f3db423dSLydia Wang }
136725eaba2fSLydia Wang 
136825eaba2fSLydia Wang /* for vt2002P */
136925eaba2fSLydia Wang 
1370096a8854STakashi Iwai static const struct hda_verb vt2002P_init_verbs[] = {
1371eadb9a80SLydia Wang 	/* Class-D speaker related verbs */
1372eadb9a80SLydia Wang 	{0x1, 0xfe0, 0x4},
1373eadb9a80SLydia Wang 	{0x1, 0xfe9, 0x80},
1374eadb9a80SLydia Wang 	{0x1, 0xfe2, 0x22},
137525eaba2fSLydia Wang 	/* Enable Boost Volume backdoor */
137625eaba2fSLydia Wang 	{0x1, 0xfb9, 0x24},
137725eaba2fSLydia Wang 	/* Enable AOW0 to MW9 */
137825eaba2fSLydia Wang 	{0x1, 0xfb8, 0x88},
137925eaba2fSLydia Wang 	{ }
138025eaba2fSLydia Wang };
13814a918ffeSTakashi Iwai 
1382096a8854STakashi Iwai static const struct hda_verb vt1802_init_verbs[] = {
138311890956SLydia Wang 	/* Enable Boost Volume backdoor */
138411890956SLydia Wang 	{0x1, 0xfb9, 0x24},
138511890956SLydia Wang 	/* Enable AOW0 to MW9 */
138611890956SLydia Wang 	{0x1, 0xfb8, 0x88},
138711890956SLydia Wang 	{ }
138811890956SLydia Wang };
138925eaba2fSLydia Wang 
13903e95b9abSLydia Wang static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
13913e95b9abSLydia Wang {
13923e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
13933e95b9abSLydia Wang 	int imux_is_smixer;
13943e95b9abSLydia Wang 	unsigned int parm;
13953e95b9abSLydia Wang 	unsigned int present;
13963e95b9abSLydia Wang 	/* MUX9 (1eh) = stereo mixer */
13973e95b9abSLydia Wang 	imux_is_smixer =
13983e95b9abSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
13993e95b9abSLydia Wang 	/* inputs */
14003e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
14013e95b9abSLydia Wang 	parm = AC_PWRST_D3;
14023e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
14033e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
14043e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
14053e95b9abSLydia Wang 	parm = AC_PWRST_D0;
14063e95b9abSLydia Wang 	/* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
1407054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
1408054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
1409054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1410054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
14113e95b9abSLydia Wang 
14123e95b9abSLydia Wang 	/* outputs */
14133e95b9abSLydia Wang 	/* AOW0 (8h)*/
1414054d867eSTakashi Iwai 	update_power_state(codec, 0x8, parm);
14153e95b9abSLydia Wang 
141611890956SLydia Wang 	if (spec->codec_type == VT1802) {
141711890956SLydia Wang 		/* PW4 (28h), MW4 (18h), MUX4(38h) */
141811890956SLydia Wang 		parm = AC_PWRST_D3;
141911890956SLydia Wang 		set_pin_power_state(codec, 0x28, &parm);
1420054d867eSTakashi Iwai 		update_power_state(codec, 0x18, parm);
1421054d867eSTakashi Iwai 		update_power_state(codec, 0x38, parm);
142211890956SLydia Wang 	} else {
14233e95b9abSLydia Wang 		/* PW4 (26h), MW4 (1ch), MUX4(37h) */
14243e95b9abSLydia Wang 		parm = AC_PWRST_D3;
14253e95b9abSLydia Wang 		set_pin_power_state(codec, 0x26, &parm);
1426054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, parm);
1427054d867eSTakashi Iwai 		update_power_state(codec, 0x37, parm);
142811890956SLydia Wang 	}
14293e95b9abSLydia Wang 
143011890956SLydia Wang 	if (spec->codec_type == VT1802) {
143111890956SLydia Wang 		/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
143211890956SLydia Wang 		parm = AC_PWRST_D3;
143311890956SLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
1434054d867eSTakashi Iwai 		update_power_state(codec, 0x15, parm);
1435054d867eSTakashi Iwai 		update_power_state(codec, 0x35, parm);
143611890956SLydia Wang 	} else {
14373e95b9abSLydia Wang 		/* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
14383e95b9abSLydia Wang 		parm = AC_PWRST_D3;
14393e95b9abSLydia Wang 		set_pin_power_state(codec, 0x25, &parm);
1440054d867eSTakashi Iwai 		update_power_state(codec, 0x19, parm);
1441054d867eSTakashi Iwai 		update_power_state(codec, 0x35, parm);
144211890956SLydia Wang 	}
14433e95b9abSLydia Wang 
1444b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
1445054d867eSTakashi Iwai 		update_power_state(codec, 0x9, AC_PWRST_D0);
14463e95b9abSLydia Wang 
14473e95b9abSLydia Wang 	/* Class-D */
14483e95b9abSLydia Wang 	/* PW0 (24h), MW0(18h/14h), MUX0(34h) */
14493e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
14503e95b9abSLydia Wang 
14513e95b9abSLydia Wang 	parm = AC_PWRST_D3;
14523e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
14533e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
145411890956SLydia Wang 	if (spec->codec_type == VT1802)
1455054d867eSTakashi Iwai 		update_power_state(codec, 0x14, parm);
145611890956SLydia Wang 	else
1457054d867eSTakashi Iwai 		update_power_state(codec, 0x18, parm);
1458054d867eSTakashi Iwai 	update_power_state(codec, 0x34, parm);
14593e95b9abSLydia Wang 
14603e95b9abSLydia Wang 	/* Mono Out */
14613e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x26);
14623e95b9abSLydia Wang 
14633e95b9abSLydia Wang 	parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
146411890956SLydia Wang 	if (spec->codec_type == VT1802) {
146511890956SLydia Wang 		/* PW15 (33h), MW8(1ch), MUX8(3ch) */
1466054d867eSTakashi Iwai 		update_power_state(codec, 0x33, parm);
1467054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, parm);
1468054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, parm);
146911890956SLydia Wang 	} else {
14703e95b9abSLydia Wang 		/* PW15 (31h), MW8(17h), MUX8(3bh) */
1471054d867eSTakashi Iwai 		update_power_state(codec, 0x31, parm);
1472054d867eSTakashi Iwai 		update_power_state(codec, 0x17, parm);
1473054d867eSTakashi Iwai 		update_power_state(codec, 0x3b, parm);
147411890956SLydia Wang 	}
14753e95b9abSLydia Wang 	/* MW9 (21h) */
14763e95b9abSLydia Wang 	if (imux_is_smixer || !is_aa_path_mute(codec))
1477054d867eSTakashi Iwai 		update_power_state(codec, 0x21, AC_PWRST_D0);
14783e95b9abSLydia Wang 	else
1479054d867eSTakashi Iwai 		update_power_state(codec, 0x21, AC_PWRST_D3);
14803e95b9abSLydia Wang }
148125eaba2fSLydia Wang 
14824b527b65SDavid Henningsson /*
14834b527b65SDavid Henningsson  * pin fix-up
14844b527b65SDavid Henningsson  */
14854b527b65SDavid Henningsson enum {
14864b527b65SDavid Henningsson 	VIA_FIXUP_INTMIC_BOOST,
1487d5266125STakashi Iwai 	VIA_FIXUP_ASUS_G75,
14884b527b65SDavid Henningsson };
14894b527b65SDavid Henningsson 
14904b527b65SDavid Henningsson static void via_fixup_intmic_boost(struct hda_codec *codec,
14914b527b65SDavid Henningsson 				  const struct hda_fixup *fix, int action)
14924b527b65SDavid Henningsson {
14934b527b65SDavid Henningsson 	if (action == HDA_FIXUP_ACT_PRE_PROBE)
14944b527b65SDavid Henningsson 		override_mic_boost(codec, 0x30, 0, 2, 40);
14954b527b65SDavid Henningsson }
14964b527b65SDavid Henningsson 
14974b527b65SDavid Henningsson static const struct hda_fixup via_fixups[] = {
14984b527b65SDavid Henningsson 	[VIA_FIXUP_INTMIC_BOOST] = {
14994b527b65SDavid Henningsson 		.type = HDA_FIXUP_FUNC,
15004b527b65SDavid Henningsson 		.v.func = via_fixup_intmic_boost,
15014b527b65SDavid Henningsson 	},
1502d5266125STakashi Iwai 	[VIA_FIXUP_ASUS_G75] = {
1503d5266125STakashi Iwai 		.type = HDA_FIXUP_PINS,
1504d5266125STakashi Iwai 		.v.pins = (const struct hda_pintbl[]) {
1505d5266125STakashi Iwai 			/* set 0x24 and 0x33 as speakers */
1506d5266125STakashi Iwai 			{ 0x24, 0x991301f0 },
1507d5266125STakashi Iwai 			{ 0x33, 0x991301f1 }, /* subwoofer */
1508d5266125STakashi Iwai 			{ }
1509d5266125STakashi Iwai 		}
1510d5266125STakashi Iwai 	},
15114b527b65SDavid Henningsson };
15124b527b65SDavid Henningsson 
15134b527b65SDavid Henningsson static const struct snd_pci_quirk vt2002p_fixups[] = {
1514d5266125STakashi Iwai 	SND_PCI_QUIRK(0x1043, 0x1487, "Asus G75", VIA_FIXUP_ASUS_G75),
15154b527b65SDavid Henningsson 	SND_PCI_QUIRK(0x1043, 0x8532, "Asus X202E", VIA_FIXUP_INTMIC_BOOST),
15164b527b65SDavid Henningsson 	{}
15174b527b65SDavid Henningsson };
15184b527b65SDavid Henningsson 
1519ef4da458STakashi Iwai /* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
1520ef4da458STakashi Iwai  * Replace this with mixer NID 0x1c
1521ef4da458STakashi Iwai  */
1522ef4da458STakashi Iwai static void fix_vt1802_connections(struct hda_codec *codec)
1523ef4da458STakashi Iwai {
1524ef4da458STakashi Iwai 	static hda_nid_t conn_24[] = { 0x14, 0x1c };
1525ef4da458STakashi Iwai 	static hda_nid_t conn_33[] = { 0x1c };
1526ef4da458STakashi Iwai 
1527ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
1528ef4da458STakashi Iwai 	snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
1529ef4da458STakashi Iwai }
1530ef4da458STakashi Iwai 
153125eaba2fSLydia Wang /* patch for vt2002P */
153225eaba2fSLydia Wang static int patch_vt2002P(struct hda_codec *codec)
153325eaba2fSLydia Wang {
153425eaba2fSLydia Wang 	struct via_spec *spec;
153525eaba2fSLydia Wang 	int err;
153625eaba2fSLydia Wang 
153725eaba2fSLydia Wang 	/* create a codec specific record */
15385b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
153925eaba2fSLydia Wang 	if (spec == NULL)
154025eaba2fSLydia Wang 		return -ENOMEM;
154125eaba2fSLydia Wang 
1542b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1543d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1544d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
1545ef4da458STakashi Iwai 	if (spec->codec_type == VT1802)
1546ef4da458STakashi Iwai 		fix_vt1802_connections(codec);
154730b45033STakashi Iwai 	add_secret_dac_path(codec);
1548620e2b28STakashi Iwai 
15494b527b65SDavid Henningsson 	snd_hda_pick_fixup(codec, NULL, vt2002p_fixups, via_fixups);
15504b527b65SDavid Henningsson 	snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
15514b527b65SDavid Henningsson 
155225eaba2fSLydia Wang 	/* automatic parse from the BIOS config */
155312daef65STakashi Iwai 	err = via_parse_auto_config(codec);
155425eaba2fSLydia Wang 	if (err < 0) {
155525eaba2fSLydia Wang 		via_free(codec);
155625eaba2fSLydia Wang 		return err;
155725eaba2fSLydia Wang 	}
155825eaba2fSLydia Wang 
155911890956SLydia Wang 	if (spec->codec_type == VT1802)
15604a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
156111890956SLydia Wang 	else
15624a918ffeSTakashi Iwai 		spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
156311890956SLydia Wang 
156425eaba2fSLydia Wang 	codec->patch_ops = via_patch_ops;
156525eaba2fSLydia Wang 
15663e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt2002P;
156725eaba2fSLydia Wang 	return 0;
156825eaba2fSLydia Wang }
1569ab6734e7SLydia Wang 
1570ab6734e7SLydia Wang /* for vt1812 */
1571ab6734e7SLydia Wang 
1572096a8854STakashi Iwai static const struct hda_verb vt1812_init_verbs[] = {
1573ab6734e7SLydia Wang 	/* Enable Boost Volume backdoor */
1574ab6734e7SLydia Wang 	{0x1, 0xfb9, 0x24},
1575ab6734e7SLydia Wang 	/* Enable AOW0 to MW9 */
1576ab6734e7SLydia Wang 	{0x1, 0xfb8, 0xa8},
1577ab6734e7SLydia Wang 	{ }
1578ab6734e7SLydia Wang };
1579ab6734e7SLydia Wang 
15803e95b9abSLydia Wang static void set_widgets_power_state_vt1812(struct hda_codec *codec)
15813e95b9abSLydia Wang {
15823e95b9abSLydia Wang 	struct via_spec *spec = codec->spec;
15833e95b9abSLydia Wang 	unsigned int parm;
15843e95b9abSLydia Wang 	unsigned int present;
15853e95b9abSLydia Wang 	/* inputs */
15863e95b9abSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
15873e95b9abSLydia Wang 	parm = AC_PWRST_D3;
15883e95b9abSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
15893e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
15903e95b9abSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
15913e95b9abSLydia Wang 	parm = AC_PWRST_D0;
15923e95b9abSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
1593054d867eSTakashi Iwai 	update_power_state(codec, 0x1e, parm);
1594054d867eSTakashi Iwai 	update_power_state(codec, 0x1f, parm);
1595054d867eSTakashi Iwai 	update_power_state(codec, 0x10, parm);
1596054d867eSTakashi Iwai 	update_power_state(codec, 0x11, parm);
15973e95b9abSLydia Wang 
15983e95b9abSLydia Wang 	/* outputs */
15993e95b9abSLydia Wang 	/* AOW0 (8h)*/
1600054d867eSTakashi Iwai 	update_power_state(codec, 0x8, AC_PWRST_D0);
16013e95b9abSLydia Wang 
16023e95b9abSLydia Wang 	/* PW4 (28h), MW4 (18h), MUX4(38h) */
16033e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16043e95b9abSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
1605054d867eSTakashi Iwai 	update_power_state(codec, 0x18, parm);
1606054d867eSTakashi Iwai 	update_power_state(codec, 0x38, parm);
16073e95b9abSLydia Wang 
16083e95b9abSLydia Wang 	/* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
16093e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16103e95b9abSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
1611054d867eSTakashi Iwai 	update_power_state(codec, 0x15, parm);
1612054d867eSTakashi Iwai 	update_power_state(codec, 0x35, parm);
1613b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
1614054d867eSTakashi Iwai 		update_power_state(codec, 0x9, AC_PWRST_D0);
16153e95b9abSLydia Wang 
16163e95b9abSLydia Wang 	/* Internal Speaker */
16173e95b9abSLydia Wang 	/* PW0 (24h), MW0(14h), MUX0(34h) */
16183e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x25);
16193e95b9abSLydia Wang 
16203e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16213e95b9abSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
16223e95b9abSLydia Wang 	if (present) {
1623054d867eSTakashi Iwai 		update_power_state(codec, 0x14, AC_PWRST_D3);
1624054d867eSTakashi Iwai 		update_power_state(codec, 0x34, AC_PWRST_D3);
16253e95b9abSLydia Wang 	} else {
1626054d867eSTakashi Iwai 		update_power_state(codec, 0x14, AC_PWRST_D0);
1627054d867eSTakashi Iwai 		update_power_state(codec, 0x34, AC_PWRST_D0);
16283e95b9abSLydia Wang 	}
16293e95b9abSLydia Wang 
16303e95b9abSLydia Wang 
16313e95b9abSLydia Wang 	/* Mono Out */
16323e95b9abSLydia Wang 	/* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
16333e95b9abSLydia Wang 	present = snd_hda_jack_detect(codec, 0x28);
16343e95b9abSLydia Wang 
16353e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16363e95b9abSLydia Wang 	set_pin_power_state(codec, 0x31, &parm);
16373e95b9abSLydia Wang 	if (present) {
1638054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, AC_PWRST_D3);
1639054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, AC_PWRST_D3);
1640054d867eSTakashi Iwai 		update_power_state(codec, 0x3e, AC_PWRST_D3);
16413e95b9abSLydia Wang 	} else {
1642054d867eSTakashi Iwai 		update_power_state(codec, 0x1c, AC_PWRST_D0);
1643054d867eSTakashi Iwai 		update_power_state(codec, 0x3c, AC_PWRST_D0);
1644054d867eSTakashi Iwai 		update_power_state(codec, 0x3e, AC_PWRST_D0);
16453e95b9abSLydia Wang 	}
16463e95b9abSLydia Wang 
16473e95b9abSLydia Wang 	/* PW15 (33h), MW15 (1dh), MUX15(3dh) */
16483e95b9abSLydia Wang 	parm = AC_PWRST_D3;
16493e95b9abSLydia Wang 	set_pin_power_state(codec, 0x33, &parm);
1650054d867eSTakashi Iwai 	update_power_state(codec, 0x1d, parm);
1651054d867eSTakashi Iwai 	update_power_state(codec, 0x3d, parm);
16523e95b9abSLydia Wang 
16533e95b9abSLydia Wang }
1654ab6734e7SLydia Wang 
1655ab6734e7SLydia Wang /* patch for vt1812 */
1656ab6734e7SLydia Wang static int patch_vt1812(struct hda_codec *codec)
1657ab6734e7SLydia Wang {
1658ab6734e7SLydia Wang 	struct via_spec *spec;
1659ab6734e7SLydia Wang 	int err;
1660ab6734e7SLydia Wang 
1661ab6734e7SLydia Wang 	/* create a codec specific record */
16625b0cb1d8SJaroslav Kysela 	spec = via_new_spec(codec);
1663ab6734e7SLydia Wang 	if (spec == NULL)
1664ab6734e7SLydia Wang 		return -ENOMEM;
1665ab6734e7SLydia Wang 
1666b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x21;
1667d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x2b, 0, 3, 40);
1668d7a99cceSTakashi Iwai 	override_mic_boost(codec, 0x29, 0, 3, 40);
166930b45033STakashi Iwai 	add_secret_dac_path(codec);
1670620e2b28STakashi Iwai 
1671ab6734e7SLydia Wang 	/* automatic parse from the BIOS config */
167212daef65STakashi Iwai 	err = via_parse_auto_config(codec);
1673ab6734e7SLydia Wang 	if (err < 0) {
1674ab6734e7SLydia Wang 		via_free(codec);
1675ab6734e7SLydia Wang 		return err;
1676ab6734e7SLydia Wang 	}
1677ab6734e7SLydia Wang 
1678096a8854STakashi Iwai 	spec->init_verbs[spec->num_iverbs++]  = vt1812_init_verbs;
1679ab6734e7SLydia Wang 
1680ab6734e7SLydia Wang 	codec->patch_ops = via_patch_ops;
1681ab6734e7SLydia Wang 
16823e95b9abSLydia Wang 	spec->set_widgets_power_state =  set_widgets_power_state_vt1812;
1683ab6734e7SLydia Wang 	return 0;
1684ab6734e7SLydia Wang }
1685ab6734e7SLydia Wang 
168643737e0aSLydia Wang /* patch for vt3476 */
168743737e0aSLydia Wang 
168843737e0aSLydia Wang static const struct hda_verb vt3476_init_verbs[] = {
168943737e0aSLydia Wang 	/* Enable DMic 8/16/32K */
169043737e0aSLydia Wang 	{0x1, 0xF7B, 0x30},
169143737e0aSLydia Wang 	/* Enable Boost Volume backdoor */
169243737e0aSLydia Wang 	{0x1, 0xFB9, 0x20},
169343737e0aSLydia Wang 	/* Enable AOW-MW9 path */
169443737e0aSLydia Wang 	{0x1, 0xFB8, 0x10},
169543737e0aSLydia Wang 	{ }
169643737e0aSLydia Wang };
169743737e0aSLydia Wang 
169843737e0aSLydia Wang static void set_widgets_power_state_vt3476(struct hda_codec *codec)
169943737e0aSLydia Wang {
170043737e0aSLydia Wang 	struct via_spec *spec = codec->spec;
170143737e0aSLydia Wang 	int imux_is_smixer;
170243737e0aSLydia Wang 	unsigned int parm, parm2;
170343737e0aSLydia Wang 	/* MUX10 (1eh) = stereo mixer */
170443737e0aSLydia Wang 	imux_is_smixer =
170543737e0aSLydia Wang 	snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 4;
170643737e0aSLydia Wang 	/* inputs */
170743737e0aSLydia Wang 	/* PW 5/6/7 (29h/2ah/2bh) */
170843737e0aSLydia Wang 	parm = AC_PWRST_D3;
170943737e0aSLydia Wang 	set_pin_power_state(codec, 0x29, &parm);
171043737e0aSLydia Wang 	set_pin_power_state(codec, 0x2a, &parm);
171143737e0aSLydia Wang 	set_pin_power_state(codec, 0x2b, &parm);
171243737e0aSLydia Wang 	if (imux_is_smixer)
171343737e0aSLydia Wang 		parm = AC_PWRST_D0;
171443737e0aSLydia Wang 	/* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
171543737e0aSLydia Wang 	update_power_state(codec, 0x1e, parm);
171643737e0aSLydia Wang 	update_power_state(codec, 0x1f, parm);
171743737e0aSLydia Wang 	update_power_state(codec, 0x10, parm);
171843737e0aSLydia Wang 	update_power_state(codec, 0x11, parm);
171943737e0aSLydia Wang 
172043737e0aSLydia Wang 	/* outputs */
172143737e0aSLydia Wang 	/* PW3 (27h), MW3(37h), AOW3 (bh) */
172243737e0aSLydia Wang 	if (spec->codec_type == VT1705CF) {
172343737e0aSLydia Wang 		parm = AC_PWRST_D3;
172443737e0aSLydia Wang 		update_power_state(codec, 0x27, parm);
172543737e0aSLydia Wang 		update_power_state(codec, 0x37, parm);
172643737e0aSLydia Wang 	}	else {
172743737e0aSLydia Wang 		parm = AC_PWRST_D3;
172843737e0aSLydia Wang 		set_pin_power_state(codec, 0x27, &parm);
172943737e0aSLydia Wang 		update_power_state(codec, 0x37, parm);
173043737e0aSLydia Wang 	}
173143737e0aSLydia Wang 
173243737e0aSLydia Wang 	/* PW2 (26h), MW2(36h), AOW2 (ah) */
173343737e0aSLydia Wang 	parm = AC_PWRST_D3;
173443737e0aSLydia Wang 	set_pin_power_state(codec, 0x26, &parm);
173543737e0aSLydia Wang 	update_power_state(codec, 0x36, parm);
1736b3f6008fSTakashi Iwai 	if (smart51_enabled(codec)) {
173743737e0aSLydia Wang 		/* PW7(2bh), MW7(3bh), MUX7(1Bh) */
173843737e0aSLydia Wang 		set_pin_power_state(codec, 0x2b, &parm);
173943737e0aSLydia Wang 		update_power_state(codec, 0x3b, parm);
174043737e0aSLydia Wang 		update_power_state(codec, 0x1b, parm);
174143737e0aSLydia Wang 	}
174243737e0aSLydia Wang 	update_conv_power_state(codec, 0xa, parm, 2);
174343737e0aSLydia Wang 
174443737e0aSLydia Wang 	/* PW1 (25h), MW1(35h), AOW1 (9h) */
174543737e0aSLydia Wang 	parm = AC_PWRST_D3;
174643737e0aSLydia Wang 	set_pin_power_state(codec, 0x25, &parm);
174743737e0aSLydia Wang 	update_power_state(codec, 0x35, parm);
1748b3f6008fSTakashi Iwai 	if (smart51_enabled(codec)) {
174943737e0aSLydia Wang 		/* PW6(2ah), MW6(3ah), MUX6(1ah) */
175043737e0aSLydia Wang 		set_pin_power_state(codec, 0x2a, &parm);
175143737e0aSLydia Wang 		update_power_state(codec, 0x3a, parm);
175243737e0aSLydia Wang 		update_power_state(codec, 0x1a, parm);
175343737e0aSLydia Wang 	}
175443737e0aSLydia Wang 	update_conv_power_state(codec, 0x9, parm, 1);
175543737e0aSLydia Wang 
175643737e0aSLydia Wang 	/* PW4 (28h), MW4 (38h), MUX4(18h), AOW3(bh)/AOW0(8h) */
175743737e0aSLydia Wang 	parm = AC_PWRST_D3;
175843737e0aSLydia Wang 	set_pin_power_state(codec, 0x28, &parm);
175943737e0aSLydia Wang 	update_power_state(codec, 0x38, parm);
176043737e0aSLydia Wang 	update_power_state(codec, 0x18, parm);
1761b3f6008fSTakashi Iwai 	if (spec->gen.indep_hp_enabled)
176243737e0aSLydia Wang 		update_conv_power_state(codec, 0xb, parm, 3);
176343737e0aSLydia Wang 	parm2 = parm; /* for pin 0x0b */
176443737e0aSLydia Wang 
176543737e0aSLydia Wang 	/* PW0 (24h), MW0(34h), MW9(3fh), AOW0 (8h) */
176643737e0aSLydia Wang 	parm = AC_PWRST_D3;
176743737e0aSLydia Wang 	set_pin_power_state(codec, 0x24, &parm);
176843737e0aSLydia Wang 	update_power_state(codec, 0x34, parm);
1769b3f6008fSTakashi Iwai 	if (!spec->gen.indep_hp_enabled && parm2 != AC_PWRST_D3)
177043737e0aSLydia Wang 		parm = parm2;
177143737e0aSLydia Wang 	update_conv_power_state(codec, 0x8, parm, 0);
177243737e0aSLydia Wang 	/* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
177343737e0aSLydia Wang 	update_power_state(codec, 0x3f, imux_is_smixer ? AC_PWRST_D0 : parm);
177443737e0aSLydia Wang }
177543737e0aSLydia Wang 
177643737e0aSLydia Wang static int patch_vt3476(struct hda_codec *codec)
177743737e0aSLydia Wang {
177843737e0aSLydia Wang 	struct via_spec *spec;
177943737e0aSLydia Wang 	int err;
178043737e0aSLydia Wang 
178143737e0aSLydia Wang 	/* create a codec specific record */
178243737e0aSLydia Wang 	spec = via_new_spec(codec);
178343737e0aSLydia Wang 	if (spec == NULL)
178443737e0aSLydia Wang 		return -ENOMEM;
178543737e0aSLydia Wang 
1786b3f6008fSTakashi Iwai 	spec->gen.mixer_nid = 0x3f;
178743737e0aSLydia Wang 	add_secret_dac_path(codec);
178843737e0aSLydia Wang 
178943737e0aSLydia Wang 	/* automatic parse from the BIOS config */
179043737e0aSLydia Wang 	err = via_parse_auto_config(codec);
179143737e0aSLydia Wang 	if (err < 0) {
179243737e0aSLydia Wang 		via_free(codec);
179343737e0aSLydia Wang 		return err;
179443737e0aSLydia Wang 	}
179543737e0aSLydia Wang 
179643737e0aSLydia Wang 	spec->init_verbs[spec->num_iverbs++] = vt3476_init_verbs;
179743737e0aSLydia Wang 
179843737e0aSLydia Wang 	codec->patch_ops = via_patch_ops;
179943737e0aSLydia Wang 
180043737e0aSLydia Wang 	spec->set_widgets_power_state = set_widgets_power_state_vt3476;
180143737e0aSLydia Wang 
180243737e0aSLydia Wang 	return 0;
180343737e0aSLydia Wang }
180443737e0aSLydia Wang 
1805c577b8a1SJoseph Chan /*
1806c577b8a1SJoseph Chan  * patch entries
1807c577b8a1SJoseph Chan  */
180890dd48a1STakashi Iwai static const struct hda_codec_preset snd_hda_preset_via[] = {
18093218c178STakashi Iwai 	{ .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
18103218c178STakashi Iwai 	{ .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
18113218c178STakashi Iwai 	{ .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
18123218c178STakashi Iwai 	{ .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
18133218c178STakashi Iwai 	{ .id = 0x1106e710, .name = "VT1709 10-Ch",
1814ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18153218c178STakashi Iwai 	{ .id = 0x1106e711, .name = "VT1709 10-Ch",
1816ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18173218c178STakashi Iwai 	{ .id = 0x1106e712, .name = "VT1709 10-Ch",
1818ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18193218c178STakashi Iwai 	{ .id = 0x1106e713, .name = "VT1709 10-Ch",
1820ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18213218c178STakashi Iwai 	{ .id = 0x1106e714, .name = "VT1709 6-Ch",
1822ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18233218c178STakashi Iwai 	{ .id = 0x1106e715, .name = "VT1709 6-Ch",
1824ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18253218c178STakashi Iwai 	{ .id = 0x1106e716, .name = "VT1709 6-Ch",
1826ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18273218c178STakashi Iwai 	{ .id = 0x1106e717, .name = "VT1709 6-Ch",
1828ddd304d8STakashi Iwai 	  .patch = patch_vt1709},
18293218c178STakashi Iwai 	{ .id = 0x1106e720, .name = "VT1708B 8-Ch",
1830ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18313218c178STakashi Iwai 	{ .id = 0x1106e721, .name = "VT1708B 8-Ch",
1832ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18333218c178STakashi Iwai 	{ .id = 0x1106e722, .name = "VT1708B 8-Ch",
1834ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18353218c178STakashi Iwai 	{ .id = 0x1106e723, .name = "VT1708B 8-Ch",
1836ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18373218c178STakashi Iwai 	{ .id = 0x1106e724, .name = "VT1708B 4-Ch",
1838ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18393218c178STakashi Iwai 	{ .id = 0x1106e725, .name = "VT1708B 4-Ch",
1840ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18413218c178STakashi Iwai 	{ .id = 0x1106e726, .name = "VT1708B 4-Ch",
1842ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18433218c178STakashi Iwai 	{ .id = 0x1106e727, .name = "VT1708B 4-Ch",
1844ddd304d8STakashi Iwai 	  .patch = patch_vt1708B},
18453218c178STakashi Iwai 	{ .id = 0x11060397, .name = "VT1708S",
1846d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18473218c178STakashi Iwai 	{ .id = 0x11061397, .name = "VT1708S",
1848d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18493218c178STakashi Iwai 	{ .id = 0x11062397, .name = "VT1708S",
1850d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18513218c178STakashi Iwai 	{ .id = 0x11063397, .name = "VT1708S",
1852d949cac1SHarald Welte 	  .patch = patch_vt1708S},
1853bc92df7fSLydia Wang 	{ .id = 0x11064397, .name = "VT1705",
1854d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18553218c178STakashi Iwai 	{ .id = 0x11065397, .name = "VT1708S",
1856d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18573218c178STakashi Iwai 	{ .id = 0x11066397, .name = "VT1708S",
1858d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18593218c178STakashi Iwai 	{ .id = 0x11067397, .name = "VT1708S",
1860d949cac1SHarald Welte 	  .patch = patch_vt1708S},
18613218c178STakashi Iwai 	{ .id = 0x11060398, .name = "VT1702",
1862d949cac1SHarald Welte 	  .patch = patch_vt1702},
18633218c178STakashi Iwai 	{ .id = 0x11061398, .name = "VT1702",
1864d949cac1SHarald Welte 	  .patch = patch_vt1702},
18653218c178STakashi Iwai 	{ .id = 0x11062398, .name = "VT1702",
1866d949cac1SHarald Welte 	  .patch = patch_vt1702},
18673218c178STakashi Iwai 	{ .id = 0x11063398, .name = "VT1702",
1868d949cac1SHarald Welte 	  .patch = patch_vt1702},
18693218c178STakashi Iwai 	{ .id = 0x11064398, .name = "VT1702",
1870d949cac1SHarald Welte 	  .patch = patch_vt1702},
18713218c178STakashi Iwai 	{ .id = 0x11065398, .name = "VT1702",
1872d949cac1SHarald Welte 	  .patch = patch_vt1702},
18733218c178STakashi Iwai 	{ .id = 0x11066398, .name = "VT1702",
1874d949cac1SHarald Welte 	  .patch = patch_vt1702},
18753218c178STakashi Iwai 	{ .id = 0x11067398, .name = "VT1702",
1876d949cac1SHarald Welte 	  .patch = patch_vt1702},
1877eb7188caSLydia Wang 	{ .id = 0x11060428, .name = "VT1718S",
1878eb7188caSLydia Wang 	  .patch = patch_vt1718S},
1879eb7188caSLydia Wang 	{ .id = 0x11064428, .name = "VT1718S",
1880eb7188caSLydia Wang 	  .patch = patch_vt1718S},
1881bb3c6bfcSLydia Wang 	{ .id = 0x11060441, .name = "VT2020",
1882bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
1883bb3c6bfcSLydia Wang 	{ .id = 0x11064441, .name = "VT1828S",
1884bb3c6bfcSLydia Wang 	  .patch = patch_vt1718S},
1885f3db423dSLydia Wang 	{ .id = 0x11060433, .name = "VT1716S",
1886f3db423dSLydia Wang 	  .patch = patch_vt1716S},
1887f3db423dSLydia Wang 	{ .id = 0x1106a721, .name = "VT1716S",
1888f3db423dSLydia Wang 	  .patch = patch_vt1716S},
188925eaba2fSLydia Wang 	{ .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
189025eaba2fSLydia Wang 	{ .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
1891ab6734e7SLydia Wang 	{ .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
189236dd5c4aSLydia Wang 	{ .id = 0x11060440, .name = "VT1818S",
189336dd5c4aSLydia Wang 	  .patch = patch_vt1708S},
189411890956SLydia Wang 	{ .id = 0x11060446, .name = "VT1802",
189511890956SLydia Wang 		.patch = patch_vt2002P},
189611890956SLydia Wang 	{ .id = 0x11068446, .name = "VT1802",
189711890956SLydia Wang 		.patch = patch_vt2002P},
189843737e0aSLydia Wang 	{ .id = 0x11064760, .name = "VT1705CF",
189943737e0aSLydia Wang 		.patch = patch_vt3476},
19006121b84aSLydia Wang 	{ .id = 0x11064761, .name = "VT1708SCE",
19016121b84aSLydia Wang 		.patch = patch_vt3476},
19026121b84aSLydia Wang 	{ .id = 0x11064762, .name = "VT1808",
19036121b84aSLydia Wang 		.patch = patch_vt3476},
1904c577b8a1SJoseph Chan 	{} /* terminator */
1905c577b8a1SJoseph Chan };
19061289e9e8STakashi Iwai 
19071289e9e8STakashi Iwai MODULE_ALIAS("snd-hda-codec-id:1106*");
19081289e9e8STakashi Iwai 
19091289e9e8STakashi Iwai static struct hda_codec_preset_list via_list = {
19101289e9e8STakashi Iwai 	.preset = snd_hda_preset_via,
19111289e9e8STakashi Iwai 	.owner = THIS_MODULE,
19121289e9e8STakashi Iwai };
19131289e9e8STakashi Iwai 
19141289e9e8STakashi Iwai MODULE_LICENSE("GPL");
19151289e9e8STakashi Iwai MODULE_DESCRIPTION("VIA HD-audio codec");
19161289e9e8STakashi Iwai 
19171289e9e8STakashi Iwai static int __init patch_via_init(void)
19181289e9e8STakashi Iwai {
19191289e9e8STakashi Iwai 	return snd_hda_add_codec_preset(&via_list);
19201289e9e8STakashi Iwai }
19211289e9e8STakashi Iwai 
19221289e9e8STakashi Iwai static void __exit patch_via_exit(void)
19231289e9e8STakashi Iwai {
19241289e9e8STakashi Iwai 	snd_hda_delete_codec_preset(&via_list);
19251289e9e8STakashi Iwai }
19261289e9e8STakashi Iwai 
19271289e9e8STakashi Iwai module_init(patch_via_init)
19281289e9e8STakashi Iwai module_exit(patch_via_exit)
1929