blob: 785f7f5022a421e15c0d745028d7df691ed1f33e [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010052#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080053#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010054#include "hda_codec.h"
55#include "hda_local.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010056
Joseph Chanc577b8a2006-11-29 15:29:40 +010057/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080058#define VT1708_HP_PIN_NID 0x20
59#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010060
Harald Welted7426322008-09-15 22:43:23 +080061enum VIA_HDA_CODEC {
62 UNKNOWN = -1,
63 VT1708,
64 VT1709_10CH,
65 VT1709_6CH,
66 VT1708B_8CH,
67 VT1708B_4CH,
68 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080069 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080070 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080071 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080072 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080073 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080074 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080075 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080076 CODEC_TYPES,
77};
78
Lydia Wang118909562011-03-23 17:57:34 +080079#define VT2002P_COMPATIBLE(spec) \
80 ((spec)->codec_type == VT2002P ||\
81 (spec)->codec_type == VT1812 ||\
82 (spec)->codec_type == VT1802)
83
Takashi Iwai8e3679d2011-06-21 09:01:36 +020084#define MAX_NID_PATH_DEPTH 5
85
Takashi Iwai4a796162011-06-17 17:53:38 +020086struct nid_path {
87 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020088 hda_nid_t path[MAX_NID_PATH_DEPTH];
89 short idx[MAX_NID_PATH_DEPTH];
Takashi Iwai4a796162011-06-17 17:53:38 +020090};
91
Lydia Wang1f2e99f2009-10-10 19:08:17 +080092struct via_spec {
93 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +020094 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +080095 unsigned int num_mixers;
96
Takashi Iwai90dd48a2011-05-02 12:38:19 +020097 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +080098 unsigned int num_iverbs;
99
Takashi Iwai82673bc2011-06-17 16:24:21 +0200100 char stream_name_analog[32];
Takashi Iwai7eb56e842011-06-18 16:40:14 +0200101 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200102 const struct hda_pcm_stream *stream_analog_playback;
103 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800104
Takashi Iwai82673bc2011-06-17 16:24:21 +0200105 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200106 const struct hda_pcm_stream *stream_digital_playback;
107 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800108
109 /* playback */
110 struct hda_multi_out multiout;
111 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200112 hda_nid_t hp_dac_nid;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200113 int num_active_streams;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800114
Takashi Iwai4a796162011-06-17 17:53:38 +0200115 struct nid_path out_path[4];
116 struct nid_path hp_path;
117 struct nid_path hp_dep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200118 struct nid_path speaker_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200119
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800120 /* capture */
121 unsigned int num_adc_nids;
Takashi Iwaia766d0d2011-06-17 09:01:29 +0200122 hda_nid_t adc_nids[3];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800123 hda_nid_t mux_nids[3];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200124 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800125 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800126
127 /* capture source */
128 const struct hda_input_mux *input_mux;
129 unsigned int cur_mux[3];
130
131 /* PCM information */
132 struct hda_pcm pcm_rec[3];
133
134 /* dynamic controls, init_verbs and input_mux */
135 struct auto_pin_cfg autocfg;
136 struct snd_array kctls;
137 struct hda_input_mux private_imux[2];
138 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
139
140 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800141 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800142 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200143 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800144 enum VIA_HDA_CODEC codec_type;
145
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200146 /* smart51 setup */
147 unsigned int smart51_nums;
148 hda_nid_t smart51_pins[2];
149 int smart51_idxs[2];
150 const char *smart51_labels[2];
151 unsigned int smart51_enabled;
152
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800153 /* work to check hp jack state */
154 struct hda_codec *codec;
155 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200156 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800157 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800158
159 void (*set_widgets_power_state)(struct hda_codec *codec);
160
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800161 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200162 int num_loopbacks;
163 struct hda_amp_list loopback_list[8];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800164};
165
Lydia Wang0341ccd2011-03-22 16:25:03 +0800166static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100167static struct via_spec * via_new_spec(struct hda_codec *codec)
168{
169 struct via_spec *spec;
170
171 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
172 if (spec == NULL)
173 return NULL;
174
175 codec->spec = spec;
176 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800177 spec->codec_type = get_codec_type(codec);
178 /* VT1708BCE & VT1708S are almost same */
179 if (spec->codec_type == VT1708BCE)
180 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100181 return spec;
182}
183
Lydia Wang744ff5f2009-10-10 19:07:26 +0800184static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800185{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800186 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800187 u16 ven_id = vendor_id >> 16;
188 u16 dev_id = vendor_id & 0xffff;
189 enum VIA_HDA_CODEC codec_type;
190
191 /* get codec type */
192 if (ven_id != 0x1106)
193 codec_type = UNKNOWN;
194 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
195 codec_type = VT1708;
196 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
197 codec_type = VT1709_10CH;
198 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
199 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800200 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800201 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800202 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
203 codec_type = VT1708BCE;
204 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800205 codec_type = VT1708B_4CH;
206 else if ((dev_id & 0xfff) == 0x397
207 && (dev_id >> 12) < 8)
208 codec_type = VT1708S;
209 else if ((dev_id & 0xfff) == 0x398
210 && (dev_id >> 12) < 8)
211 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800212 else if ((dev_id & 0xfff) == 0x428
213 && (dev_id >> 12) < 8)
214 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800215 else if (dev_id == 0x0433 || dev_id == 0xa721)
216 codec_type = VT1716S;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +0800217 else if (dev_id == 0x0441 || dev_id == 0x4441)
218 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800219 else if (dev_id == 0x0438 || dev_id == 0x4438)
220 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800221 else if (dev_id == 0x0448)
222 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800223 else if (dev_id == 0x0440)
224 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800225 else if ((dev_id & 0xfff) == 0x446)
226 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800227 else
228 codec_type = UNKNOWN;
229 return codec_type;
230};
231
Lydia Wangec7e7e42011-03-24 12:43:44 +0800232#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800233#define VIA_HP_EVENT 0x01
234#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200235#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800236
Joseph Chanc577b8a2006-11-29 15:29:40 +0100237enum {
238 VIA_CTL_WIDGET_VOL,
239 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800240 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100241};
242
Takashi Iwaiada509e2011-06-20 15:40:19 +0200243static void analog_low_current_mode(struct hda_codec *codec);
244static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800245
246static void vt1708_start_hp_work(struct via_spec *spec)
247{
248 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
249 return;
250 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200251 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800252 if (!delayed_work_pending(&spec->vt1708_hp_work))
253 schedule_delayed_work(&spec->vt1708_hp_work,
254 msecs_to_jiffies(100));
255}
256
257static void vt1708_stop_hp_work(struct via_spec *spec)
258{
259 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
260 return;
261 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
262 && !is_aa_path_mute(spec->codec))
263 return;
264 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200265 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100266 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800267}
Lydia Wangf5271102009-10-10 19:07:35 +0800268
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800269static void set_widgets_power_state(struct hda_codec *codec)
270{
271 struct via_spec *spec = codec->spec;
272 if (spec->set_widgets_power_state)
273 spec->set_widgets_power_state(codec);
274}
Lydia Wang25eaba22009-10-10 19:08:43 +0800275
Lydia Wangf5271102009-10-10 19:07:35 +0800276static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
277 struct snd_ctl_elem_value *ucontrol)
278{
279 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
280 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
281
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800282 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200283 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800284 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
285 if (is_aa_path_mute(codec))
286 vt1708_start_hp_work(codec->spec);
287 else
288 vt1708_stop_hp_work(codec->spec);
289 }
Lydia Wangf5271102009-10-10 19:07:35 +0800290 return change;
291}
292
293/* modify .put = snd_hda_mixer_amp_switch_put */
294#define ANALOG_INPUT_MUTE \
295 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
296 .name = NULL, \
297 .index = 0, \
298 .info = snd_hda_mixer_amp_switch_info, \
299 .get = snd_hda_mixer_amp_switch_get, \
300 .put = analog_input_switch_put, \
301 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
302
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200303static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100304 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
305 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800306 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100307};
308
Lydia Wangab6734e2009-10-10 19:08:46 +0800309
Joseph Chanc577b8a2006-11-29 15:29:40 +0100310/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200311static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
312 const struct snd_kcontrol_new *tmpl,
313 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100314{
315 struct snd_kcontrol_new *knew;
316
Takashi Iwai603c4012008-07-30 15:01:44 +0200317 snd_array_init(&spec->kctls, sizeof(*knew), 32);
318 knew = snd_array_new(&spec->kctls);
319 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200320 return NULL;
321 *knew = *tmpl;
322 if (!name)
323 name = tmpl->name;
324 if (name) {
325 knew->name = kstrdup(name, GFP_KERNEL);
326 if (!knew->name)
327 return NULL;
328 }
329 return knew;
330}
331
332static int __via_add_control(struct via_spec *spec, int type, const char *name,
333 int idx, unsigned long val)
334{
335 struct snd_kcontrol_new *knew;
336
337 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
338 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100339 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200340 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100341 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100342 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100343 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100344 return 0;
345}
346
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200347#define via_add_control(spec, type, name, val) \
348 __via_add_control(spec, type, name, 0, val)
349
Takashi Iwai291c9e32011-06-17 16:15:26 +0200350#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100351
Takashi Iwai603c4012008-07-30 15:01:44 +0200352static void via_free_kctls(struct hda_codec *codec)
353{
354 struct via_spec *spec = codec->spec;
355
356 if (spec->kctls.list) {
357 struct snd_kcontrol_new *kctl = spec->kctls.list;
358 int i;
359 for (i = 0; i < spec->kctls.used; i++)
360 kfree(kctl[i].name);
361 }
362 snd_array_free(&spec->kctls);
363}
364
Joseph Chanc577b8a2006-11-29 15:29:40 +0100365/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800366static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200367 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100368{
369 char name[32];
370 int err;
371
372 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200373 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100374 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
375 if (err < 0)
376 return err;
377 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200378 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100379 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
380 if (err < 0)
381 return err;
382 return 0;
383}
384
Takashi Iwai5d417622011-06-20 11:32:27 +0200385/* return the index of the given widget nid as the source of mux;
386 * return -1 if not found;
387 * if num_conns is non-NULL, set the total number of connections
388 */
389static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux,
390 hda_nid_t nid, int *num_conns)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100391{
Takashi Iwai5d417622011-06-20 11:32:27 +0200392 hda_nid_t conn[HDA_MAX_NUM_INPUTS];
393 int i, nums;
394
395 nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
396 if (num_conns)
397 *num_conns = nums;
398 for (i = 0; i < nums; i++)
399 if (conn[i] == nid)
400 return i;
401 return -1;
402}
403
404#define get_connection_index(codec, mux, nid) \
405 __get_connection_index(codec, mux, nid, NULL)
406
Takashi Iwai8df2a312011-06-21 11:48:29 +0200407static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
408 unsigned int mask)
409{
410 unsigned int caps = get_wcaps(codec, nid);
411 if (dir == HDA_INPUT)
412 caps &= AC_WCAP_IN_AMP;
413 else
414 caps &= AC_WCAP_OUT_AMP;
415 if (!caps)
416 return false;
417 if (query_amp_caps(codec, nid, dir) & mask)
418 return true;
419 return false;
420}
421
422#define have_vol_or_mute(codec, nid, dir) \
423 check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)
424
Takashi Iwai5d417622011-06-20 11:32:27 +0200425/* unmute input amp and select the specificed source */
426static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid,
427 hda_nid_t src, hda_nid_t mix)
428{
429 int idx, num_conns;
430
431 idx = __get_connection_index(codec, nid, src, &num_conns);
432 if (idx < 0)
433 return;
434
435 /* select the route explicitly when multiple connections exist */
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200436 if (num_conns > 1 &&
437 get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
Lydia Wang377ff312009-10-10 19:08:55 +0800438 snd_hda_codec_write(codec, nid, 0,
Takashi Iwai5d417622011-06-20 11:32:27 +0200439 AC_VERB_SET_CONNECT_SEL, idx);
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200440
Takashi Iwai5d417622011-06-20 11:32:27 +0200441 /* unmute if the input amp is present */
Takashi Iwai8df2a312011-06-21 11:48:29 +0200442 if (have_vol_or_mute(codec, nid, HDA_INPUT))
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200443 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
444 AMP_IN_UNMUTE(idx));
445
446 /* unmute the src output */
Takashi Iwai8df2a312011-06-21 11:48:29 +0200447 if (have_vol_or_mute(codec, src, HDA_OUTPUT))
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200448 snd_hda_codec_write(codec, src, 0, AC_VERB_SET_AMP_GAIN_MUTE,
449 AMP_OUT_UNMUTE);
Takashi Iwai5d417622011-06-20 11:32:27 +0200450
451 /* unmute AA-path if present */
Takashi Iwai8df2a312011-06-21 11:48:29 +0200452 if (!mix || mix == src)
Takashi Iwai5d417622011-06-20 11:32:27 +0200453 return;
454 idx = __get_connection_index(codec, nid, mix, NULL);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200455 if (idx >= 0 && have_vol_or_mute(codec, nid, HDA_INPUT))
Takashi Iwai5d417622011-06-20 11:32:27 +0200456 snd_hda_codec_write(codec, nid, 0,
457 AC_VERB_SET_AMP_GAIN_MUTE,
458 AMP_IN_UNMUTE(idx));
459}
460
461/* set the given pin as output */
462static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
463 int pin_type)
464{
465 if (!pin)
466 return;
467 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
468 pin_type);
469 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
470 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200471 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100472}
473
Takashi Iwai5d417622011-06-20 11:32:27 +0200474static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin,
475 int pin_type, struct nid_path *path)
476{
477 struct via_spec *spec = codec->spec;
478 unsigned int caps;
479 hda_nid_t nid;
480 int i;
481
482 if (!pin)
483 return;
484
485 init_output_pin(codec, pin, pin_type);
486 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
487 if (caps & AC_AMPCAP_MUTE) {
488 unsigned int val;
489 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
490 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
491 AMP_OUT_MUTE | val);
492 }
493
494 /* initialize the output path */
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200495 for (i = path->depth - 1; i > 0; i--) {
496 nid = path->path[i - 1];
497 unmute_and_select(codec, path->path[i], nid, spec->aa_mix_nid);
Takashi Iwai5d417622011-06-20 11:32:27 +0200498 }
499}
500
Joseph Chanc577b8a2006-11-29 15:29:40 +0100501
502static void via_auto_init_multi_out(struct hda_codec *codec)
503{
504 struct via_spec *spec = codec->spec;
505 int i;
506
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200507 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwai5d417622011-06-20 11:32:27 +0200508 via_auto_init_output(codec, spec->autocfg.line_out_pins[i],
509 PIN_OUT, &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100510}
511
512static void via_auto_init_hp_out(struct hda_codec *codec)
513{
514 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100515
Takashi Iwai5d417622011-06-20 11:32:27 +0200516 if (spec->hp_dac_nid)
517 via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
518 &spec->hp_path);
519 else
520 via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
521 &spec->hp_dep_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100522}
523
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200524static void via_auto_init_speaker_out(struct hda_codec *codec)
525{
526 struct via_spec *spec = codec->spec;
527
528 if (spec->autocfg.speaker_outs)
529 via_auto_init_output(codec, spec->autocfg.speaker_pins[0],
530 PIN_OUT, &spec->speaker_path);
531}
532
Takashi Iwaif4a78282011-06-17 18:46:48 +0200533static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200534
Joseph Chanc577b8a2006-11-29 15:29:40 +0100535static void via_auto_init_analog_input(struct hda_codec *codec)
536{
537 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200538 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200539 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200540 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200541 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100542
Takashi Iwai096a8852011-06-20 12:09:02 +0200543 /* init ADCs */
544 for (i = 0; i < spec->num_adc_nids; i++) {
545 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
546 AC_VERB_SET_AMP_GAIN_MUTE,
547 AMP_IN_UNMUTE(0));
548 }
549
550 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200551 for (i = 0; i < cfg->num_inputs; i++) {
552 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200553 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200554 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100555 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200556 ctl = PIN_VREF50;
557 else
558 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100559 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200560 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100561 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200562
563 /* init input-src */
564 for (i = 0; i < spec->num_adc_nids; i++) {
565 const struct hda_input_mux *imux = spec->input_mux;
566 if (!imux || !spec->mux_nids[i])
567 continue;
568 snd_hda_codec_write(codec, spec->mux_nids[i], 0,
569 AC_VERB_SET_CONNECT_SEL,
570 imux->items[spec->cur_mux[i]].index);
571 }
572
573 /* init aa-mixer */
574 if (!spec->aa_mix_nid)
575 return;
576 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
577 ARRAY_SIZE(conn));
578 for (i = 0; i < num_conns; i++) {
579 unsigned int caps = get_wcaps(codec, conn[i]);
580 if (get_wcaps_type(caps) == AC_WID_PIN)
581 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
582 AC_VERB_SET_AMP_GAIN_MUTE,
583 AMP_IN_MUTE(i));
584 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100585}
Lydia Wangf5271102009-10-10 19:07:35 +0800586
587static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
588 unsigned int *affected_parm)
589{
590 unsigned parm;
591 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
592 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
593 >> AC_DEFCFG_MISC_SHIFT
594 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800595 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200596 unsigned present = 0;
597
598 no_presence |= spec->no_pin_power_ctl;
599 if (!no_presence)
600 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200601 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800602 || ((no_presence || present)
603 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800604 *affected_parm = AC_PWRST_D0; /* if it's connected */
605 parm = AC_PWRST_D0;
606 } else
607 parm = AC_PWRST_D3;
608
609 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
610}
611
Takashi Iwai24088a52011-06-17 16:59:21 +0200612static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
613 struct snd_ctl_elem_info *uinfo)
614{
615 static const char * const texts[] = {
616 "Disabled", "Enabled"
617 };
618
619 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
620 uinfo->count = 1;
621 uinfo->value.enumerated.items = 2;
622 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
623 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
624 strcpy(uinfo->value.enumerated.name,
625 texts[uinfo->value.enumerated.item]);
626 return 0;
627}
628
629static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
630 struct snd_ctl_elem_value *ucontrol)
631{
632 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
633 struct via_spec *spec = codec->spec;
634 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
635 return 0;
636}
637
638static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
639 struct snd_ctl_elem_value *ucontrol)
640{
641 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
642 struct via_spec *spec = codec->spec;
643 unsigned int val = !ucontrol->value.enumerated.item[0];
644
645 if (val == spec->no_pin_power_ctl)
646 return 0;
647 spec->no_pin_power_ctl = val;
648 set_widgets_power_state(codec);
649 return 1;
650}
651
652static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
653 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
654 .name = "Dynamic Power-Control",
655 .info = via_pin_power_ctl_info,
656 .get = via_pin_power_ctl_get,
657 .put = via_pin_power_ctl_put,
658};
659
660
Joseph Chanc577b8a2006-11-29 15:29:40 +0100661/*
662 * input MUX handling
663 */
664static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
665 struct snd_ctl_elem_info *uinfo)
666{
667 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
668 struct via_spec *spec = codec->spec;
669 return snd_hda_input_mux_info(spec->input_mux, uinfo);
670}
671
672static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
673 struct snd_ctl_elem_value *ucontrol)
674{
675 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
676 struct via_spec *spec = codec->spec;
677 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
678
679 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
680 return 0;
681}
682
683static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
684 struct snd_ctl_elem_value *ucontrol)
685{
686 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
687 struct via_spec *spec = codec->spec;
688 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800689 int ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100690
Takashi Iwai337b9d02009-07-07 18:18:59 +0200691 if (!spec->mux_nids[adc_idx])
692 return -EINVAL;
Lydia Wanga80e6e32009-10-10 19:07:55 +0800693 /* switch to D0 beofre change index */
694 if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
695 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
696 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
697 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800698
699 ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
700 spec->mux_nids[adc_idx],
701 &spec->cur_mux[adc_idx]);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800702 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800703 set_widgets_power_state(codec);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800704
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800705 return ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100706}
707
Harald Welte0aa62ae2008-09-09 15:58:27 +0800708static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
709 struct snd_ctl_elem_info *uinfo)
710{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200711 static const char * const texts[] = { "OFF", "ON" };
712
713 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
714 uinfo->count = 1;
715 uinfo->value.enumerated.items = 2;
716 if (uinfo->value.enumerated.item >= 2)
717 uinfo->value.enumerated.item = 1;
718 strcpy(uinfo->value.enumerated.name,
719 texts[uinfo->value.enumerated.item]);
720 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800721}
722
723static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
724 struct snd_ctl_elem_value *ucontrol)
725{
726 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800727 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800728
Takashi Iwaiece8d042011-06-19 16:24:21 +0200729 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800730 return 0;
731}
732
Harald Welte0aa62ae2008-09-09 15:58:27 +0800733static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
734 struct snd_ctl_elem_value *ucontrol)
735{
736 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
737 struct via_spec *spec = codec->spec;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200738 hda_nid_t nid, src;
739 int i, idx, num_conns;
740 struct nid_path *path;
741
742 spec->hp_independent_mode = !!ucontrol->value.enumerated.item[0];
743 if (spec->hp_independent_mode)
744 path = &spec->hp_path;
745 else
746 path = &spec->hp_dep_path;
747
748 /* re-route the output path */
749 for (i = path->depth - 1; i > 0; i--) {
750 nid = path->path[i];
751 src = path->path[i - 1];
752 idx = __get_connection_index(codec, nid, src, &num_conns);
753 if (idx < 0)
754 continue;
755 if (num_conns > 1 &&
756 get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
757 snd_hda_codec_write(codec, nid, 0,
758 AC_VERB_SET_CONNECT_SEL, idx);
759 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800760
Lydia Wangce0e5a92011-03-22 16:22:37 +0800761 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800762 set_widgets_power_state(codec);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800763 return 0;
764}
765
Takashi Iwaiece8d042011-06-19 16:24:21 +0200766static const struct snd_kcontrol_new via_hp_mixer = {
767 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
768 .name = "Independent HP",
769 .info = via_independent_hp_info,
770 .get = via_independent_hp_get,
771 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800772};
773
Takashi Iwai3d83e572010-04-14 14:36:23 +0200774static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100775{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200776 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100777 struct snd_kcontrol_new *knew;
778 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100779
Takashi Iwaiece8d042011-06-19 16:24:21 +0200780 nid = spec->autocfg.hp_pins[0];
781 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200782 if (knew == NULL)
783 return -ENOMEM;
784
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100785 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100786
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100787 return 0;
788}
789
Lydia Wang1564b282009-10-10 19:07:52 +0800790static void notify_aa_path_ctls(struct hda_codec *codec)
791{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200792 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800793 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800794
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200795 for (i = 0; i < spec->smart51_nums; i++) {
796 struct snd_kcontrol *ctl;
797 struct snd_ctl_elem_id id;
798 memset(&id, 0, sizeof(id));
799 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
800 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800801 ctl = snd_hda_find_mixer_ctl(codec, id.name);
802 if (ctl)
803 snd_ctl_notify(codec->bus->card,
804 SNDRV_CTL_EVENT_MASK_VALUE,
805 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800806 }
807}
808
809static void mute_aa_path(struct hda_codec *codec, int mute)
810{
811 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200812 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800813 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200814
Lydia Wang1564b282009-10-10 19:07:52 +0800815 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200816 for (i = 0; i < spec->smart51_nums; i++) {
817 if (spec->smart51_idxs[i] < 0)
818 continue;
819 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
820 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800821 HDA_AMP_MUTE, val);
822 }
823}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200824
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200825static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
826{
827 struct via_spec *spec = codec->spec;
828 int i;
829
830 for (i = 0; i < spec->smart51_nums; i++)
831 if (spec->smart51_pins[i] == pin)
832 return true;
833 return false;
834}
835
Lydia Wang1564b282009-10-10 19:07:52 +0800836static int via_smart51_info(struct snd_kcontrol *kcontrol,
837 struct snd_ctl_elem_info *uinfo)
838{
839 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
840 uinfo->count = 1;
841 uinfo->value.integer.min = 0;
842 uinfo->value.integer.max = 1;
843 return 0;
844}
845
846static int via_smart51_get(struct snd_kcontrol *kcontrol,
847 struct snd_ctl_elem_value *ucontrol)
848{
849 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
850 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800851 int on = 1;
852 int i;
853
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200854 for (i = 0; i < spec->smart51_nums; i++) {
855 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwaif4a78282011-06-17 18:46:48 +0200856 unsigned int ctl;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200857 ctl = snd_hda_codec_read(codec, nid, 0,
858 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200859 if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
860 on = 0;
Lydia Wang1564b282009-10-10 19:07:52 +0800861 }
862 *ucontrol->value.integer.value = on;
863 return 0;
864}
865
866static int via_smart51_put(struct snd_kcontrol *kcontrol,
867 struct snd_ctl_elem_value *ucontrol)
868{
869 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
870 struct via_spec *spec = codec->spec;
871 int out_in = *ucontrol->value.integer.value
872 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800873 int i;
874
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200875 for (i = 0; i < spec->smart51_nums; i++) {
876 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200877 unsigned int parm;
878
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200879 parm = snd_hda_codec_read(codec, nid, 0,
880 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
881 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
882 parm |= out_in;
883 snd_hda_codec_write(codec, nid, 0,
884 AC_VERB_SET_PIN_WIDGET_CONTROL,
885 parm);
886 if (out_in == AC_PINCTL_OUT_EN) {
887 mute_aa_path(codec, 1);
888 notify_aa_path_ctls(codec);
889 }
Lydia Wang1564b282009-10-10 19:07:52 +0800890 }
891 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800892 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800893 return 1;
894}
895
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200896static const struct snd_kcontrol_new via_smart51_mixer = {
897 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
898 .name = "Smart 5.1",
899 .count = 1,
900 .info = via_smart51_info,
901 .get = via_smart51_get,
902 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800903};
904
Takashi Iwaif4a78282011-06-17 18:46:48 +0200905static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100906{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200907 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100908
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200909 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800910 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200911 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100912 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100913 return 0;
914}
915
Takashi Iwaiada509e2011-06-20 15:40:19 +0200916/* check AA path's mute status */
917static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800918{
Lydia Wangf5271102009-10-10 19:07:35 +0800919 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200920 const struct hda_amp_list *p;
921 int i, ch, v;
922
923 for (i = 0; i < spec->num_loopbacks; i++) {
924 p = &spec->loopback_list[i];
925 for (ch = 0; ch < 2; ch++) {
926 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
927 p->idx);
928 if (!(v & HDA_AMP_MUTE) && v > 0)
929 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800930 }
931 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200932 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800933}
934
935/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200936static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800937{
938 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200939 bool enable;
940 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800941
Takashi Iwaiada509e2011-06-20 15:40:19 +0200942 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800943
944 /* decide low current mode's verb & parameter */
945 switch (spec->codec_type) {
946 case VT1708B_8CH:
947 case VT1708B_4CH:
948 verb = 0xf70;
949 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
950 break;
951 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800952 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800953 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800954 verb = 0xf73;
955 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
956 break;
957 case VT1702:
958 verb = 0xf73;
959 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
960 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800961 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800962 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800963 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800964 verb = 0xf93;
965 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
966 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800967 default:
968 return; /* other codecs are not supported */
969 }
970 /* send verb */
971 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
972}
973
Joseph Chanc577b8a2006-11-29 15:29:40 +0100974/*
975 * generic initialization of ADC, input mixers and output mixers
976 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200977static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800978 /* power down jack detect function */
979 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +0100980 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100981};
982
Takashi Iwaiada509e2011-06-20 15:40:19 +0200983static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e842011-06-18 16:40:14 +0200984{
Takashi Iwaiada509e2011-06-20 15:40:19 +0200985 struct via_spec *spec = codec->spec;
986
987 if (active)
988 spec->num_active_streams++;
989 else
990 spec->num_active_streams--;
991 analog_low_current_mode(codec);
Takashi Iwai7eb56e842011-06-18 16:40:14 +0200992}
993
Takashi Iwaiece8d042011-06-19 16:24:21 +0200994static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100995 struct hda_codec *codec,
996 struct snd_pcm_substream *substream)
997{
998 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200999 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001000
1001 if (!spec->hp_independent_mode)
1002 spec->multiout.hp_nid = spec->hp_dac_nid;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001003 set_stream_active(codec, true);
1004 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1005 hinfo);
1006 if (err < 0) {
1007 spec->multiout.hp_nid = 0;
1008 set_stream_active(codec, false);
1009 return err;
1010 }
1011 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001012}
1013
Takashi Iwaiece8d042011-06-19 16:24:21 +02001014static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001015 struct hda_codec *codec,
1016 struct snd_pcm_substream *substream)
1017{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001018 struct via_spec *spec = codec->spec;
1019
1020 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001021 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001022 return 0;
1023}
1024
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001025static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1026 struct hda_codec *codec,
1027 struct snd_pcm_substream *substream)
1028{
1029 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001030
Takashi Iwaiece8d042011-06-19 16:24:21 +02001031 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001032 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001033 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1034 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001035 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001036 return 0;
1037}
1038
1039static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1040 struct hda_codec *codec,
1041 struct snd_pcm_substream *substream)
1042{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001043 set_stream_active(codec, false);
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001044 return 0;
1045}
1046
1047static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1048 struct hda_codec *codec,
1049 unsigned int stream_tag,
1050 unsigned int format,
1051 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001052{
1053 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001054
Takashi Iwaiece8d042011-06-19 16:24:21 +02001055 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1056 format, substream);
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001057 vt1708_start_hp_work(spec);
1058 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001059}
1060
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001061static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1062 struct hda_codec *codec,
1063 unsigned int stream_tag,
1064 unsigned int format,
1065 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001066{
1067 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001068
Takashi Iwaiece8d042011-06-19 16:24:21 +02001069 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1070 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001071 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001072 return 0;
1073}
1074
1075static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1076 struct hda_codec *codec,
1077 struct snd_pcm_substream *substream)
1078{
1079 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001080
Takashi Iwaiece8d042011-06-19 16:24:21 +02001081 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001082 vt1708_stop_hp_work(spec);
1083 return 0;
1084}
1085
1086static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1087 struct hda_codec *codec,
1088 struct snd_pcm_substream *substream)
1089{
1090 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001091
Takashi Iwaiece8d042011-06-19 16:24:21 +02001092 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001093 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001094 return 0;
1095}
1096
Joseph Chanc577b8a2006-11-29 15:29:40 +01001097/*
1098 * Digital out
1099 */
1100static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1101 struct hda_codec *codec,
1102 struct snd_pcm_substream *substream)
1103{
1104 struct via_spec *spec = codec->spec;
1105 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1106}
1107
1108static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1109 struct hda_codec *codec,
1110 struct snd_pcm_substream *substream)
1111{
1112 struct via_spec *spec = codec->spec;
1113 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1114}
1115
Harald Welte5691ec72008-09-15 22:42:26 +08001116static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001117 struct hda_codec *codec,
1118 unsigned int stream_tag,
1119 unsigned int format,
1120 struct snd_pcm_substream *substream)
1121{
1122 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001123 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1124 stream_tag, format, substream);
1125}
Harald Welte5691ec72008-09-15 22:42:26 +08001126
Takashi Iwai9da29272009-05-07 16:31:14 +02001127static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1128 struct hda_codec *codec,
1129 struct snd_pcm_substream *substream)
1130{
1131 struct via_spec *spec = codec->spec;
1132 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001133 return 0;
1134}
1135
Joseph Chanc577b8a2006-11-29 15:29:40 +01001136/*
1137 * Analog capture
1138 */
1139static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1140 struct hda_codec *codec,
1141 unsigned int stream_tag,
1142 unsigned int format,
1143 struct snd_pcm_substream *substream)
1144{
1145 struct via_spec *spec = codec->spec;
1146
1147 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1148 stream_tag, 0, format);
1149 return 0;
1150}
1151
1152static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1153 struct hda_codec *codec,
1154 struct snd_pcm_substream *substream)
1155{
1156 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001157 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001158 return 0;
1159}
1160
Takashi Iwai9af74212011-06-18 16:17:45 +02001161static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001162 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001163 .channels_min = 2,
1164 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001165 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001166 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001167 .open = via_playback_multi_pcm_open,
1168 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001169 .prepare = via_playback_multi_pcm_prepare,
1170 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001171 },
1172};
1173
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001174static const struct hda_pcm_stream via_pcm_hp_playback = {
1175 .substreams = 1,
1176 .channels_min = 2,
1177 .channels_max = 2,
1178 /* NID is set in via_build_pcms */
1179 .ops = {
1180 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001181 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001182 .prepare = via_playback_hp_pcm_prepare,
1183 .cleanup = via_playback_hp_pcm_cleanup
1184 },
1185};
1186
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001187static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001188 .substreams = 1,
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001189 .channels_min = 2,
1190 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001191 /* NID is set in via_build_pcms */
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001192 /* We got noisy outputs on the right channel on VT1708 when
1193 * 24bit samples are used. Until any workaround is found,
1194 * disable the 24bit format, so far.
1195 */
1196 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1197 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001198 .open = via_playback_multi_pcm_open,
1199 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001200 .prepare = via_playback_multi_pcm_prepare,
1201 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001202 },
1203};
1204
Takashi Iwai9af74212011-06-18 16:17:45 +02001205static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001206 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001207 .channels_min = 2,
1208 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001209 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001210 .ops = {
1211 .prepare = via_capture_pcm_prepare,
1212 .cleanup = via_capture_pcm_cleanup
1213 },
1214};
1215
Takashi Iwai9af74212011-06-18 16:17:45 +02001216static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001217 .substreams = 1,
1218 .channels_min = 2,
1219 .channels_max = 2,
1220 /* NID is set in via_build_pcms */
1221 .ops = {
1222 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001223 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001224 .prepare = via_dig_playback_pcm_prepare,
1225 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001226 },
1227};
1228
Takashi Iwai9af74212011-06-18 16:17:45 +02001229static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001230 .substreams = 1,
1231 .channels_min = 2,
1232 .channels_max = 2,
1233};
1234
Takashi Iwai370bafb2011-06-20 12:47:45 +02001235/*
1236 * slave controls for virtual master
1237 */
1238static const char * const via_slave_vols[] = {
1239 "Front Playback Volume",
1240 "Surround Playback Volume",
1241 "Center Playback Volume",
1242 "LFE Playback Volume",
1243 "Side Playback Volume",
1244 "Headphone Playback Volume",
1245 "Speaker Playback Volume",
1246 NULL,
1247};
1248
1249static const char * const via_slave_sws[] = {
1250 "Front Playback Switch",
1251 "Surround Playback Switch",
1252 "Center Playback Switch",
1253 "LFE Playback Switch",
1254 "Side Playback Switch",
1255 "Headphone Playback Switch",
1256 "Speaker Playback Switch",
1257 NULL,
1258};
1259
Joseph Chanc577b8a2006-11-29 15:29:40 +01001260static int via_build_controls(struct hda_codec *codec)
1261{
1262 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001263 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001264 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001265
Takashi Iwai24088a52011-06-17 16:59:21 +02001266 if (spec->set_widgets_power_state)
1267 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1268 return -ENOMEM;
1269
Joseph Chanc577b8a2006-11-29 15:29:40 +01001270 for (i = 0; i < spec->num_mixers; i++) {
1271 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1272 if (err < 0)
1273 return err;
1274 }
1275
1276 if (spec->multiout.dig_out_nid) {
1277 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001278 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001279 spec->multiout.dig_out_nid);
1280 if (err < 0)
1281 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001282 err = snd_hda_create_spdif_share_sw(codec,
1283 &spec->multiout);
1284 if (err < 0)
1285 return err;
1286 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001287 }
1288 if (spec->dig_in_nid) {
1289 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1290 if (err < 0)
1291 return err;
1292 }
Lydia Wang17314372009-10-10 19:07:37 +08001293
Takashi Iwai370bafb2011-06-20 12:47:45 +02001294 /* if we have no master control, let's create it */
1295 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1296 unsigned int vmaster_tlv[4];
1297 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1298 HDA_OUTPUT, vmaster_tlv);
1299 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1300 vmaster_tlv, via_slave_vols);
1301 if (err < 0)
1302 return err;
1303 }
1304 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1305 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1306 NULL, via_slave_sws);
1307 if (err < 0)
1308 return err;
1309 }
1310
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001311 /* assign Capture Source enums to NID */
1312 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1313 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001314 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001315 if (err < 0)
1316 return err;
1317 }
1318
Lydia Wang17314372009-10-10 19:07:37 +08001319 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001320 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001321 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001322
Takashi Iwai603c4012008-07-30 15:01:44 +02001323 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001324 return 0;
1325}
1326
1327static int via_build_pcms(struct hda_codec *codec)
1328{
1329 struct via_spec *spec = codec->spec;
1330 struct hda_pcm *info = spec->pcm_rec;
1331
1332 codec->num_pcms = 1;
1333 codec->pcm_info = info;
1334
Takashi Iwai82673bc2011-06-17 16:24:21 +02001335 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1336 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001337 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001338
1339 if (!spec->stream_analog_playback)
1340 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001341 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001342 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001343 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1344 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001345 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1346 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001347
1348 if (!spec->stream_analog_capture)
1349 spec->stream_analog_capture = &via_pcm_analog_capture;
1350 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1351 *spec->stream_analog_capture;
1352 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1353 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1354 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001355
1356 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1357 codec->num_pcms++;
1358 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001359 snprintf(spec->stream_name_digital,
1360 sizeof(spec->stream_name_digital),
1361 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001362 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001363 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001364 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001365 if (!spec->stream_digital_playback)
1366 spec->stream_digital_playback =
1367 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001368 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001369 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001370 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1371 spec->multiout.dig_out_nid;
1372 }
1373 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001374 if (!spec->stream_digital_capture)
1375 spec->stream_digital_capture =
1376 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001377 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001378 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001379 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1380 spec->dig_in_nid;
1381 }
1382 }
1383
Takashi Iwaiece8d042011-06-19 16:24:21 +02001384 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001385 codec->num_pcms++;
1386 info++;
1387 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1388 "%s HP", codec->chip_name);
1389 info->name = spec->stream_name_hp;
1390 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1391 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001392 spec->hp_dac_nid;
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001393 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001394 return 0;
1395}
1396
1397static void via_free(struct hda_codec *codec)
1398{
1399 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001400
1401 if (!spec)
1402 return;
1403
Takashi Iwai603c4012008-07-30 15:01:44 +02001404 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001405 vt1708_stop_hp_work(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001406 kfree(codec->spec);
1407}
1408
Takashi Iwai64be2852011-06-17 16:51:39 +02001409/* mute/unmute outputs */
1410static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1411 hda_nid_t *pins, bool mute)
1412{
1413 int i;
1414 for (i = 0; i < num_pins; i++)
1415 snd_hda_codec_write(codec, pins[i], 0,
1416 AC_VERB_SET_PIN_WIDGET_CONTROL,
1417 mute ? 0 : PIN_OUT);
1418}
1419
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001420/* mute internal speaker if line-out is plugged */
1421static void via_line_automute(struct hda_codec *codec, int present)
1422{
1423 struct via_spec *spec = codec->spec;
1424
1425 if (!spec->autocfg.speaker_outs)
1426 return;
1427 if (!present)
1428 present = snd_hda_jack_detect(codec,
1429 spec->autocfg.line_out_pins[0]);
1430 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1431 spec->autocfg.speaker_pins,
1432 present);
1433}
1434
Harald Welte69e52a82008-09-09 15:57:32 +08001435/* mute internal speaker if HP is plugged */
1436static void via_hp_automute(struct hda_codec *codec)
1437{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001438 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001439 struct via_spec *spec = codec->spec;
1440
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001441 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
1442 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai64be2852011-06-17 16:51:39 +02001443 toggle_output_mutes(codec, spec->autocfg.line_outs,
1444 spec->autocfg.line_out_pins,
1445 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001446 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001447 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001448}
1449
Harald Welte69e52a82008-09-09 15:57:32 +08001450static void via_gpio_control(struct hda_codec *codec)
1451{
1452 unsigned int gpio_data;
1453 unsigned int vol_counter;
1454 unsigned int vol;
1455 unsigned int master_vol;
1456
1457 struct via_spec *spec = codec->spec;
1458
1459 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1460 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1461
1462 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1463 0xF84, 0) & 0x3F0000) >> 16;
1464
1465 vol = vol_counter & 0x1F;
1466 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1467 AC_VERB_GET_AMP_GAIN_MUTE,
1468 AC_AMP_GET_INPUT);
1469
1470 if (gpio_data == 0x02) {
1471 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001472 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1473 AC_VERB_SET_PIN_WIDGET_CONTROL,
1474 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001475 if (vol_counter & 0x20) {
1476 /* decrease volume */
1477 if (vol > master_vol)
1478 vol = master_vol;
1479 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1480 0, HDA_AMP_VOLMASK,
1481 master_vol-vol);
1482 } else {
1483 /* increase volume */
1484 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1485 HDA_AMP_VOLMASK,
1486 ((master_vol+vol) > 0x2A) ? 0x2A :
1487 (master_vol+vol));
1488 }
1489 } else if (!(gpio_data & 0x02)) {
1490 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001491 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1492 AC_VERB_SET_PIN_WIDGET_CONTROL,
1493 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001494 }
1495}
1496
1497/* unsolicited event for jack sensing */
1498static void via_unsol_event(struct hda_codec *codec,
1499 unsigned int res)
1500{
1501 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001502
Lydia Wanga34df192009-10-10 19:08:01 +08001503 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001504 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001505
1506 res &= ~VIA_JACK_EVENT;
1507
1508 if (res == VIA_HP_EVENT)
1509 via_hp_automute(codec);
1510 else if (res == VIA_GPIO_EVENT)
1511 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001512 else if (res == VIA_LINE_EVENT)
1513 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001514}
1515
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001516#ifdef SND_HDA_NEEDS_RESUME
1517static int via_suspend(struct hda_codec *codec, pm_message_t state)
1518{
1519 struct via_spec *spec = codec->spec;
1520 vt1708_stop_hp_work(spec);
1521 return 0;
1522}
1523#endif
1524
Takashi Iwaicb53c622007-08-10 17:21:45 +02001525#ifdef CONFIG_SND_HDA_POWER_SAVE
1526static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1527{
1528 struct via_spec *spec = codec->spec;
1529 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1530}
1531#endif
1532
Joseph Chanc577b8a2006-11-29 15:29:40 +01001533/*
1534 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001535
1536static int via_init(struct hda_codec *codec);
1537
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001538static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001539 .build_controls = via_build_controls,
1540 .build_pcms = via_build_pcms,
1541 .init = via_init,
1542 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001543 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001544#ifdef SND_HDA_NEEDS_RESUME
1545 .suspend = via_suspend,
1546#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001547#ifdef CONFIG_SND_HDA_POWER_SAVE
1548 .check_power_status = via_check_power_status,
1549#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001550};
1551
Takashi Iwai4a796162011-06-17 17:53:38 +02001552static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001553{
Takashi Iwai4a796162011-06-17 17:53:38 +02001554 struct via_spec *spec = codec->spec;
1555 int i;
1556
1557 for (i = 0; i < spec->multiout.num_dacs; i++) {
1558 if (spec->multiout.dac_nids[i] == dac)
1559 return false;
1560 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001561 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001562 return false;
1563 return true;
1564}
1565
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001566static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001567 hda_nid_t target_dac, struct nid_path *path,
1568 int depth, int wid_type)
1569{
1570 hda_nid_t conn[8];
1571 int i, nums;
1572
1573 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1574 for (i = 0; i < nums; i++) {
1575 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1576 continue;
1577 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001578 path->path[0] = conn[i];
1579 path->idx[0] = i;
1580 path->depth = 1;
Takashi Iwai4a796162011-06-17 17:53:38 +02001581 return true;
1582 }
1583 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001584 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001585 return false;
1586 for (i = 0; i < nums; i++) {
1587 unsigned int type;
1588 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1589 if (type == AC_WID_AUD_OUT ||
1590 (wid_type != -1 && type != wid_type))
1591 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001592 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai4a796162011-06-17 17:53:38 +02001593 path, depth + 1, AC_WID_AUD_SEL)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001594 path->path[path->depth] = conn[i];
1595 path->idx[path->depth] = i;
1596 path->depth++;
Takashi Iwai4a796162011-06-17 17:53:38 +02001597 return true;
1598 }
1599 }
1600 return false;
1601}
1602
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001603static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1604 hda_nid_t target_dac, struct nid_path *path)
1605{
1606 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1607 path->path[path->depth] = nid;
1608 path->depth++;
1609 return true;
1610 }
1611 return false;
1612}
1613
Takashi Iwai4a796162011-06-17 17:53:38 +02001614static int via_auto_fill_dac_nids(struct hda_codec *codec)
1615{
1616 struct via_spec *spec = codec->spec;
1617 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001618 int i;
1619 hda_nid_t nid;
1620
Joseph Chanc577b8a2006-11-29 15:29:40 +01001621 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001622 spec->multiout.num_dacs = cfg->line_outs;
1623 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001624 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001625 if (!nid)
1626 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001627 if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
1628 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001629 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001630 return 0;
1631}
1632
Takashi Iwai4a796162011-06-17 17:53:38 +02001633static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
1634 hda_nid_t pin, hda_nid_t dac, int chs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001635{
Takashi Iwai4a796162011-06-17 17:53:38 +02001636 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001637 char name[32];
Takashi Iwai4a796162011-06-17 17:53:38 +02001638 hda_nid_t nid;
1639 int err;
1640
Takashi Iwai8df2a312011-06-21 11:48:29 +02001641 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001642 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001643 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001644 nid = pin;
1645 else
1646 nid = 0;
1647 if (nid) {
1648 sprintf(name, "%s Playback Volume", pfx);
1649 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001650 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001651 if (err < 0)
1652 return err;
1653 }
1654
Takashi Iwai8df2a312011-06-21 11:48:29 +02001655 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001656 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001657 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001658 nid = pin;
1659 else
1660 nid = 0;
1661 if (nid) {
1662 sprintf(name, "%s Playback Switch", pfx);
1663 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1664 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1665 if (err < 0)
1666 return err;
1667 }
1668 return 0;
1669}
1670
Takashi Iwaif4a78282011-06-17 18:46:48 +02001671static void mangle_smart51(struct hda_codec *codec)
1672{
1673 struct via_spec *spec = codec->spec;
1674 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001675 struct auto_pin_cfg_item *ins = cfg->inputs;
1676 int i, j, nums, attr;
1677 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001678
Takashi Iwai0f98c242011-06-21 12:51:33 +02001679 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1680 nums = 0;
1681 for (i = 0; i < cfg->num_inputs; i++) {
1682 unsigned int def;
1683 if (ins[i].type > AUTO_PIN_LINE_IN)
1684 continue;
1685 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1686 if (snd_hda_get_input_pin_attr(def) != attr)
1687 continue;
1688 for (j = 0; j < nums; j++)
1689 if (ins[pins[j]].type < ins[i].type) {
1690 memmove(pins + j + 1, pins + j,
1691 (nums - j - 1) * sizeof(int));
1692 break;
1693 }
1694 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001695 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001696 }
1697 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001698 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001699 for (i = 0; i < nums; i++) {
1700 hda_nid_t pin = ins[pins[i]].pin;
1701 spec->smart51_pins[spec->smart51_nums++] = pin;
1702 cfg->line_out_pins[cfg->line_outs++] = pin;
1703 if (cfg->line_outs == 3)
1704 break;
1705 }
1706 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001707 }
1708}
1709
Takashi Iwai4a796162011-06-17 17:53:38 +02001710/* add playback controls from the parsed DAC table */
1711static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1712{
1713 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001714 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001715 static const char * const chname[4] = {
1716 "Front", "Surround", "C/LFE", "Side"
1717 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001718 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001719 int old_line_outs;
1720
1721 /* check smart51 */
1722 old_line_outs = cfg->line_outs;
1723 if (cfg->line_outs == 1)
1724 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001725
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001726 err = via_auto_fill_dac_nids(codec);
1727 if (err < 0)
1728 return err;
1729
Takashi Iwai4a796162011-06-17 17:53:38 +02001730 for (i = 0; i < cfg->line_outs; i++) {
1731 hda_nid_t pin, dac;
1732 pin = cfg->line_out_pins[i];
1733 dac = spec->multiout.dac_nids[i];
1734 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001735 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001736 if (i == HDA_CLFE) {
Takashi Iwai4a796162011-06-17 17:53:38 +02001737 err = create_ch_ctls(codec, "Center", pin, dac, 1);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001738 if (err < 0)
1739 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02001740 err = create_ch_ctls(codec, "LFE", pin, dac, 2);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001741 if (err < 0)
1742 return err;
1743 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001744 const char *pfx = chname[i];
1745 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1746 cfg->line_outs == 1)
1747 pfx = "Speaker";
1748 err = create_ch_ctls(codec, pfx, pin, dac, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001749 if (err < 0)
1750 return err;
1751 }
1752 }
1753
Takashi Iwai4a796162011-06-17 17:53:38 +02001754 idx = get_connection_index(codec, spec->aa_mix_nid,
1755 spec->multiout.dac_nids[0]);
1756 if (idx >= 0) {
1757 /* add control to mixer */
1758 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1759 "PCM Playback Volume",
1760 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1761 idx, HDA_INPUT));
1762 if (err < 0)
1763 return err;
1764 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1765 "PCM Playback Switch",
1766 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1767 idx, HDA_INPUT));
1768 if (err < 0)
1769 return err;
1770 }
1771
Takashi Iwaif4a78282011-06-17 18:46:48 +02001772 cfg->line_outs = old_line_outs;
1773
Joseph Chanc577b8a2006-11-29 15:29:40 +01001774 return 0;
1775}
1776
Takashi Iwai4a796162011-06-17 17:53:38 +02001777static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001778{
Takashi Iwai4a796162011-06-17 17:53:38 +02001779 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001780 int err;
1781
1782 if (!pin)
1783 return 0;
1784
Takashi Iwai8df2a312011-06-21 11:48:29 +02001785 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001786 spec->hp_dac_nid = spec->hp_path.path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001787
Takashi Iwaiece8d042011-06-19 16:24:21 +02001788 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001789 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001790 !spec->hp_dac_nid)
1791 return 0;
1792
Takashi Iwaiece8d042011-06-19 16:24:21 +02001793 err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001794 if (err < 0)
1795 return err;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001796
Joseph Chanc577b8a2006-11-29 15:29:40 +01001797 return 0;
1798}
1799
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001800static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1801{
1802 struct via_spec *spec = codec->spec;
1803 hda_nid_t pin, dac;
1804
1805 pin = spec->autocfg.speaker_pins[0];
1806 if (!spec->autocfg.speaker_outs || !pin)
1807 return 0;
1808
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001809 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1810 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001811 spec->multiout.extra_out_nid[0] = dac;
1812 return create_ch_ctls(codec, "Speaker", pin, dac, 3);
1813 }
1814 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001815 &spec->speaker_path))
1816 return create_ch_ctls(codec, "Speaker", pin, 0, 3);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001817
1818 return 0;
1819}
1820
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001821/* look for ADCs */
1822static int via_fill_adcs(struct hda_codec *codec)
1823{
1824 struct via_spec *spec = codec->spec;
1825 hda_nid_t nid = codec->start_nid;
1826 int i;
1827
1828 for (i = 0; i < codec->num_nodes; i++, nid++) {
1829 unsigned int wcaps = get_wcaps(codec, nid);
1830 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1831 continue;
1832 if (wcaps & AC_WCAP_DIGITAL)
1833 continue;
1834 if (!(wcaps & AC_WCAP_CONN_LIST))
1835 continue;
1836 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1837 return -ENOMEM;
1838 spec->adc_nids[spec->num_adc_nids++] = nid;
1839 }
1840 return 0;
1841}
1842
1843static int get_mux_nids(struct hda_codec *codec);
1844
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001845static const struct snd_kcontrol_new via_input_src_ctl = {
1846 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1847 /* The multiple "Capture Source" controls confuse alsamixer
1848 * So call somewhat different..
1849 */
1850 /* .name = "Capture Source", */
1851 .name = "Input Source",
1852 .info = via_mux_enum_info,
1853 .get = via_mux_enum_get,
1854 .put = via_mux_enum_put,
1855};
1856
Takashi Iwai13af8e72011-06-20 14:05:46 +02001857static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
1858{
1859 struct hda_amp_list *list;
1860
1861 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
1862 return;
1863 list = spec->loopback_list + spec->num_loopbacks;
1864 list->nid = mix;
1865 list->dir = HDA_INPUT;
1866 list->idx = idx;
1867 spec->num_loopbacks++;
1868 spec->loopback.amplist = spec->loopback_list;
1869}
Takashi Iwai13af8e72011-06-20 14:05:46 +02001870
Joseph Chanc577b8a2006-11-29 15:29:40 +01001871/* create playback/capture controls for input pins */
Takashi Iwai620e2b22011-06-17 17:19:19 +02001872static int via_auto_create_analog_input_ctls(struct hda_codec *codec,
1873 const struct auto_pin_cfg *cfg)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001874{
Takashi Iwai10a20af2010-09-09 16:28:02 +02001875 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001876 struct hda_input_mux *imux = &spec->private_imux[0];
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001877 int i, j, err, idx, idx2, type, type_idx = 0;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001878 hda_nid_t cap_nid;
1879 hda_nid_t pin_idxs[8];
1880 int num_idxs;
1881
1882 err = via_fill_adcs(codec);
1883 if (err < 0)
1884 return err;
1885 err = get_mux_nids(codec);
1886 if (err < 0)
1887 return err;
1888 cap_nid = spec->mux_nids[0];
1889
1890 num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
1891 ARRAY_SIZE(pin_idxs));
1892 if (num_idxs <= 0)
1893 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001894
1895 /* for internal loopback recording select */
Takashi Iwaif3268512010-08-30 11:00:19 +02001896 for (idx = 0; idx < num_idxs; idx++) {
Takashi Iwai620e2b22011-06-17 17:19:19 +02001897 if (pin_idxs[idx] == spec->aa_mix_nid) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001898 snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
Takashi Iwaif3268512010-08-30 11:00:19 +02001899 break;
1900 }
1901 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001902
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001903 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001904 const char *label;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001905 type = cfg->inputs[i].type;
Takashi Iwaif3268512010-08-30 11:00:19 +02001906 for (idx = 0; idx < num_idxs; idx++)
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001907 if (pin_idxs[idx] == cfg->inputs[i].pin)
Takashi Iwaif3268512010-08-30 11:00:19 +02001908 break;
1909 if (idx >= num_idxs)
1910 continue;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001911 if (i > 0 && type == cfg->inputs[i - 1].type)
1912 type_idx++;
1913 else
1914 type_idx = 0;
Takashi Iwai10a20af2010-09-09 16:28:02 +02001915 label = hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwai620e2b22011-06-17 17:19:19 +02001916 idx2 = get_connection_index(codec, spec->aa_mix_nid,
1917 pin_idxs[idx]);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001918 if (idx2 >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08001919 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwai620e2b22011-06-17 17:19:19 +02001920 idx2, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001921 if (err < 0)
1922 return err;
1923 add_loopback_list(spec, spec->aa_mix_nid, idx2);
1924 }
Takashi Iwai10a20af2010-09-09 16:28:02 +02001925 snd_hda_add_imux_item(imux, label, idx, NULL);
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001926
1927 /* remember the label for smart51 control */
1928 for (j = 0; j < spec->smart51_nums; j++) {
1929 if (spec->smart51_pins[j] == cfg->inputs[i].pin) {
1930 spec->smart51_idxs[j] = idx;
1931 spec->smart51_labels[j] = label;
1932 break;
1933 }
1934 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001935 }
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001936
1937 /* create capture mixer elements */
1938 for (i = 0; i < spec->num_adc_nids; i++) {
1939 hda_nid_t adc = spec->adc_nids[i];
1940 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
1941 "Capture Volume", i,
1942 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1943 HDA_INPUT));
1944 if (err < 0)
1945 return err;
1946 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1947 "Capture Switch", i,
1948 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1949 HDA_INPUT));
1950 if (err < 0)
1951 return err;
1952 }
1953
1954 /* input-source control */
1955 for (i = 0; i < spec->num_adc_nids; i++)
1956 if (!spec->mux_nids[i])
1957 break;
1958 if (i) {
1959 struct snd_kcontrol_new *knew;
1960 knew = via_clone_control(spec, &via_input_src_ctl);
1961 if (!knew)
1962 return -ENOMEM;
1963 knew->count = i;
1964 }
1965
1966 /* mic-boosts */
1967 for (i = 0; i < cfg->num_inputs; i++) {
1968 hda_nid_t pin = cfg->inputs[i].pin;
1969 unsigned int caps;
1970 const char *label;
1971 char name[32];
1972
1973 if (cfg->inputs[i].type != AUTO_PIN_MIC)
1974 continue;
1975 caps = query_amp_caps(codec, pin, HDA_INPUT);
1976 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
1977 continue;
1978 label = hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwai30f7c5d2011-06-21 08:37:41 +02001979 snprintf(name, sizeof(name), "%s Boost Volume", label);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001980 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1981 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
1982 if (err < 0)
1983 return err;
1984 }
1985
Joseph Chanc577b8a2006-11-29 15:29:40 +01001986 return 0;
1987}
1988
Harald Welte76d9b0d2008-09-09 15:50:37 +08001989static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
1990{
1991 unsigned int def_conf;
1992 unsigned char seqassoc;
1993
Takashi Iwai2f334f92009-02-20 14:37:42 +01001994 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08001995 seqassoc = (unsigned char) get_defcfg_association(def_conf);
1996 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08001997 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
1998 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
1999 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2000 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002001 }
2002
2003 return;
2004}
2005
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002006static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002007 struct snd_ctl_elem_value *ucontrol)
2008{
2009 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2010 struct via_spec *spec = codec->spec;
2011
2012 if (spec->codec_type != VT1708)
2013 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002014 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002015 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002016 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002017 return 0;
2018}
2019
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002020static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002021 struct snd_ctl_elem_value *ucontrol)
2022{
2023 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2024 struct via_spec *spec = codec->spec;
2025 int change;
2026
2027 if (spec->codec_type != VT1708)
2028 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002029 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002030 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002031 == !spec->vt1708_jack_detect;
2032 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002033 mute_aa_path(codec, 1);
2034 notify_aa_path_ctls(codec);
2035 }
2036 return change;
2037}
2038
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002039static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2040 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2041 .name = "Jack Detect",
2042 .count = 1,
2043 .info = snd_ctl_boolean_mono_info,
2044 .get = vt1708_jack_detect_get,
2045 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002046};
2047
Takashi Iwai12daef62011-06-18 17:45:49 +02002048static void fill_dig_outs(struct hda_codec *codec);
2049static void fill_dig_in(struct hda_codec *codec);
2050
2051static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002052{
2053 struct via_spec *spec = codec->spec;
2054 int err;
2055
2056 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2057 if (err < 0)
2058 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002059 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002060 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002061
Takashi Iwai4a796162011-06-17 17:53:38 +02002062 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002063 if (err < 0)
2064 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002065 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002066 if (err < 0)
2067 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002068 err = via_auto_create_speaker_ctls(codec);
2069 if (err < 0)
2070 return err;
Takashi Iwai620e2b22011-06-17 17:19:19 +02002071 err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002072 if (err < 0)
2073 return err;
2074
2075 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2076
Takashi Iwai12daef62011-06-18 17:45:49 +02002077 fill_dig_outs(codec);
2078 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002079
Takashi Iwai603c4012008-07-30 15:01:44 +02002080 if (spec->kctls.list)
2081 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002082
Takashi Iwai096a8852011-06-20 12:09:02 +02002083 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002084
Harald Welte0aa62ae2008-09-09 15:58:27 +08002085 spec->input_mux = &spec->private_imux[0];
2086
Takashi Iwai8df2a312011-06-21 11:48:29 +02002087 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002088 err = via_hp_build(codec);
2089 if (err < 0)
2090 return err;
2091 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002092
Takashi Iwaif4a78282011-06-17 18:46:48 +02002093 err = via_smart51_build(codec);
2094 if (err < 0)
2095 return err;
2096
Takashi Iwai5d417622011-06-20 11:32:27 +02002097 /* assign slave outs */
2098 if (spec->slave_dig_outs[0])
2099 codec->slave_dig_outs = spec->slave_dig_outs;
2100
Joseph Chanc577b8a2006-11-29 15:29:40 +01002101 return 1;
2102}
2103
Takashi Iwai5d417622011-06-20 11:32:27 +02002104static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002105{
Lydia Wang25eaba22009-10-10 19:08:43 +08002106 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002107 if (spec->multiout.dig_out_nid)
2108 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2109 if (spec->slave_dig_outs[0])
2110 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2111}
Lydia Wang25eaba22009-10-10 19:08:43 +08002112
Takashi Iwai5d417622011-06-20 11:32:27 +02002113static void via_auto_init_dig_in(struct hda_codec *codec)
2114{
2115 struct via_spec *spec = codec->spec;
2116 if (!spec->dig_in_nid)
2117 return;
2118 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2119 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2120}
2121
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002122/* initialize the unsolicited events */
2123static void via_auto_init_unsol_event(struct hda_codec *codec)
2124{
2125 struct via_spec *spec = codec->spec;
2126 struct auto_pin_cfg *cfg = &spec->autocfg;
2127 unsigned int ev;
2128 int i;
2129
2130 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2131 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2132 AC_VERB_SET_UNSOLICITED_ENABLE,
2133 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2134
2135 if (cfg->speaker_pins[0])
2136 ev = VIA_LINE_EVENT;
2137 else
2138 ev = 0;
2139 for (i = 0; i < cfg->line_outs; i++) {
2140 if (cfg->line_out_pins[i] &&
2141 is_jack_detectable(codec, cfg->line_out_pins[i]))
2142 snd_hda_codec_write(codec, cfg->line_out_pins[0], 0,
2143 AC_VERB_SET_UNSOLICITED_ENABLE,
2144 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2145 }
2146
2147 for (i = 0; i < cfg->num_inputs; i++) {
2148 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2149 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2150 AC_VERB_SET_UNSOLICITED_ENABLE,
2151 AC_USRSP_EN | VIA_JACK_EVENT);
2152 }
2153}
2154
Takashi Iwai5d417622011-06-20 11:32:27 +02002155static int via_init(struct hda_codec *codec)
2156{
2157 struct via_spec *spec = codec->spec;
2158 int i;
2159
2160 for (i = 0; i < spec->num_iverbs; i++)
2161 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2162
Joseph Chanc577b8a2006-11-29 15:29:40 +01002163 via_auto_init_multi_out(codec);
2164 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002165 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002166 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002167 via_auto_init_dig_outs(codec);
2168 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002169
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002170 via_auto_init_unsol_event(codec);
2171
2172 via_hp_automute(codec);
2173 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002174
Joseph Chanc577b8a2006-11-29 15:29:40 +01002175 return 0;
2176}
2177
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002178static void vt1708_update_hp_jack_state(struct work_struct *work)
2179{
2180 struct via_spec *spec = container_of(work, struct via_spec,
2181 vt1708_hp_work.work);
2182 if (spec->codec_type != VT1708)
2183 return;
2184 /* if jack state toggled */
2185 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002186 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002187 spec->vt1708_hp_present ^= 1;
2188 via_hp_automute(spec->codec);
2189 }
2190 vt1708_start_hp_work(spec);
2191}
2192
Takashi Iwai337b9d02009-07-07 18:18:59 +02002193static int get_mux_nids(struct hda_codec *codec)
2194{
2195 struct via_spec *spec = codec->spec;
2196 hda_nid_t nid, conn[8];
2197 unsigned int type;
2198 int i, n;
2199
2200 for (i = 0; i < spec->num_adc_nids; i++) {
2201 nid = spec->adc_nids[i];
2202 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002203 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002204 if (type == AC_WID_PIN)
2205 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002206 n = snd_hda_get_connections(codec, nid, conn,
2207 ARRAY_SIZE(conn));
2208 if (n <= 0)
2209 break;
2210 if (n > 1) {
2211 spec->mux_nids[i] = nid;
2212 break;
2213 }
2214 nid = conn[0];
2215 }
2216 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002217 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002218}
2219
Joseph Chanc577b8a2006-11-29 15:29:40 +01002220static int patch_vt1708(struct hda_codec *codec)
2221{
2222 struct via_spec *spec;
2223 int err;
2224
2225 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002226 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002227 if (spec == NULL)
2228 return -ENOMEM;
2229
Takashi Iwai620e2b22011-06-17 17:19:19 +02002230 spec->aa_mix_nid = 0x17;
2231
Takashi Iwai12daef62011-06-18 17:45:49 +02002232 /* Add HP and CD pin config connect bit re-config action */
2233 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2234 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2235
Joseph Chanc577b8a2006-11-29 15:29:40 +01002236 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002237 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002238 if (err < 0) {
2239 via_free(codec);
2240 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002241 }
2242
Takashi Iwai12daef62011-06-18 17:45:49 +02002243 /* add jack detect on/off control */
2244 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2245 return -ENOMEM;
2246
Takashi Iwaibc9b562382008-05-23 17:50:27 +02002247 /* disable 32bit format on VT1708 */
2248 if (codec->vendor_id == 0x11061708)
2249 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002250
Joseph Chanc577b8a2006-11-29 15:29:40 +01002251 codec->patch_ops = via_patch_ops;
2252
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002253 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002254 return 0;
2255}
2256
Joseph Chanc577b8a2006-11-29 15:29:40 +01002257static int patch_vt1709_10ch(struct hda_codec *codec)
2258{
2259 struct via_spec *spec;
2260 int err;
2261
2262 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002263 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002264 if (spec == NULL)
2265 return -ENOMEM;
2266
Takashi Iwai620e2b22011-06-17 17:19:19 +02002267 spec->aa_mix_nid = 0x18;
2268
Takashi Iwai12daef62011-06-18 17:45:49 +02002269 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002270 if (err < 0) {
2271 via_free(codec);
2272 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002273 }
2274
Joseph Chanc577b8a2006-11-29 15:29:40 +01002275 codec->patch_ops = via_patch_ops;
2276
Joseph Chanc577b8a2006-11-29 15:29:40 +01002277 return 0;
2278}
2279/*
2280 * generic initialization of ADC, input mixers and output mixers
2281 */
Joseph Chanc577b8a2006-11-29 15:29:40 +01002282static int patch_vt1709_6ch(struct hda_codec *codec)
2283{
2284 struct via_spec *spec;
2285 int err;
2286
2287 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002288 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002289 if (spec == NULL)
2290 return -ENOMEM;
2291
Takashi Iwai620e2b22011-06-17 17:19:19 +02002292 spec->aa_mix_nid = 0x18;
2293
Takashi Iwai12daef62011-06-18 17:45:49 +02002294 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002295 if (err < 0) {
2296 via_free(codec);
2297 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002298 }
2299
Joseph Chanc577b8a2006-11-29 15:29:40 +01002300 codec->patch_ops = via_patch_ops;
2301
Josepch Chanf7278fd2007-12-13 16:40:40 +01002302 return 0;
2303}
2304
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002305static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2306{
2307 struct via_spec *spec = codec->spec;
2308 int imux_is_smixer;
2309 unsigned int parm;
2310 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002311 if ((spec->codec_type != VT1708B_4CH) &&
2312 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002313 is_8ch = 1;
2314
2315 /* SW0 (17h) = stereo mixer */
2316 imux_is_smixer =
2317 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2318 == ((spec->codec_type == VT1708S) ? 5 : 0));
2319 /* inputs */
2320 /* PW 1/2/5 (1ah/1bh/1eh) */
2321 parm = AC_PWRST_D3;
2322 set_pin_power_state(codec, 0x1a, &parm);
2323 set_pin_power_state(codec, 0x1b, &parm);
2324 set_pin_power_state(codec, 0x1e, &parm);
2325 if (imux_is_smixer)
2326 parm = AC_PWRST_D0;
2327 /* SW0 (17h), AIW 0/1 (13h/14h) */
2328 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2329 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2330 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2331
2332 /* outputs */
2333 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2334 parm = AC_PWRST_D3;
2335 set_pin_power_state(codec, 0x19, &parm);
2336 if (spec->smart51_enabled)
2337 set_pin_power_state(codec, 0x1b, &parm);
2338 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2339 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2340
2341 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2342 if (is_8ch) {
2343 parm = AC_PWRST_D3;
2344 set_pin_power_state(codec, 0x22, &parm);
2345 if (spec->smart51_enabled)
2346 set_pin_power_state(codec, 0x1a, &parm);
2347 snd_hda_codec_write(codec, 0x26, 0,
2348 AC_VERB_SET_POWER_STATE, parm);
2349 snd_hda_codec_write(codec, 0x24, 0,
2350 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002351 } else if (codec->vendor_id == 0x11064397) {
2352 /* PW7(23h), SW2(27h), AOW2(25h) */
2353 parm = AC_PWRST_D3;
2354 set_pin_power_state(codec, 0x23, &parm);
2355 if (spec->smart51_enabled)
2356 set_pin_power_state(codec, 0x1a, &parm);
2357 snd_hda_codec_write(codec, 0x27, 0,
2358 AC_VERB_SET_POWER_STATE, parm);
2359 snd_hda_codec_write(codec, 0x25, 0,
2360 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002361 }
2362
2363 /* PW 3/4/7 (1ch/1dh/23h) */
2364 parm = AC_PWRST_D3;
2365 /* force to D0 for internal Speaker */
2366 set_pin_power_state(codec, 0x1c, &parm);
2367 set_pin_power_state(codec, 0x1d, &parm);
2368 if (is_8ch)
2369 set_pin_power_state(codec, 0x23, &parm);
2370
2371 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2372 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2373 imux_is_smixer ? AC_PWRST_D0 : parm);
2374 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2375 if (is_8ch) {
2376 snd_hda_codec_write(codec, 0x25, 0,
2377 AC_VERB_SET_POWER_STATE, parm);
2378 snd_hda_codec_write(codec, 0x27, 0,
2379 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002380 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2381 snd_hda_codec_write(codec, 0x25, 0,
2382 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002383}
2384
Lydia Wang518bf3b2009-10-10 19:07:29 +08002385static int patch_vt1708S(struct hda_codec *codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002386static int patch_vt1708B_8ch(struct hda_codec *codec)
2387{
2388 struct via_spec *spec;
2389 int err;
2390
Lydia Wang518bf3b2009-10-10 19:07:29 +08002391 if (get_codec_type(codec) == VT1708BCE)
2392 return patch_vt1708S(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002393 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002394 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002395 if (spec == NULL)
2396 return -ENOMEM;
2397
Takashi Iwai620e2b22011-06-17 17:19:19 +02002398 spec->aa_mix_nid = 0x16;
2399
Josepch Chanf7278fd2007-12-13 16:40:40 +01002400 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002401 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002402 if (err < 0) {
2403 via_free(codec);
2404 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002405 }
2406
Josepch Chanf7278fd2007-12-13 16:40:40 +01002407 codec->patch_ops = via_patch_ops;
2408
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002409 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2410
Josepch Chanf7278fd2007-12-13 16:40:40 +01002411 return 0;
2412}
2413
2414static int patch_vt1708B_4ch(struct hda_codec *codec)
2415{
2416 struct via_spec *spec;
2417 int err;
2418
2419 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002420 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002421 if (spec == NULL)
2422 return -ENOMEM;
2423
Josepch Chanf7278fd2007-12-13 16:40:40 +01002424 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002425 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002426 if (err < 0) {
2427 via_free(codec);
2428 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002429 }
2430
Josepch Chanf7278fd2007-12-13 16:40:40 +01002431 codec->patch_ops = via_patch_ops;
2432
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002433 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2434
Joseph Chanc577b8a2006-11-29 15:29:40 +01002435 return 0;
2436}
2437
Harald Welted949cac2008-09-09 15:56:01 +08002438/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002439static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002440 /* Enable Mic Boost Volume backdoor */
2441 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002442 /* don't bybass mixer */
2443 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002444 { }
2445};
2446
Takashi Iwai9da29272009-05-07 16:31:14 +02002447/* fill out digital output widgets; one for master and one for slave outputs */
2448static void fill_dig_outs(struct hda_codec *codec)
2449{
2450 struct via_spec *spec = codec->spec;
2451 int i;
2452
2453 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2454 hda_nid_t nid;
2455 int conn;
2456
2457 nid = spec->autocfg.dig_out_pins[i];
2458 if (!nid)
2459 continue;
2460 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2461 if (conn < 1)
2462 continue;
2463 if (!spec->multiout.dig_out_nid)
2464 spec->multiout.dig_out_nid = nid;
2465 else {
2466 spec->slave_dig_outs[0] = nid;
2467 break; /* at most two dig outs */
2468 }
2469 }
2470}
2471
Takashi Iwai12daef62011-06-18 17:45:49 +02002472static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002473{
2474 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002475 hda_nid_t dig_nid;
2476 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002477
Takashi Iwai12daef62011-06-18 17:45:49 +02002478 if (!spec->autocfg.dig_in_pin)
2479 return;
Harald Welted949cac2008-09-09 15:56:01 +08002480
Takashi Iwai12daef62011-06-18 17:45:49 +02002481 dig_nid = codec->start_nid;
2482 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2483 unsigned int wcaps = get_wcaps(codec, dig_nid);
2484 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2485 continue;
2486 if (!(wcaps & AC_WCAP_DIGITAL))
2487 continue;
2488 if (!(wcaps & AC_WCAP_CONN_LIST))
2489 continue;
2490 err = get_connection_index(codec, dig_nid,
2491 spec->autocfg.dig_in_pin);
2492 if (err >= 0) {
2493 spec->dig_in_nid = dig_nid;
2494 break;
2495 }
2496 }
Harald Welted949cac2008-09-09 15:56:01 +08002497}
2498
Lydia Wang6369bcf2009-10-10 19:08:31 +08002499static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2500 int offset, int num_steps, int step_size)
2501{
2502 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2503 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2504 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2505 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2506 (0 << AC_AMPCAP_MUTE_SHIFT));
2507}
2508
Harald Welted949cac2008-09-09 15:56:01 +08002509static int patch_vt1708S(struct hda_codec *codec)
2510{
2511 struct via_spec *spec;
2512 int err;
2513
2514 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002515 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002516 if (spec == NULL)
2517 return -ENOMEM;
2518
Takashi Iwai620e2b22011-06-17 17:19:19 +02002519 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002520 override_mic_boost(codec, 0x1a, 0, 3, 40);
2521 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002522
Harald Welted949cac2008-09-09 15:56:01 +08002523 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002524 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002525 if (err < 0) {
2526 via_free(codec);
2527 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002528 }
2529
Takashi Iwai096a8852011-06-20 12:09:02 +02002530 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002531
Harald Welted949cac2008-09-09 15:56:01 +08002532 codec->patch_ops = via_patch_ops;
2533
Lydia Wang518bf3b2009-10-10 19:07:29 +08002534 /* correct names for VT1708BCE */
2535 if (get_codec_type(codec) == VT1708BCE) {
2536 kfree(codec->chip_name);
2537 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2538 snprintf(codec->bus->card->mixername,
2539 sizeof(codec->bus->card->mixername),
2540 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f630f2011-03-22 16:25:56 +08002541 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002542 /* correct names for VT1705 */
2543 if (codec->vendor_id == 0x11064397) {
2544 kfree(codec->chip_name);
2545 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2546 snprintf(codec->bus->card->mixername,
2547 sizeof(codec->bus->card->mixername),
2548 "%s %s", codec->vendor_name, codec->chip_name);
2549 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002550 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002551 return 0;
2552}
2553
2554/* Patch for VT1702 */
2555
Takashi Iwai096a8852011-06-20 12:09:02 +02002556static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002557 /* mixer enable */
2558 {0x1, 0xF88, 0x3},
2559 /* GPIO 0~2 */
2560 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002561 { }
2562};
2563
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002564static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2565{
2566 int imux_is_smixer =
2567 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2568 unsigned int parm;
2569 /* inputs */
2570 /* PW 1/2/5 (14h/15h/18h) */
2571 parm = AC_PWRST_D3;
2572 set_pin_power_state(codec, 0x14, &parm);
2573 set_pin_power_state(codec, 0x15, &parm);
2574 set_pin_power_state(codec, 0x18, &parm);
2575 if (imux_is_smixer)
2576 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2577 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2578 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2579 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2580 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2581 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2582
2583 /* outputs */
2584 /* PW 3/4 (16h/17h) */
2585 parm = AC_PWRST_D3;
2586 set_pin_power_state(codec, 0x17, &parm);
2587 set_pin_power_state(codec, 0x16, &parm);
2588 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2589 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2590 imux_is_smixer ? AC_PWRST_D0 : parm);
2591 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2592 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2593}
2594
Harald Welted949cac2008-09-09 15:56:01 +08002595static int patch_vt1702(struct hda_codec *codec)
2596{
2597 struct via_spec *spec;
2598 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002599
2600 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002601 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002602 if (spec == NULL)
2603 return -ENOMEM;
2604
Takashi Iwai620e2b22011-06-17 17:19:19 +02002605 spec->aa_mix_nid = 0x1a;
2606
Takashi Iwai12daef62011-06-18 17:45:49 +02002607 /* limit AA path volume to 0 dB */
2608 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2609 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2610 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2611 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2612 (1 << AC_AMPCAP_MUTE_SHIFT));
2613
Harald Welted949cac2008-09-09 15:56:01 +08002614 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002615 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002616 if (err < 0) {
2617 via_free(codec);
2618 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002619 }
2620
Takashi Iwai096a8852011-06-20 12:09:02 +02002621 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002622
Harald Welted949cac2008-09-09 15:56:01 +08002623 codec->patch_ops = via_patch_ops;
2624
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002625 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002626 return 0;
2627}
2628
Lydia Wangeb7188c2009-10-10 19:08:34 +08002629/* Patch for VT1718S */
2630
Takashi Iwai096a8852011-06-20 12:09:02 +02002631static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002632 /* Enable MW0 adjust Gain 5 */
2633 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002634 /* Enable Boost Volume backdoor */
2635 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002636
Lydia Wangeb7188c2009-10-10 19:08:34 +08002637 { }
2638};
2639
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002640static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2641{
2642 struct via_spec *spec = codec->spec;
2643 int imux_is_smixer;
2644 unsigned int parm;
2645 /* MUX6 (1eh) = stereo mixer */
2646 imux_is_smixer =
2647 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2648 /* inputs */
2649 /* PW 5/6/7 (29h/2ah/2bh) */
2650 parm = AC_PWRST_D3;
2651 set_pin_power_state(codec, 0x29, &parm);
2652 set_pin_power_state(codec, 0x2a, &parm);
2653 set_pin_power_state(codec, 0x2b, &parm);
2654 if (imux_is_smixer)
2655 parm = AC_PWRST_D0;
2656 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2657 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2658 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2659 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2660 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2661
2662 /* outputs */
2663 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2664 parm = AC_PWRST_D3;
2665 set_pin_power_state(codec, 0x27, &parm);
2666 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2667 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2668
2669 /* PW2 (26h), AOW2 (ah) */
2670 parm = AC_PWRST_D3;
2671 set_pin_power_state(codec, 0x26, &parm);
2672 if (spec->smart51_enabled)
2673 set_pin_power_state(codec, 0x2b, &parm);
2674 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2675
2676 /* PW0 (24h), AOW0 (8h) */
2677 parm = AC_PWRST_D3;
2678 set_pin_power_state(codec, 0x24, &parm);
2679 if (!spec->hp_independent_mode) /* check for redirected HP */
2680 set_pin_power_state(codec, 0x28, &parm);
2681 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2682 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2683 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2684 imux_is_smixer ? AC_PWRST_D0 : parm);
2685
2686 /* PW1 (25h), AOW1 (9h) */
2687 parm = AC_PWRST_D3;
2688 set_pin_power_state(codec, 0x25, &parm);
2689 if (spec->smart51_enabled)
2690 set_pin_power_state(codec, 0x2a, &parm);
2691 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2692
2693 if (spec->hp_independent_mode) {
2694 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2695 parm = AC_PWRST_D3;
2696 set_pin_power_state(codec, 0x28, &parm);
2697 snd_hda_codec_write(codec, 0x1b, 0,
2698 AC_VERB_SET_POWER_STATE, parm);
2699 snd_hda_codec_write(codec, 0x34, 0,
2700 AC_VERB_SET_POWER_STATE, parm);
2701 snd_hda_codec_write(codec, 0xc, 0,
2702 AC_VERB_SET_POWER_STATE, parm);
2703 }
2704}
2705
Lydia Wangeb7188c2009-10-10 19:08:34 +08002706static int patch_vt1718S(struct hda_codec *codec)
2707{
2708 struct via_spec *spec;
2709 int err;
2710
2711 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002712 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002713 if (spec == NULL)
2714 return -ENOMEM;
2715
Takashi Iwai620e2b22011-06-17 17:19:19 +02002716 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002717 override_mic_boost(codec, 0x2b, 0, 3, 40);
2718 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002719
Lydia Wangeb7188c2009-10-10 19:08:34 +08002720 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002721 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002722 if (err < 0) {
2723 via_free(codec);
2724 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002725 }
2726
Takashi Iwai096a8852011-06-20 12:09:02 +02002727 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002728
Lydia Wangeb7188c2009-10-10 19:08:34 +08002729 codec->patch_ops = via_patch_ops;
2730
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002731 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2732
Lydia Wangeb7188c2009-10-10 19:08:34 +08002733 return 0;
2734}
Lydia Wangf3db4232009-10-10 19:08:41 +08002735
2736/* Patch for VT1716S */
2737
2738static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2739 struct snd_ctl_elem_info *uinfo)
2740{
2741 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2742 uinfo->count = 1;
2743 uinfo->value.integer.min = 0;
2744 uinfo->value.integer.max = 1;
2745 return 0;
2746}
2747
2748static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2749 struct snd_ctl_elem_value *ucontrol)
2750{
2751 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2752 int index = 0;
2753
2754 index = snd_hda_codec_read(codec, 0x26, 0,
2755 AC_VERB_GET_CONNECT_SEL, 0);
2756 if (index != -1)
2757 *ucontrol->value.integer.value = index;
2758
2759 return 0;
2760}
2761
2762static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2763 struct snd_ctl_elem_value *ucontrol)
2764{
2765 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2766 struct via_spec *spec = codec->spec;
2767 int index = *ucontrol->value.integer.value;
2768
2769 snd_hda_codec_write(codec, 0x26, 0,
2770 AC_VERB_SET_CONNECT_SEL, index);
2771 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002772 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002773 return 1;
2774}
2775
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002776static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002777 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2778 {
2779 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2780 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002781 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08002782 .count = 1,
2783 .info = vt1716s_dmic_info,
2784 .get = vt1716s_dmic_get,
2785 .put = vt1716s_dmic_put,
2786 },
2787 {} /* end */
2788};
2789
2790
2791/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002792static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002793 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
2794 { } /* end */
2795};
2796
Takashi Iwai096a8852011-06-20 12:09:02 +02002797static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002798 /* Enable Boost Volume backdoor */
2799 {0x1, 0xf8a, 0x80},
2800 /* don't bybass mixer */
2801 {0x1, 0xf88, 0xc0},
2802 /* Enable mono output */
2803 {0x1, 0xf90, 0x08},
2804 { }
2805};
2806
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002807static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
2808{
2809 struct via_spec *spec = codec->spec;
2810 int imux_is_smixer;
2811 unsigned int parm;
2812 unsigned int mono_out, present;
2813 /* SW0 (17h) = stereo mixer */
2814 imux_is_smixer =
2815 (snd_hda_codec_read(codec, 0x17, 0,
2816 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
2817 /* inputs */
2818 /* PW 1/2/5 (1ah/1bh/1eh) */
2819 parm = AC_PWRST_D3;
2820 set_pin_power_state(codec, 0x1a, &parm);
2821 set_pin_power_state(codec, 0x1b, &parm);
2822 set_pin_power_state(codec, 0x1e, &parm);
2823 if (imux_is_smixer)
2824 parm = AC_PWRST_D0;
2825 /* SW0 (17h), AIW0(13h) */
2826 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2827 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2828
2829 parm = AC_PWRST_D3;
2830 set_pin_power_state(codec, 0x1e, &parm);
2831 /* PW11 (22h) */
2832 if (spec->dmic_enabled)
2833 set_pin_power_state(codec, 0x22, &parm);
2834 else
2835 snd_hda_codec_write(codec, 0x22, 0,
2836 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
2837
2838 /* SW2(26h), AIW1(14h) */
2839 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
2840 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2841
2842 /* outputs */
2843 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2844 parm = AC_PWRST_D3;
2845 set_pin_power_state(codec, 0x19, &parm);
2846 /* Smart 5.1 PW2(1bh) */
2847 if (spec->smart51_enabled)
2848 set_pin_power_state(codec, 0x1b, &parm);
2849 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2850 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2851
2852 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
2853 parm = AC_PWRST_D3;
2854 set_pin_power_state(codec, 0x23, &parm);
2855 /* Smart 5.1 PW1(1ah) */
2856 if (spec->smart51_enabled)
2857 set_pin_power_state(codec, 0x1a, &parm);
2858 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
2859
2860 /* Smart 5.1 PW5(1eh) */
2861 if (spec->smart51_enabled)
2862 set_pin_power_state(codec, 0x1e, &parm);
2863 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
2864
2865 /* Mono out */
2866 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
2867 present = snd_hda_jack_detect(codec, 0x1c);
2868
2869 if (present)
2870 mono_out = 0;
2871 else {
2872 present = snd_hda_jack_detect(codec, 0x1d);
2873 if (!spec->hp_independent_mode && present)
2874 mono_out = 0;
2875 else
2876 mono_out = 1;
2877 }
2878 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
2879 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
2880 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
2881 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
2882
2883 /* PW 3/4 (1ch/1dh) */
2884 parm = AC_PWRST_D3;
2885 set_pin_power_state(codec, 0x1c, &parm);
2886 set_pin_power_state(codec, 0x1d, &parm);
2887 /* HP Independent Mode, power on AOW3 */
2888 if (spec->hp_independent_mode)
2889 snd_hda_codec_write(codec, 0x25, 0,
2890 AC_VERB_SET_POWER_STATE, parm);
2891
2892 /* force to D0 for internal Speaker */
2893 /* MW0 (16h), AOW0 (10h) */
2894 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2895 imux_is_smixer ? AC_PWRST_D0 : parm);
2896 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
2897 mono_out ? AC_PWRST_D0 : parm);
2898}
2899
Lydia Wangf3db4232009-10-10 19:08:41 +08002900static int patch_vt1716S(struct hda_codec *codec)
2901{
2902 struct via_spec *spec;
2903 int err;
2904
2905 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002906 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002907 if (spec == NULL)
2908 return -ENOMEM;
2909
Takashi Iwai620e2b22011-06-17 17:19:19 +02002910 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002911 override_mic_boost(codec, 0x1a, 0, 3, 40);
2912 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002913
Lydia Wangf3db4232009-10-10 19:08:41 +08002914 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002915 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002916 if (err < 0) {
2917 via_free(codec);
2918 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08002919 }
2920
Takashi Iwai096a8852011-06-20 12:09:02 +02002921 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08002922
Lydia Wangf3db4232009-10-10 19:08:41 +08002923 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
2924 spec->num_mixers++;
2925
2926 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
2927
2928 codec->patch_ops = via_patch_ops;
2929
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002930 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08002931 return 0;
2932}
Lydia Wang25eaba22009-10-10 19:08:43 +08002933
2934/* for vt2002P */
2935
Takashi Iwai096a8852011-06-20 12:09:02 +02002936static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08002937 /* Class-D speaker related verbs */
2938 {0x1, 0xfe0, 0x4},
2939 {0x1, 0xfe9, 0x80},
2940 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08002941 /* Enable Boost Volume backdoor */
2942 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08002943 /* Enable AOW0 to MW9 */
2944 {0x1, 0xfb8, 0x88},
2945 { }
2946};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002947
Takashi Iwai096a8852011-06-20 12:09:02 +02002948static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08002949 /* Enable Boost Volume backdoor */
2950 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08002951 /* Enable AOW0 to MW9 */
2952 {0x1, 0xfb8, 0x88},
2953 { }
2954};
Lydia Wang25eaba22009-10-10 19:08:43 +08002955
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002956static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
2957{
2958 struct via_spec *spec = codec->spec;
2959 int imux_is_smixer;
2960 unsigned int parm;
2961 unsigned int present;
2962 /* MUX9 (1eh) = stereo mixer */
2963 imux_is_smixer =
2964 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2965 /* inputs */
2966 /* PW 5/6/7 (29h/2ah/2bh) */
2967 parm = AC_PWRST_D3;
2968 set_pin_power_state(codec, 0x29, &parm);
2969 set_pin_power_state(codec, 0x2a, &parm);
2970 set_pin_power_state(codec, 0x2b, &parm);
2971 parm = AC_PWRST_D0;
2972 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
2973 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2974 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2975 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2976 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2977
2978 /* outputs */
2979 /* AOW0 (8h)*/
2980 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2981
Lydia Wang118909562011-03-23 17:57:34 +08002982 if (spec->codec_type == VT1802) {
2983 /* PW4 (28h), MW4 (18h), MUX4(38h) */
2984 parm = AC_PWRST_D3;
2985 set_pin_power_state(codec, 0x28, &parm);
2986 snd_hda_codec_write(codec, 0x18, 0,
2987 AC_VERB_SET_POWER_STATE, parm);
2988 snd_hda_codec_write(codec, 0x38, 0,
2989 AC_VERB_SET_POWER_STATE, parm);
2990 } else {
2991 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
2992 parm = AC_PWRST_D3;
2993 set_pin_power_state(codec, 0x26, &parm);
2994 snd_hda_codec_write(codec, 0x1c, 0,
2995 AC_VERB_SET_POWER_STATE, parm);
2996 snd_hda_codec_write(codec, 0x37, 0,
2997 AC_VERB_SET_POWER_STATE, parm);
2998 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002999
Lydia Wang118909562011-03-23 17:57:34 +08003000 if (spec->codec_type == VT1802) {
3001 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3002 parm = AC_PWRST_D3;
3003 set_pin_power_state(codec, 0x25, &parm);
3004 snd_hda_codec_write(codec, 0x15, 0,
3005 AC_VERB_SET_POWER_STATE, parm);
3006 snd_hda_codec_write(codec, 0x35, 0,
3007 AC_VERB_SET_POWER_STATE, parm);
3008 } else {
3009 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3010 parm = AC_PWRST_D3;
3011 set_pin_power_state(codec, 0x25, &parm);
3012 snd_hda_codec_write(codec, 0x19, 0,
3013 AC_VERB_SET_POWER_STATE, parm);
3014 snd_hda_codec_write(codec, 0x35, 0,
3015 AC_VERB_SET_POWER_STATE, parm);
3016 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003017
3018 if (spec->hp_independent_mode)
3019 snd_hda_codec_write(codec, 0x9, 0,
3020 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3021
3022 /* Class-D */
3023 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3024 present = snd_hda_jack_detect(codec, 0x25);
3025
3026 parm = AC_PWRST_D3;
3027 set_pin_power_state(codec, 0x24, &parm);
3028 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003029 if (spec->codec_type == VT1802)
3030 snd_hda_codec_write(codec, 0x14, 0,
3031 AC_VERB_SET_POWER_STATE, parm);
3032 else
3033 snd_hda_codec_write(codec, 0x18, 0,
3034 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003035 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3036
3037 /* Mono Out */
3038 present = snd_hda_jack_detect(codec, 0x26);
3039
3040 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003041 if (spec->codec_type == VT1802) {
3042 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3043 snd_hda_codec_write(codec, 0x33, 0,
3044 AC_VERB_SET_POWER_STATE, parm);
3045 snd_hda_codec_write(codec, 0x1c, 0,
3046 AC_VERB_SET_POWER_STATE, parm);
3047 snd_hda_codec_write(codec, 0x3c, 0,
3048 AC_VERB_SET_POWER_STATE, parm);
3049 } else {
3050 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3051 snd_hda_codec_write(codec, 0x31, 0,
3052 AC_VERB_SET_POWER_STATE, parm);
3053 snd_hda_codec_write(codec, 0x17, 0,
3054 AC_VERB_SET_POWER_STATE, parm);
3055 snd_hda_codec_write(codec, 0x3b, 0,
3056 AC_VERB_SET_POWER_STATE, parm);
3057 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003058 /* MW9 (21h) */
3059 if (imux_is_smixer || !is_aa_path_mute(codec))
3060 snd_hda_codec_write(codec, 0x21, 0,
3061 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3062 else
3063 snd_hda_codec_write(codec, 0x21, 0,
3064 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3065}
Lydia Wang25eaba22009-10-10 19:08:43 +08003066
3067/* patch for vt2002P */
3068static int patch_vt2002P(struct hda_codec *codec)
3069{
3070 struct via_spec *spec;
3071 int err;
3072
3073 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003074 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003075 if (spec == NULL)
3076 return -ENOMEM;
3077
Takashi Iwai620e2b22011-06-17 17:19:19 +02003078 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003079 override_mic_boost(codec, 0x2b, 0, 3, 40);
3080 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003081
Lydia Wang25eaba22009-10-10 19:08:43 +08003082 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003083 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003084 if (err < 0) {
3085 via_free(codec);
3086 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003087 }
3088
Lydia Wang118909562011-03-23 17:57:34 +08003089 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003090 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003091 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003092 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003093
Lydia Wang25eaba22009-10-10 19:08:43 +08003094 codec->patch_ops = via_patch_ops;
3095
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003096 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003097 return 0;
3098}
Lydia Wangab6734e2009-10-10 19:08:46 +08003099
3100/* for vt1812 */
3101
Takashi Iwai096a8852011-06-20 12:09:02 +02003102static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003103 /* Enable Boost Volume backdoor */
3104 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003105 /* Enable AOW0 to MW9 */
3106 {0x1, 0xfb8, 0xa8},
3107 { }
3108};
3109
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003110static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3111{
3112 struct via_spec *spec = codec->spec;
3113 int imux_is_smixer =
3114 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3115 unsigned int parm;
3116 unsigned int present;
3117 /* MUX10 (1eh) = stereo mixer */
3118 imux_is_smixer =
3119 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3120 /* inputs */
3121 /* PW 5/6/7 (29h/2ah/2bh) */
3122 parm = AC_PWRST_D3;
3123 set_pin_power_state(codec, 0x29, &parm);
3124 set_pin_power_state(codec, 0x2a, &parm);
3125 set_pin_power_state(codec, 0x2b, &parm);
3126 parm = AC_PWRST_D0;
3127 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3128 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3129 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3130 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3131 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3132
3133 /* outputs */
3134 /* AOW0 (8h)*/
3135 snd_hda_codec_write(codec, 0x8, 0,
3136 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3137
3138 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3139 parm = AC_PWRST_D3;
3140 set_pin_power_state(codec, 0x28, &parm);
3141 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3142 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3143
3144 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3145 parm = AC_PWRST_D3;
3146 set_pin_power_state(codec, 0x25, &parm);
3147 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3148 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3149 if (spec->hp_independent_mode)
3150 snd_hda_codec_write(codec, 0x9, 0,
3151 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3152
3153 /* Internal Speaker */
3154 /* PW0 (24h), MW0(14h), MUX0(34h) */
3155 present = snd_hda_jack_detect(codec, 0x25);
3156
3157 parm = AC_PWRST_D3;
3158 set_pin_power_state(codec, 0x24, &parm);
3159 if (present) {
3160 snd_hda_codec_write(codec, 0x14, 0,
3161 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3162 snd_hda_codec_write(codec, 0x34, 0,
3163 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3164 } else {
3165 snd_hda_codec_write(codec, 0x14, 0,
3166 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3167 snd_hda_codec_write(codec, 0x34, 0,
3168 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3169 }
3170
3171
3172 /* Mono Out */
3173 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3174 present = snd_hda_jack_detect(codec, 0x28);
3175
3176 parm = AC_PWRST_D3;
3177 set_pin_power_state(codec, 0x31, &parm);
3178 if (present) {
3179 snd_hda_codec_write(codec, 0x1c, 0,
3180 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3181 snd_hda_codec_write(codec, 0x3c, 0,
3182 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3183 snd_hda_codec_write(codec, 0x3e, 0,
3184 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3185 } else {
3186 snd_hda_codec_write(codec, 0x1c, 0,
3187 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3188 snd_hda_codec_write(codec, 0x3c, 0,
3189 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3190 snd_hda_codec_write(codec, 0x3e, 0,
3191 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3192 }
3193
3194 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3195 parm = AC_PWRST_D3;
3196 set_pin_power_state(codec, 0x33, &parm);
3197 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3198 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3199
3200}
Lydia Wangab6734e2009-10-10 19:08:46 +08003201
3202/* patch for vt1812 */
3203static int patch_vt1812(struct hda_codec *codec)
3204{
3205 struct via_spec *spec;
3206 int err;
3207
3208 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003209 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003210 if (spec == NULL)
3211 return -ENOMEM;
3212
Takashi Iwai620e2b22011-06-17 17:19:19 +02003213 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003214 override_mic_boost(codec, 0x2b, 0, 3, 40);
3215 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003216
Lydia Wangab6734e2009-10-10 19:08:46 +08003217 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003218 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003219 if (err < 0) {
3220 via_free(codec);
3221 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003222 }
3223
Takashi Iwai096a8852011-06-20 12:09:02 +02003224 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003225
Lydia Wangab6734e2009-10-10 19:08:46 +08003226 codec->patch_ops = via_patch_ops;
3227
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003228 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003229 return 0;
3230}
3231
Joseph Chanc577b8a2006-11-29 15:29:40 +01003232/*
3233 * patch entries
3234 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003235static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003236 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3237 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3238 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3239 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3240 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003241 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003242 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003243 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003244 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003245 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003246 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003247 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003248 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003249 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003250 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003251 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003252 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003253 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003254 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003255 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003256 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003257 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003258 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003259 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003260 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003261 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003262 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003263 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003264 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003265 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003266 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003267 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003268 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003269 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003270 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003271 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003272 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003273 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003274 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003275 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003276 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003277 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003278 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003279 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003280 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003281 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003282 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003283 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003284 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003285 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003286 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003287 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003288 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003289 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003290 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003291 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003292 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003293 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003294 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003295 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003296 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003297 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003298 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003299 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003300 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003301 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003302 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003303 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003304 { .id = 0x11060428, .name = "VT1718S",
3305 .patch = patch_vt1718S},
3306 { .id = 0x11064428, .name = "VT1718S",
3307 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003308 { .id = 0x11060441, .name = "VT2020",
3309 .patch = patch_vt1718S},
3310 { .id = 0x11064441, .name = "VT1828S",
3311 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003312 { .id = 0x11060433, .name = "VT1716S",
3313 .patch = patch_vt1716S},
3314 { .id = 0x1106a721, .name = "VT1716S",
3315 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003316 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3317 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003318 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003319 { .id = 0x11060440, .name = "VT1818S",
3320 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003321 { .id = 0x11060446, .name = "VT1802",
3322 .patch = patch_vt2002P},
3323 { .id = 0x11068446, .name = "VT1802",
3324 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003325 {} /* terminator */
3326};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003327
3328MODULE_ALIAS("snd-hda-codec-id:1106*");
3329
3330static struct hda_codec_preset_list via_list = {
3331 .preset = snd_hda_preset_via,
3332 .owner = THIS_MODULE,
3333};
3334
3335MODULE_LICENSE("GPL");
3336MODULE_DESCRIPTION("VIA HD-audio codec");
3337
3338static int __init patch_via_init(void)
3339{
3340 return snd_hda_add_codec_preset(&via_list);
3341}
3342
3343static void __exit patch_via_exit(void)
3344{
3345 snd_hda_delete_codec_preset(&via_list);
3346}
3347
3348module_init(patch_via_init)
3349module_exit(patch_via_exit)