blob: af47b9aca97453ae62713bc88914ea86737b3518 [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 Iwai09a9ad62011-06-21 15:57:44 +020086/* output-path: DAC -> ... -> pin
87 * idx[] contains the source index number of the next widget;
88 * e.g. idx[0] is the index of the DAC selected by path[1] widget
89 * multi[] indicates whether it's a selector widget with multi-connectors
90 * (i.e. the connection selection is mandatory)
91 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
92 */
Takashi Iwai4a796162011-06-17 17:53:38 +020093struct nid_path {
94 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020095 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad62011-06-21 15:57:44 +020096 unsigned char idx[MAX_NID_PATH_DEPTH];
97 unsigned char multi[MAX_NID_PATH_DEPTH];
98 unsigned int vol_ctl;
99 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200100};
101
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800102struct via_spec {
103 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200104 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800105 unsigned int num_mixers;
106
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200107 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800108 unsigned int num_iverbs;
109
Takashi Iwai82673bc2011-06-17 16:24:21 +0200110 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200111 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200112 const struct hda_pcm_stream *stream_analog_playback;
113 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800114
Takashi Iwai82673bc2011-06-17 16:24:21 +0200115 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200116 const struct hda_pcm_stream *stream_digital_playback;
117 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800118
119 /* playback */
120 struct hda_multi_out multiout;
121 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200122 hda_nid_t hp_dac_nid;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200123 int num_active_streams;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800124
Takashi Iwai4a796162011-06-17 17:53:38 +0200125 struct nid_path out_path[4];
126 struct nid_path hp_path;
127 struct nid_path hp_dep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200128 struct nid_path speaker_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200129
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800130 /* capture */
131 unsigned int num_adc_nids;
Takashi Iwaia766d0d2011-06-17 09:01:29 +0200132 hda_nid_t adc_nids[3];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800133 hda_nid_t mux_nids[3];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200134 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800135 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800136
137 /* capture source */
138 const struct hda_input_mux *input_mux;
139 unsigned int cur_mux[3];
140
141 /* PCM information */
142 struct hda_pcm pcm_rec[3];
143
144 /* dynamic controls, init_verbs and input_mux */
145 struct auto_pin_cfg autocfg;
146 struct snd_array kctls;
147 struct hda_input_mux private_imux[2];
148 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
149
150 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800151 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800152 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200153 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800154 enum VIA_HDA_CODEC codec_type;
155
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200156 /* smart51 setup */
157 unsigned int smart51_nums;
158 hda_nid_t smart51_pins[2];
159 int smart51_idxs[2];
160 const char *smart51_labels[2];
161 unsigned int smart51_enabled;
162
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800163 /* work to check hp jack state */
164 struct hda_codec *codec;
165 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200166 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800167 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800168
169 void (*set_widgets_power_state)(struct hda_codec *codec);
170
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800171 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200172 int num_loopbacks;
173 struct hda_amp_list loopback_list[8];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800174};
175
Lydia Wang0341ccd2011-03-22 16:25:03 +0800176static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100177static struct via_spec * via_new_spec(struct hda_codec *codec)
178{
179 struct via_spec *spec;
180
181 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
182 if (spec == NULL)
183 return NULL;
184
185 codec->spec = spec;
186 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800187 spec->codec_type = get_codec_type(codec);
188 /* VT1708BCE & VT1708S are almost same */
189 if (spec->codec_type == VT1708BCE)
190 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100191 return spec;
192}
193
Lydia Wang744ff5f2009-10-10 19:07:26 +0800194static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800195{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800196 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800197 u16 ven_id = vendor_id >> 16;
198 u16 dev_id = vendor_id & 0xffff;
199 enum VIA_HDA_CODEC codec_type;
200
201 /* get codec type */
202 if (ven_id != 0x1106)
203 codec_type = UNKNOWN;
204 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
205 codec_type = VT1708;
206 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
207 codec_type = VT1709_10CH;
208 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
209 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800210 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800211 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800212 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
213 codec_type = VT1708BCE;
214 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800215 codec_type = VT1708B_4CH;
216 else if ((dev_id & 0xfff) == 0x397
217 && (dev_id >> 12) < 8)
218 codec_type = VT1708S;
219 else if ((dev_id & 0xfff) == 0x398
220 && (dev_id >> 12) < 8)
221 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800222 else if ((dev_id & 0xfff) == 0x428
223 && (dev_id >> 12) < 8)
224 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800225 else if (dev_id == 0x0433 || dev_id == 0xa721)
226 codec_type = VT1716S;
Lydia Wangbb3c6bf2009-10-10 19:08:39 +0800227 else if (dev_id == 0x0441 || dev_id == 0x4441)
228 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800229 else if (dev_id == 0x0438 || dev_id == 0x4438)
230 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800231 else if (dev_id == 0x0448)
232 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800233 else if (dev_id == 0x0440)
234 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800235 else if ((dev_id & 0xfff) == 0x446)
236 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800237 else
238 codec_type = UNKNOWN;
239 return codec_type;
240};
241
Lydia Wangec7e7e42011-03-24 12:43:44 +0800242#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800243#define VIA_HP_EVENT 0x01
244#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200245#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800246
Joseph Chanc577b8a2006-11-29 15:29:40 +0100247enum {
248 VIA_CTL_WIDGET_VOL,
249 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800250 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100251};
252
Takashi Iwaiada509e2011-06-20 15:40:19 +0200253static void analog_low_current_mode(struct hda_codec *codec);
254static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800255
256static void vt1708_start_hp_work(struct via_spec *spec)
257{
258 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
259 return;
260 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200261 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800262 if (!delayed_work_pending(&spec->vt1708_hp_work))
263 schedule_delayed_work(&spec->vt1708_hp_work,
264 msecs_to_jiffies(100));
265}
266
267static void vt1708_stop_hp_work(struct via_spec *spec)
268{
269 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
270 return;
271 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
272 && !is_aa_path_mute(spec->codec))
273 return;
274 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200275 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100276 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800277}
Lydia Wangf5271102009-10-10 19:07:35 +0800278
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800279static void set_widgets_power_state(struct hda_codec *codec)
280{
281 struct via_spec *spec = codec->spec;
282 if (spec->set_widgets_power_state)
283 spec->set_widgets_power_state(codec);
284}
Lydia Wang25eaba22009-10-10 19:08:43 +0800285
Lydia Wangf5271102009-10-10 19:07:35 +0800286static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
287 struct snd_ctl_elem_value *ucontrol)
288{
289 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
290 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
291
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800292 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200293 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800294 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
295 if (is_aa_path_mute(codec))
296 vt1708_start_hp_work(codec->spec);
297 else
298 vt1708_stop_hp_work(codec->spec);
299 }
Lydia Wangf5271102009-10-10 19:07:35 +0800300 return change;
301}
302
303/* modify .put = snd_hda_mixer_amp_switch_put */
304#define ANALOG_INPUT_MUTE \
305 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
306 .name = NULL, \
307 .index = 0, \
308 .info = snd_hda_mixer_amp_switch_info, \
309 .get = snd_hda_mixer_amp_switch_get, \
310 .put = analog_input_switch_put, \
311 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
312
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200313static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100314 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
315 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800316 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100317};
318
Lydia Wangab6734e2009-10-10 19:08:46 +0800319
Joseph Chanc577b8a2006-11-29 15:29:40 +0100320/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200321static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
322 const struct snd_kcontrol_new *tmpl,
323 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100324{
325 struct snd_kcontrol_new *knew;
326
Takashi Iwai603c4012008-07-30 15:01:44 +0200327 snd_array_init(&spec->kctls, sizeof(*knew), 32);
328 knew = snd_array_new(&spec->kctls);
329 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200330 return NULL;
331 *knew = *tmpl;
332 if (!name)
333 name = tmpl->name;
334 if (name) {
335 knew->name = kstrdup(name, GFP_KERNEL);
336 if (!knew->name)
337 return NULL;
338 }
339 return knew;
340}
341
342static int __via_add_control(struct via_spec *spec, int type, const char *name,
343 int idx, unsigned long val)
344{
345 struct snd_kcontrol_new *knew;
346
347 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
348 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100349 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200350 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100351 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100352 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100353 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100354 return 0;
355}
356
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200357#define via_add_control(spec, type, name, val) \
358 __via_add_control(spec, type, name, 0, val)
359
Takashi Iwai291c9e32011-06-17 16:15:26 +0200360#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100361
Takashi Iwai603c4012008-07-30 15:01:44 +0200362static void via_free_kctls(struct hda_codec *codec)
363{
364 struct via_spec *spec = codec->spec;
365
366 if (spec->kctls.list) {
367 struct snd_kcontrol_new *kctl = spec->kctls.list;
368 int i;
369 for (i = 0; i < spec->kctls.used; i++)
370 kfree(kctl[i].name);
371 }
372 snd_array_free(&spec->kctls);
373}
374
Joseph Chanc577b8a2006-11-29 15:29:40 +0100375/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800376static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200377 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100378{
379 char name[32];
380 int err;
381
382 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200383 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100384 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
385 if (err < 0)
386 return err;
387 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200388 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100389 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
390 if (err < 0)
391 return err;
392 return 0;
393}
394
Takashi Iwai5d417622011-06-20 11:32:27 +0200395/* return the index of the given widget nid as the source of mux;
396 * return -1 if not found;
397 * if num_conns is non-NULL, set the total number of connections
398 */
399static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux,
400 hda_nid_t nid, int *num_conns)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100401{
Takashi Iwai5d417622011-06-20 11:32:27 +0200402 hda_nid_t conn[HDA_MAX_NUM_INPUTS];
403 int i, nums;
404
405 nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
406 if (num_conns)
407 *num_conns = nums;
408 for (i = 0; i < nums; i++)
409 if (conn[i] == nid)
410 return i;
411 return -1;
412}
413
414#define get_connection_index(codec, mux, nid) \
415 __get_connection_index(codec, mux, nid, NULL)
416
Takashi Iwai8df2a312011-06-21 11:48:29 +0200417static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
418 unsigned int mask)
419{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200420 unsigned int caps;
421 if (!nid)
422 return false;
423 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200424 if (dir == HDA_INPUT)
425 caps &= AC_WCAP_IN_AMP;
426 else
427 caps &= AC_WCAP_OUT_AMP;
428 if (!caps)
429 return false;
430 if (query_amp_caps(codec, nid, dir) & mask)
431 return true;
432 return false;
433}
434
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200435#define have_mute(codec, nid, dir) \
436 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200437
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200438/* enable/disable the output-route */
439static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
440 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200441{
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200442 int i;
443 for (i = 0; i < path->depth; i++) {
444 hda_nid_t src, dst;
445 int idx = path->idx[i];
446 src = path->path[i];
447 if (i < path->depth - 1)
448 dst = path->path[i + 1];
449 else
450 dst = 0;
451 if (enable && path->multi[i])
452 snd_hda_codec_write(codec, dst, 0,
453 AC_VERB_SET_CONNECT_SEL, idx);
454 if (have_mute(codec, dst, HDA_INPUT)) {
455 int val = enable ? AMP_IN_UNMUTE(idx) :
456 AMP_IN_MUTE(idx);
457 snd_hda_codec_write(codec, dst, 0,
458 AC_VERB_SET_AMP_GAIN_MUTE, val);
459 }
460 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
461 continue;
462 if (have_mute(codec, src, HDA_OUTPUT)) {
463 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
464 snd_hda_codec_write(codec, src, 0,
465 AC_VERB_SET_AMP_GAIN_MUTE, val);
466 }
467 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200468}
469
470/* set the given pin as output */
471static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
472 int pin_type)
473{
474 if (!pin)
475 return;
476 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
477 pin_type);
478 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
479 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200480 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100481}
482
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200483static void via_auto_init_output(struct hda_codec *codec,
484 struct nid_path *path, int pin_type,
485 bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200486{
487 struct via_spec *spec = codec->spec;
488 unsigned int caps;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200489 hda_nid_t pin, nid;
490 int i, idx;
Takashi Iwai5d417622011-06-20 11:32:27 +0200491
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200492 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200493 return;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200494 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200495
496 init_output_pin(codec, pin, pin_type);
497 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
498 if (caps & AC_AMPCAP_MUTE) {
499 unsigned int val;
500 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
501 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
502 AMP_OUT_MUTE | val);
503 }
504
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200505 activate_output_path(codec, path, true, force);
506
507 /* initialize the AA-path */
508 if (!spec->aa_mix_nid)
509 return;
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200510 for (i = path->depth - 1; i > 0; i--) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200511 nid = path->path[i];
512 idx = get_connection_index(codec, nid, spec->aa_mix_nid);
513 if (idx >= 0) {
514 if (have_mute(codec, nid, HDA_INPUT))
515 snd_hda_codec_write(codec, nid, 0,
516 AC_VERB_SET_AMP_GAIN_MUTE,
517 AMP_IN_UNMUTE(idx));
518 break;
519 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200520 }
521}
522
Joseph Chanc577b8a2006-11-29 15:29:40 +0100523static void via_auto_init_multi_out(struct hda_codec *codec)
524{
525 struct via_spec *spec = codec->spec;
526 int i;
527
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200528 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200529 via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, true);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100530}
531
532static void via_auto_init_hp_out(struct hda_codec *codec)
533{
534 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100535
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200536 if (!spec->hp_dac_nid) {
537 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
538 return;
539 }
540 if (spec->hp_independent_mode) {
541 activate_output_path(codec, &spec->hp_dep_path, false, false);
542 via_auto_init_output(codec, &spec->hp_path, PIN_HP, true);
543 } else {
544 activate_output_path(codec, &spec->hp_path, false, false);
545 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
546 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100547}
548
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200549static void via_auto_init_speaker_out(struct hda_codec *codec)
550{
551 struct via_spec *spec = codec->spec;
552
553 if (spec->autocfg.speaker_outs)
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200554 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, true);
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200555}
556
Takashi Iwaif4a78282011-06-17 18:46:48 +0200557static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200558
Joseph Chanc577b8a2006-11-29 15:29:40 +0100559static void via_auto_init_analog_input(struct hda_codec *codec)
560{
561 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200562 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200563 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200564 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200565 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100566
Takashi Iwai096a8852011-06-20 12:09:02 +0200567 /* init ADCs */
568 for (i = 0; i < spec->num_adc_nids; i++) {
569 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
570 AC_VERB_SET_AMP_GAIN_MUTE,
571 AMP_IN_UNMUTE(0));
572 }
573
574 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200575 for (i = 0; i < cfg->num_inputs; i++) {
576 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200577 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200578 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100579 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200580 ctl = PIN_VREF50;
581 else
582 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100583 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200584 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100585 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200586
587 /* init input-src */
588 for (i = 0; i < spec->num_adc_nids; i++) {
589 const struct hda_input_mux *imux = spec->input_mux;
590 if (!imux || !spec->mux_nids[i])
591 continue;
592 snd_hda_codec_write(codec, spec->mux_nids[i], 0,
593 AC_VERB_SET_CONNECT_SEL,
594 imux->items[spec->cur_mux[i]].index);
595 }
596
597 /* init aa-mixer */
598 if (!spec->aa_mix_nid)
599 return;
600 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
601 ARRAY_SIZE(conn));
602 for (i = 0; i < num_conns; i++) {
603 unsigned int caps = get_wcaps(codec, conn[i]);
604 if (get_wcaps_type(caps) == AC_WID_PIN)
605 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
606 AC_VERB_SET_AMP_GAIN_MUTE,
607 AMP_IN_MUTE(i));
608 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100609}
Lydia Wangf5271102009-10-10 19:07:35 +0800610
611static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
612 unsigned int *affected_parm)
613{
614 unsigned parm;
615 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
616 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
617 >> AC_DEFCFG_MISC_SHIFT
618 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800619 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200620 unsigned present = 0;
621
622 no_presence |= spec->no_pin_power_ctl;
623 if (!no_presence)
624 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200625 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800626 || ((no_presence || present)
627 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800628 *affected_parm = AC_PWRST_D0; /* if it's connected */
629 parm = AC_PWRST_D0;
630 } else
631 parm = AC_PWRST_D3;
632
633 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
634}
635
Takashi Iwai24088a52011-06-17 16:59:21 +0200636static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
637 struct snd_ctl_elem_info *uinfo)
638{
639 static const char * const texts[] = {
640 "Disabled", "Enabled"
641 };
642
643 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
644 uinfo->count = 1;
645 uinfo->value.enumerated.items = 2;
646 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
647 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
648 strcpy(uinfo->value.enumerated.name,
649 texts[uinfo->value.enumerated.item]);
650 return 0;
651}
652
653static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
654 struct snd_ctl_elem_value *ucontrol)
655{
656 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
657 struct via_spec *spec = codec->spec;
658 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
659 return 0;
660}
661
662static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
663 struct snd_ctl_elem_value *ucontrol)
664{
665 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
666 struct via_spec *spec = codec->spec;
667 unsigned int val = !ucontrol->value.enumerated.item[0];
668
669 if (val == spec->no_pin_power_ctl)
670 return 0;
671 spec->no_pin_power_ctl = val;
672 set_widgets_power_state(codec);
673 return 1;
674}
675
676static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
677 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
678 .name = "Dynamic Power-Control",
679 .info = via_pin_power_ctl_info,
680 .get = via_pin_power_ctl_get,
681 .put = via_pin_power_ctl_put,
682};
683
684
Joseph Chanc577b8a2006-11-29 15:29:40 +0100685/*
686 * input MUX handling
687 */
688static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
689 struct snd_ctl_elem_info *uinfo)
690{
691 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
692 struct via_spec *spec = codec->spec;
693 return snd_hda_input_mux_info(spec->input_mux, uinfo);
694}
695
696static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
697 struct snd_ctl_elem_value *ucontrol)
698{
699 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
700 struct via_spec *spec = codec->spec;
701 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
702
703 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
704 return 0;
705}
706
707static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
708 struct snd_ctl_elem_value *ucontrol)
709{
710 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
711 struct via_spec *spec = codec->spec;
712 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800713 int ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100714
Takashi Iwai337b9d02009-07-07 18:18:59 +0200715 if (!spec->mux_nids[adc_idx])
716 return -EINVAL;
Lydia Wanga80e6e32009-10-10 19:07:55 +0800717 /* switch to D0 beofre change index */
718 if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
719 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
720 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
721 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800722
723 ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
724 spec->mux_nids[adc_idx],
725 &spec->cur_mux[adc_idx]);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800726 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800727 set_widgets_power_state(codec);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800728
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800729 return ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100730}
731
Harald Welte0aa62ae2008-09-09 15:58:27 +0800732static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
733 struct snd_ctl_elem_info *uinfo)
734{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200735 static const char * const texts[] = { "OFF", "ON" };
736
737 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
738 uinfo->count = 1;
739 uinfo->value.enumerated.items = 2;
740 if (uinfo->value.enumerated.item >= 2)
741 uinfo->value.enumerated.item = 1;
742 strcpy(uinfo->value.enumerated.name,
743 texts[uinfo->value.enumerated.item]);
744 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800745}
746
747static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
748 struct snd_ctl_elem_value *ucontrol)
749{
750 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800751 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800752
Takashi Iwaiece8d042011-06-19 16:24:21 +0200753 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800754 return 0;
755}
756
Harald Welte0aa62ae2008-09-09 15:58:27 +0800757static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
758 struct snd_ctl_elem_value *ucontrol)
759{
760 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
761 struct via_spec *spec = codec->spec;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200762
763 spec->hp_independent_mode = !!ucontrol->value.enumerated.item[0];
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200764 if (spec->hp_independent_mode) {
765 activate_output_path(codec, &spec->hp_dep_path, false, false);
766 activate_output_path(codec, &spec->hp_path, true, false);
767 } else {
768 activate_output_path(codec, &spec->hp_path, false, false);
769 activate_output_path(codec, &spec->hp_dep_path, true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200770 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800771
Lydia Wangce0e5a92011-03-22 16:22:37 +0800772 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800773 set_widgets_power_state(codec);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800774 return 0;
775}
776
Takashi Iwaiece8d042011-06-19 16:24:21 +0200777static const struct snd_kcontrol_new via_hp_mixer = {
778 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
779 .name = "Independent HP",
780 .info = via_independent_hp_info,
781 .get = via_independent_hp_get,
782 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800783};
784
Takashi Iwai3d83e572010-04-14 14:36:23 +0200785static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100786{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200787 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100788 struct snd_kcontrol_new *knew;
789 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100790
Takashi Iwaiece8d042011-06-19 16:24:21 +0200791 nid = spec->autocfg.hp_pins[0];
792 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200793 if (knew == NULL)
794 return -ENOMEM;
795
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100796 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100797
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100798 return 0;
799}
800
Lydia Wang1564b282009-10-10 19:07:52 +0800801static void notify_aa_path_ctls(struct hda_codec *codec)
802{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200803 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800804 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800805
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200806 for (i = 0; i < spec->smart51_nums; i++) {
807 struct snd_kcontrol *ctl;
808 struct snd_ctl_elem_id id;
809 memset(&id, 0, sizeof(id));
810 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
811 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800812 ctl = snd_hda_find_mixer_ctl(codec, id.name);
813 if (ctl)
814 snd_ctl_notify(codec->bus->card,
815 SNDRV_CTL_EVENT_MASK_VALUE,
816 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800817 }
818}
819
820static void mute_aa_path(struct hda_codec *codec, int mute)
821{
822 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200823 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800824 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200825
Lydia Wang1564b282009-10-10 19:07:52 +0800826 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200827 for (i = 0; i < spec->smart51_nums; i++) {
828 if (spec->smart51_idxs[i] < 0)
829 continue;
830 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
831 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800832 HDA_AMP_MUTE, val);
833 }
834}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200835
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200836static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
837{
838 struct via_spec *spec = codec->spec;
839 int i;
840
841 for (i = 0; i < spec->smart51_nums; i++)
842 if (spec->smart51_pins[i] == pin)
843 return true;
844 return false;
845}
846
Lydia Wang1564b282009-10-10 19:07:52 +0800847static int via_smart51_get(struct snd_kcontrol *kcontrol,
848 struct snd_ctl_elem_value *ucontrol)
849{
850 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
851 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800852
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200853 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800854 return 0;
855}
856
857static int via_smart51_put(struct snd_kcontrol *kcontrol,
858 struct snd_ctl_elem_value *ucontrol)
859{
860 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
861 struct via_spec *spec = codec->spec;
862 int out_in = *ucontrol->value.integer.value
863 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800864 int i;
865
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200866 for (i = 0; i < spec->smart51_nums; i++) {
867 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200868 unsigned int parm;
869
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200870 parm = snd_hda_codec_read(codec, nid, 0,
871 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
872 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
873 parm |= out_in;
874 snd_hda_codec_write(codec, nid, 0,
875 AC_VERB_SET_PIN_WIDGET_CONTROL,
876 parm);
877 if (out_in == AC_PINCTL_OUT_EN) {
878 mute_aa_path(codec, 1);
879 notify_aa_path_ctls(codec);
880 }
Lydia Wang1564b282009-10-10 19:07:52 +0800881 }
882 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800883 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800884 return 1;
885}
886
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200887static const struct snd_kcontrol_new via_smart51_mixer = {
888 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
889 .name = "Smart 5.1",
890 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200891 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200892 .get = via_smart51_get,
893 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800894};
895
Takashi Iwaif4a78282011-06-17 18:46:48 +0200896static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100897{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200898 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100899
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200900 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800901 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200902 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100903 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100904 return 0;
905}
906
Takashi Iwaiada509e2011-06-20 15:40:19 +0200907/* check AA path's mute status */
908static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800909{
Lydia Wangf5271102009-10-10 19:07:35 +0800910 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200911 const struct hda_amp_list *p;
912 int i, ch, v;
913
914 for (i = 0; i < spec->num_loopbacks; i++) {
915 p = &spec->loopback_list[i];
916 for (ch = 0; ch < 2; ch++) {
917 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
918 p->idx);
919 if (!(v & HDA_AMP_MUTE) && v > 0)
920 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800921 }
922 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200923 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800924}
925
926/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200927static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800928{
929 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200930 bool enable;
931 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800932
Takashi Iwaiada509e2011-06-20 15:40:19 +0200933 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800934
935 /* decide low current mode's verb & parameter */
936 switch (spec->codec_type) {
937 case VT1708B_8CH:
938 case VT1708B_4CH:
939 verb = 0xf70;
940 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
941 break;
942 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800943 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800944 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800945 verb = 0xf73;
946 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
947 break;
948 case VT1702:
949 verb = 0xf73;
950 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
951 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800952 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800953 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800954 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800955 verb = 0xf93;
956 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
957 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800958 default:
959 return; /* other codecs are not supported */
960 }
961 /* send verb */
962 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
963}
964
Joseph Chanc577b8a2006-11-29 15:29:40 +0100965/*
966 * generic initialization of ADC, input mixers and output mixers
967 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200968static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800969 /* power down jack detect function */
970 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +0100971 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100972};
973
Takashi Iwaiada509e2011-06-20 15:40:19 +0200974static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200975{
Takashi Iwaiada509e2011-06-20 15:40:19 +0200976 struct via_spec *spec = codec->spec;
977
978 if (active)
979 spec->num_active_streams++;
980 else
981 spec->num_active_streams--;
982 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200983}
984
Takashi Iwaiece8d042011-06-19 16:24:21 +0200985static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100986 struct hda_codec *codec,
987 struct snd_pcm_substream *substream)
988{
989 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200990 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +0200991
992 if (!spec->hp_independent_mode)
993 spec->multiout.hp_nid = spec->hp_dac_nid;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200994 set_stream_active(codec, true);
995 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
996 hinfo);
997 if (err < 0) {
998 spec->multiout.hp_nid = 0;
999 set_stream_active(codec, false);
1000 return err;
1001 }
1002 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001003}
1004
Takashi Iwaiece8d042011-06-19 16:24:21 +02001005static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001006 struct hda_codec *codec,
1007 struct snd_pcm_substream *substream)
1008{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001009 struct via_spec *spec = codec->spec;
1010
1011 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001012 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001013 return 0;
1014}
1015
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001016static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1017 struct hda_codec *codec,
1018 struct snd_pcm_substream *substream)
1019{
1020 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001021
Takashi Iwaiece8d042011-06-19 16:24:21 +02001022 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001023 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001024 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1025 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001026 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001027 return 0;
1028}
1029
1030static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1031 struct hda_codec *codec,
1032 struct snd_pcm_substream *substream)
1033{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001034 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001035 return 0;
1036}
1037
1038static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1039 struct hda_codec *codec,
1040 unsigned int stream_tag,
1041 unsigned int format,
1042 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001043{
1044 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001045
Takashi Iwaiece8d042011-06-19 16:24:21 +02001046 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1047 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001048 vt1708_start_hp_work(spec);
1049 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001050}
1051
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001052static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1053 struct hda_codec *codec,
1054 unsigned int stream_tag,
1055 unsigned int format,
1056 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001057{
1058 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001059
Takashi Iwaiece8d042011-06-19 16:24:21 +02001060 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1061 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001062 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001063 return 0;
1064}
1065
1066static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1067 struct hda_codec *codec,
1068 struct snd_pcm_substream *substream)
1069{
1070 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001071
Takashi Iwaiece8d042011-06-19 16:24:21 +02001072 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001073 vt1708_stop_hp_work(spec);
1074 return 0;
1075}
1076
1077static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1078 struct hda_codec *codec,
1079 struct snd_pcm_substream *substream)
1080{
1081 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001082
Takashi Iwaiece8d042011-06-19 16:24:21 +02001083 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001084 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001085 return 0;
1086}
1087
Joseph Chanc577b8a2006-11-29 15:29:40 +01001088/*
1089 * Digital out
1090 */
1091static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1092 struct hda_codec *codec,
1093 struct snd_pcm_substream *substream)
1094{
1095 struct via_spec *spec = codec->spec;
1096 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1097}
1098
1099static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1100 struct hda_codec *codec,
1101 struct snd_pcm_substream *substream)
1102{
1103 struct via_spec *spec = codec->spec;
1104 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1105}
1106
Harald Welte5691ec72008-09-15 22:42:26 +08001107static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001108 struct hda_codec *codec,
1109 unsigned int stream_tag,
1110 unsigned int format,
1111 struct snd_pcm_substream *substream)
1112{
1113 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001114 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1115 stream_tag, format, substream);
1116}
Harald Welte5691ec72008-09-15 22:42:26 +08001117
Takashi Iwai9da29272009-05-07 16:31:14 +02001118static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1119 struct hda_codec *codec,
1120 struct snd_pcm_substream *substream)
1121{
1122 struct via_spec *spec = codec->spec;
1123 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001124 return 0;
1125}
1126
Joseph Chanc577b8a2006-11-29 15:29:40 +01001127/*
1128 * Analog capture
1129 */
1130static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1131 struct hda_codec *codec,
1132 unsigned int stream_tag,
1133 unsigned int format,
1134 struct snd_pcm_substream *substream)
1135{
1136 struct via_spec *spec = codec->spec;
1137
1138 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1139 stream_tag, 0, format);
1140 return 0;
1141}
1142
1143static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1144 struct hda_codec *codec,
1145 struct snd_pcm_substream *substream)
1146{
1147 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001148 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001149 return 0;
1150}
1151
Takashi Iwai9af74212011-06-18 16:17:45 +02001152static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001153 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001154 .channels_min = 2,
1155 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001156 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001157 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001158 .open = via_playback_multi_pcm_open,
1159 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001160 .prepare = via_playback_multi_pcm_prepare,
1161 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001162 },
1163};
1164
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001165static const struct hda_pcm_stream via_pcm_hp_playback = {
1166 .substreams = 1,
1167 .channels_min = 2,
1168 .channels_max = 2,
1169 /* NID is set in via_build_pcms */
1170 .ops = {
1171 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001172 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001173 .prepare = via_playback_hp_pcm_prepare,
1174 .cleanup = via_playback_hp_pcm_cleanup
1175 },
1176};
1177
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001178static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001179 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001180 .channels_min = 2,
1181 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001182 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001183 /* We got noisy outputs on the right channel on VT1708 when
1184 * 24bit samples are used. Until any workaround is found,
1185 * disable the 24bit format, so far.
1186 */
1187 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1188 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001189 .open = via_playback_multi_pcm_open,
1190 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001191 .prepare = via_playback_multi_pcm_prepare,
1192 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001193 },
1194};
1195
Takashi Iwai9af74212011-06-18 16:17:45 +02001196static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001197 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001198 .channels_min = 2,
1199 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001200 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001201 .ops = {
1202 .prepare = via_capture_pcm_prepare,
1203 .cleanup = via_capture_pcm_cleanup
1204 },
1205};
1206
Takashi Iwai9af74212011-06-18 16:17:45 +02001207static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001208 .substreams = 1,
1209 .channels_min = 2,
1210 .channels_max = 2,
1211 /* NID is set in via_build_pcms */
1212 .ops = {
1213 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001214 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001215 .prepare = via_dig_playback_pcm_prepare,
1216 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001217 },
1218};
1219
Takashi Iwai9af74212011-06-18 16:17:45 +02001220static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001221 .substreams = 1,
1222 .channels_min = 2,
1223 .channels_max = 2,
1224};
1225
Takashi Iwai370bafb2011-06-20 12:47:45 +02001226/*
1227 * slave controls for virtual master
1228 */
1229static const char * const via_slave_vols[] = {
1230 "Front Playback Volume",
1231 "Surround Playback Volume",
1232 "Center Playback Volume",
1233 "LFE Playback Volume",
1234 "Side Playback Volume",
1235 "Headphone Playback Volume",
1236 "Speaker Playback Volume",
1237 NULL,
1238};
1239
1240static const char * const via_slave_sws[] = {
1241 "Front Playback Switch",
1242 "Surround Playback Switch",
1243 "Center Playback Switch",
1244 "LFE Playback Switch",
1245 "Side Playback Switch",
1246 "Headphone Playback Switch",
1247 "Speaker Playback Switch",
1248 NULL,
1249};
1250
Joseph Chanc577b8a2006-11-29 15:29:40 +01001251static int via_build_controls(struct hda_codec *codec)
1252{
1253 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001254 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001255 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001256
Takashi Iwai24088a52011-06-17 16:59:21 +02001257 if (spec->set_widgets_power_state)
1258 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1259 return -ENOMEM;
1260
Joseph Chanc577b8a2006-11-29 15:29:40 +01001261 for (i = 0; i < spec->num_mixers; i++) {
1262 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1263 if (err < 0)
1264 return err;
1265 }
1266
1267 if (spec->multiout.dig_out_nid) {
1268 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001269 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001270 spec->multiout.dig_out_nid);
1271 if (err < 0)
1272 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001273 err = snd_hda_create_spdif_share_sw(codec,
1274 &spec->multiout);
1275 if (err < 0)
1276 return err;
1277 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001278 }
1279 if (spec->dig_in_nid) {
1280 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1281 if (err < 0)
1282 return err;
1283 }
Lydia Wang17314372009-10-10 19:07:37 +08001284
Takashi Iwai370bafb2011-06-20 12:47:45 +02001285 /* if we have no master control, let's create it */
1286 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1287 unsigned int vmaster_tlv[4];
1288 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1289 HDA_OUTPUT, vmaster_tlv);
1290 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1291 vmaster_tlv, via_slave_vols);
1292 if (err < 0)
1293 return err;
1294 }
1295 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1296 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1297 NULL, via_slave_sws);
1298 if (err < 0)
1299 return err;
1300 }
1301
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001302 /* assign Capture Source enums to NID */
1303 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1304 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001305 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001306 if (err < 0)
1307 return err;
1308 }
1309
Lydia Wang17314372009-10-10 19:07:37 +08001310 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001311 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001312 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001313
Takashi Iwai603c4012008-07-30 15:01:44 +02001314 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001315 return 0;
1316}
1317
1318static int via_build_pcms(struct hda_codec *codec)
1319{
1320 struct via_spec *spec = codec->spec;
1321 struct hda_pcm *info = spec->pcm_rec;
1322
1323 codec->num_pcms = 1;
1324 codec->pcm_info = info;
1325
Takashi Iwai82673bc2011-06-17 16:24:21 +02001326 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1327 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001328 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001329
1330 if (!spec->stream_analog_playback)
1331 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001332 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001333 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001334 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1335 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001336 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1337 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001338
1339 if (!spec->stream_analog_capture)
1340 spec->stream_analog_capture = &via_pcm_analog_capture;
1341 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1342 *spec->stream_analog_capture;
1343 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1344 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1345 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001346
1347 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1348 codec->num_pcms++;
1349 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001350 snprintf(spec->stream_name_digital,
1351 sizeof(spec->stream_name_digital),
1352 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001353 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001354 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001355 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001356 if (!spec->stream_digital_playback)
1357 spec->stream_digital_playback =
1358 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001359 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001360 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001361 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1362 spec->multiout.dig_out_nid;
1363 }
1364 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001365 if (!spec->stream_digital_capture)
1366 spec->stream_digital_capture =
1367 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001368 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001369 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001370 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1371 spec->dig_in_nid;
1372 }
1373 }
1374
Takashi Iwaiece8d042011-06-19 16:24:21 +02001375 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001376 codec->num_pcms++;
1377 info++;
1378 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1379 "%s HP", codec->chip_name);
1380 info->name = spec->stream_name_hp;
1381 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1382 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001383 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001384 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001385 return 0;
1386}
1387
1388static void via_free(struct hda_codec *codec)
1389{
1390 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001391
1392 if (!spec)
1393 return;
1394
Takashi Iwai603c4012008-07-30 15:01:44 +02001395 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001396 vt1708_stop_hp_work(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001397 kfree(codec->spec);
1398}
1399
Takashi Iwai64be2852011-06-17 16:51:39 +02001400/* mute/unmute outputs */
1401static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1402 hda_nid_t *pins, bool mute)
1403{
1404 int i;
1405 for (i = 0; i < num_pins; i++)
1406 snd_hda_codec_write(codec, pins[i], 0,
1407 AC_VERB_SET_PIN_WIDGET_CONTROL,
1408 mute ? 0 : PIN_OUT);
1409}
1410
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001411/* mute internal speaker if line-out is plugged */
1412static void via_line_automute(struct hda_codec *codec, int present)
1413{
1414 struct via_spec *spec = codec->spec;
1415
1416 if (!spec->autocfg.speaker_outs)
1417 return;
1418 if (!present)
1419 present = snd_hda_jack_detect(codec,
1420 spec->autocfg.line_out_pins[0]);
1421 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1422 spec->autocfg.speaker_pins,
1423 present);
1424}
1425
Harald Welte69e52a82008-09-09 15:57:32 +08001426/* mute internal speaker if HP is plugged */
1427static void via_hp_automute(struct hda_codec *codec)
1428{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001429 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001430 struct via_spec *spec = codec->spec;
1431
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001432 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001433 int nums;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001434 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001435 if (spec->smart51_enabled)
1436 nums = spec->autocfg.line_outs + spec->smart51_nums;
1437 else
1438 nums = spec->autocfg.line_outs;
1439 toggle_output_mutes(codec, nums,
Takashi Iwai64be2852011-06-17 16:51:39 +02001440 spec->autocfg.line_out_pins,
1441 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001442 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001443 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001444}
1445
Harald Welte69e52a82008-09-09 15:57:32 +08001446static void via_gpio_control(struct hda_codec *codec)
1447{
1448 unsigned int gpio_data;
1449 unsigned int vol_counter;
1450 unsigned int vol;
1451 unsigned int master_vol;
1452
1453 struct via_spec *spec = codec->spec;
1454
1455 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1456 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1457
1458 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1459 0xF84, 0) & 0x3F0000) >> 16;
1460
1461 vol = vol_counter & 0x1F;
1462 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1463 AC_VERB_GET_AMP_GAIN_MUTE,
1464 AC_AMP_GET_INPUT);
1465
1466 if (gpio_data == 0x02) {
1467 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001468 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1469 AC_VERB_SET_PIN_WIDGET_CONTROL,
1470 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001471 if (vol_counter & 0x20) {
1472 /* decrease volume */
1473 if (vol > master_vol)
1474 vol = master_vol;
1475 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1476 0, HDA_AMP_VOLMASK,
1477 master_vol-vol);
1478 } else {
1479 /* increase volume */
1480 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1481 HDA_AMP_VOLMASK,
1482 ((master_vol+vol) > 0x2A) ? 0x2A :
1483 (master_vol+vol));
1484 }
1485 } else if (!(gpio_data & 0x02)) {
1486 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001487 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1488 AC_VERB_SET_PIN_WIDGET_CONTROL,
1489 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001490 }
1491}
1492
1493/* unsolicited event for jack sensing */
1494static void via_unsol_event(struct hda_codec *codec,
1495 unsigned int res)
1496{
1497 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001498
Lydia Wanga34df192009-10-10 19:08:01 +08001499 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001500 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001501
1502 res &= ~VIA_JACK_EVENT;
1503
1504 if (res == VIA_HP_EVENT)
1505 via_hp_automute(codec);
1506 else if (res == VIA_GPIO_EVENT)
1507 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001508 else if (res == VIA_LINE_EVENT)
1509 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001510}
1511
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001512#ifdef SND_HDA_NEEDS_RESUME
1513static int via_suspend(struct hda_codec *codec, pm_message_t state)
1514{
1515 struct via_spec *spec = codec->spec;
1516 vt1708_stop_hp_work(spec);
1517 return 0;
1518}
1519#endif
1520
Takashi Iwaicb53c622007-08-10 17:21:45 +02001521#ifdef CONFIG_SND_HDA_POWER_SAVE
1522static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1523{
1524 struct via_spec *spec = codec->spec;
1525 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1526}
1527#endif
1528
Joseph Chanc577b8a2006-11-29 15:29:40 +01001529/*
1530 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001531
1532static int via_init(struct hda_codec *codec);
1533
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001534static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001535 .build_controls = via_build_controls,
1536 .build_pcms = via_build_pcms,
1537 .init = via_init,
1538 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001539 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001540#ifdef SND_HDA_NEEDS_RESUME
1541 .suspend = via_suspend,
1542#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001543#ifdef CONFIG_SND_HDA_POWER_SAVE
1544 .check_power_status = via_check_power_status,
1545#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001546};
1547
Takashi Iwai4a796162011-06-17 17:53:38 +02001548static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001549{
Takashi Iwai4a796162011-06-17 17:53:38 +02001550 struct via_spec *spec = codec->spec;
1551 int i;
1552
1553 for (i = 0; i < spec->multiout.num_dacs; i++) {
1554 if (spec->multiout.dac_nids[i] == dac)
1555 return false;
1556 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001557 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001558 return false;
1559 return true;
1560}
1561
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001562static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001563 hda_nid_t target_dac, struct nid_path *path,
1564 int depth, int wid_type)
1565{
1566 hda_nid_t conn[8];
1567 int i, nums;
1568
1569 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1570 for (i = 0; i < nums; i++) {
1571 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1572 continue;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001573 if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
1574 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001575 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001576 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001577 return false;
1578 for (i = 0; i < nums; i++) {
1579 unsigned int type;
1580 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1581 if (type == AC_WID_AUD_OUT ||
1582 (wid_type != -1 && type != wid_type))
1583 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001584 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001585 path, depth + 1, AC_WID_AUD_SEL))
1586 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001587 }
1588 return false;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001589
1590 found:
1591 path->path[path->depth] = conn[i];
1592 path->idx[path->depth] = i;
1593 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1594 path->multi[path->depth] = 1;
1595 path->depth++;
1596 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001597}
1598
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001599static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1600 hda_nid_t target_dac, struct nid_path *path)
1601{
1602 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1603 path->path[path->depth] = nid;
1604 path->depth++;
1605 return true;
1606 }
1607 return false;
1608}
1609
Takashi Iwai4a796162011-06-17 17:53:38 +02001610static int via_auto_fill_dac_nids(struct hda_codec *codec)
1611{
1612 struct via_spec *spec = codec->spec;
1613 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001614 int i;
1615 hda_nid_t nid;
1616
Joseph Chanc577b8a2006-11-29 15:29:40 +01001617 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001618 spec->multiout.num_dacs = cfg->line_outs;
1619 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001620 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001621 if (!nid)
1622 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001623 if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
1624 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001625 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001626 return 0;
1627}
1628
Takashi Iwai4a796162011-06-17 17:53:38 +02001629static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001630 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001631{
Takashi Iwai4a796162011-06-17 17:53:38 +02001632 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001633 char name[32];
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001634 hda_nid_t dac, pin, sel, nid;
1635 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001636
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001637 dac = check_dac ? path->path[0] : 0;
1638 pin = path->path[path->depth - 1];
1639 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001640
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;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001645 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1646 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001647 else
1648 nid = 0;
1649 if (nid) {
1650 sprintf(name, "%s Playback Volume", pfx);
1651 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001652 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001653 if (err < 0)
1654 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001655 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001656 }
1657
Takashi Iwai8df2a312011-06-21 11:48:29 +02001658 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001659 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001660 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001661 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001662 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1663 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001664 else
1665 nid = 0;
1666 if (nid) {
1667 sprintf(name, "%s Playback Switch", pfx);
1668 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1669 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1670 if (err < 0)
1671 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001672 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001673 }
1674 return 0;
1675}
1676
Takashi Iwaif4a78282011-06-17 18:46:48 +02001677static void mangle_smart51(struct hda_codec *codec)
1678{
1679 struct via_spec *spec = codec->spec;
1680 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001681 struct auto_pin_cfg_item *ins = cfg->inputs;
1682 int i, j, nums, attr;
1683 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001684
Takashi Iwai0f98c242011-06-21 12:51:33 +02001685 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1686 nums = 0;
1687 for (i = 0; i < cfg->num_inputs; i++) {
1688 unsigned int def;
1689 if (ins[i].type > AUTO_PIN_LINE_IN)
1690 continue;
1691 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1692 if (snd_hda_get_input_pin_attr(def) != attr)
1693 continue;
1694 for (j = 0; j < nums; j++)
1695 if (ins[pins[j]].type < ins[i].type) {
1696 memmove(pins + j + 1, pins + j,
1697 (nums - j - 1) * sizeof(int));
1698 break;
1699 }
1700 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001701 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001702 }
1703 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001704 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001705 for (i = 0; i < nums; i++) {
1706 hda_nid_t pin = ins[pins[i]].pin;
1707 spec->smart51_pins[spec->smart51_nums++] = pin;
1708 cfg->line_out_pins[cfg->line_outs++] = pin;
1709 if (cfg->line_outs == 3)
1710 break;
1711 }
1712 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001713 }
1714}
1715
Takashi Iwai4a796162011-06-17 17:53:38 +02001716/* add playback controls from the parsed DAC table */
1717static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1718{
1719 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001720 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001721 static const char * const chname[4] = {
1722 "Front", "Surround", "C/LFE", "Side"
1723 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001724 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001725 int old_line_outs;
1726
1727 /* check smart51 */
1728 old_line_outs = cfg->line_outs;
1729 if (cfg->line_outs == 1)
1730 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001731
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001732 err = via_auto_fill_dac_nids(codec);
1733 if (err < 0)
1734 return err;
1735
Takashi Iwai4a796162011-06-17 17:53:38 +02001736 for (i = 0; i < cfg->line_outs; i++) {
1737 hda_nid_t pin, dac;
1738 pin = cfg->line_out_pins[i];
1739 dac = spec->multiout.dac_nids[i];
1740 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001741 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001742 if (i == HDA_CLFE) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001743 err = create_ch_ctls(codec, "Center", 1, true,
1744 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001745 if (err < 0)
1746 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001747 err = create_ch_ctls(codec, "LFE", 2, true,
1748 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001749 if (err < 0)
1750 return err;
1751 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001752 const char *pfx = chname[i];
1753 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1754 cfg->line_outs == 1)
1755 pfx = "Speaker";
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001756 err = create_ch_ctls(codec, pfx, 3, true,
1757 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001758 if (err < 0)
1759 return err;
1760 }
1761 }
1762
Takashi Iwai4a796162011-06-17 17:53:38 +02001763 idx = get_connection_index(codec, spec->aa_mix_nid,
1764 spec->multiout.dac_nids[0]);
1765 if (idx >= 0) {
1766 /* add control to mixer */
1767 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1768 "PCM Playback Volume",
1769 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1770 idx, HDA_INPUT));
1771 if (err < 0)
1772 return err;
1773 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1774 "PCM Playback Switch",
1775 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1776 idx, HDA_INPUT));
1777 if (err < 0)
1778 return err;
1779 }
1780
Takashi Iwaif4a78282011-06-17 18:46:48 +02001781 cfg->line_outs = old_line_outs;
1782
Joseph Chanc577b8a2006-11-29 15:29:40 +01001783 return 0;
1784}
1785
Takashi Iwai4a796162011-06-17 17:53:38 +02001786static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001787{
Takashi Iwai4a796162011-06-17 17:53:38 +02001788 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001789 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001790 int err;
1791
1792 if (!pin)
1793 return 0;
1794
Takashi Iwai8df2a312011-06-21 11:48:29 +02001795 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001796 spec->hp_dac_nid = spec->hp_path.path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001797
Takashi Iwaiece8d042011-06-19 16:24:21 +02001798 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001799 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001800 !spec->hp_dac_nid)
1801 return 0;
1802
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001803 if (spec->hp_dac_nid)
1804 path = &spec->hp_path;
1805 else
1806 path = &spec->hp_dep_path;
1807 err = create_ch_ctls(codec, "Headphone", 3, false, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001808 if (err < 0)
1809 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001810 if (spec->hp_dac_nid) {
1811 spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
1812 spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
1813 }
Harald Welte0aa62ae2008-09-09 15:58:27 +08001814
Joseph Chanc577b8a2006-11-29 15:29:40 +01001815 return 0;
1816}
1817
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001818static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1819{
1820 struct via_spec *spec = codec->spec;
1821 hda_nid_t pin, dac;
1822
1823 pin = spec->autocfg.speaker_pins[0];
1824 if (!spec->autocfg.speaker_outs || !pin)
1825 return 0;
1826
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001827 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1828 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001829 spec->multiout.extra_out_nid[0] = dac;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001830 return create_ch_ctls(codec, "Speaker", 3, true,
1831 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001832 }
1833 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001834 &spec->speaker_path))
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001835 return create_ch_ctls(codec, "Speaker", 3, false,
1836 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001837
1838 return 0;
1839}
1840
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001841/* look for ADCs */
1842static int via_fill_adcs(struct hda_codec *codec)
1843{
1844 struct via_spec *spec = codec->spec;
1845 hda_nid_t nid = codec->start_nid;
1846 int i;
1847
1848 for (i = 0; i < codec->num_nodes; i++, nid++) {
1849 unsigned int wcaps = get_wcaps(codec, nid);
1850 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1851 continue;
1852 if (wcaps & AC_WCAP_DIGITAL)
1853 continue;
1854 if (!(wcaps & AC_WCAP_CONN_LIST))
1855 continue;
1856 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1857 return -ENOMEM;
1858 spec->adc_nids[spec->num_adc_nids++] = nid;
1859 }
1860 return 0;
1861}
1862
1863static int get_mux_nids(struct hda_codec *codec);
1864
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001865static const struct snd_kcontrol_new via_input_src_ctl = {
1866 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1867 /* The multiple "Capture Source" controls confuse alsamixer
1868 * So call somewhat different..
1869 */
1870 /* .name = "Capture Source", */
1871 .name = "Input Source",
1872 .info = via_mux_enum_info,
1873 .get = via_mux_enum_get,
1874 .put = via_mux_enum_put,
1875};
1876
Takashi Iwai13af8e72011-06-20 14:05:46 +02001877static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
1878{
1879 struct hda_amp_list *list;
1880
1881 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
1882 return;
1883 list = spec->loopback_list + spec->num_loopbacks;
1884 list->nid = mix;
1885 list->dir = HDA_INPUT;
1886 list->idx = idx;
1887 spec->num_loopbacks++;
1888 spec->loopback.amplist = spec->loopback_list;
1889}
Takashi Iwai13af8e72011-06-20 14:05:46 +02001890
Joseph Chanc577b8a2006-11-29 15:29:40 +01001891/* create playback/capture controls for input pins */
Takashi Iwai620e2b22011-06-17 17:19:19 +02001892static int via_auto_create_analog_input_ctls(struct hda_codec *codec,
1893 const struct auto_pin_cfg *cfg)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001894{
Takashi Iwai10a20af2010-09-09 16:28:02 +02001895 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001896 struct hda_input_mux *imux = &spec->private_imux[0];
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001897 int i, j, err, idx, idx2, type, type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02001898 const char *prev_label = NULL;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001899 hda_nid_t cap_nid;
1900 hda_nid_t pin_idxs[8];
1901 int num_idxs;
1902
1903 err = via_fill_adcs(codec);
1904 if (err < 0)
1905 return err;
1906 err = get_mux_nids(codec);
1907 if (err < 0)
1908 return err;
1909 cap_nid = spec->mux_nids[0];
1910
1911 num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
1912 ARRAY_SIZE(pin_idxs));
1913 if (num_idxs <= 0)
1914 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001915
1916 /* for internal loopback recording select */
Takashi Iwaif3268512010-08-30 11:00:19 +02001917 for (idx = 0; idx < num_idxs; idx++) {
Takashi Iwai620e2b22011-06-17 17:19:19 +02001918 if (pin_idxs[idx] == spec->aa_mix_nid) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001919 snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
Takashi Iwaif3268512010-08-30 11:00:19 +02001920 break;
1921 }
1922 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001923
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001924 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001925 const char *label;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001926 type = cfg->inputs[i].type;
Takashi Iwaif3268512010-08-30 11:00:19 +02001927 for (idx = 0; idx < num_idxs; idx++)
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001928 if (pin_idxs[idx] == cfg->inputs[i].pin)
Takashi Iwaif3268512010-08-30 11:00:19 +02001929 break;
1930 if (idx >= num_idxs)
1931 continue;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02001932 label = hda_get_autocfg_input_label(codec, cfg, i);
1933 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001934 type_idx++;
1935 else
1936 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02001937 prev_label = label;
Takashi Iwai620e2b22011-06-17 17:19:19 +02001938 idx2 = get_connection_index(codec, spec->aa_mix_nid,
1939 pin_idxs[idx]);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001940 if (idx2 >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08001941 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwai620e2b22011-06-17 17:19:19 +02001942 idx2, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001943 if (err < 0)
1944 return err;
1945 add_loopback_list(spec, spec->aa_mix_nid, idx2);
1946 }
Takashi Iwai10a20af2010-09-09 16:28:02 +02001947 snd_hda_add_imux_item(imux, label, idx, NULL);
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001948
1949 /* remember the label for smart51 control */
1950 for (j = 0; j < spec->smart51_nums; j++) {
1951 if (spec->smart51_pins[j] == cfg->inputs[i].pin) {
1952 spec->smart51_idxs[j] = idx;
1953 spec->smart51_labels[j] = label;
1954 break;
1955 }
1956 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001957 }
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001958
1959 /* create capture mixer elements */
1960 for (i = 0; i < spec->num_adc_nids; i++) {
1961 hda_nid_t adc = spec->adc_nids[i];
1962 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
1963 "Capture Volume", i,
1964 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1965 HDA_INPUT));
1966 if (err < 0)
1967 return err;
1968 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1969 "Capture Switch", i,
1970 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1971 HDA_INPUT));
1972 if (err < 0)
1973 return err;
1974 }
1975
1976 /* input-source control */
1977 for (i = 0; i < spec->num_adc_nids; i++)
1978 if (!spec->mux_nids[i])
1979 break;
1980 if (i) {
1981 struct snd_kcontrol_new *knew;
1982 knew = via_clone_control(spec, &via_input_src_ctl);
1983 if (!knew)
1984 return -ENOMEM;
1985 knew->count = i;
1986 }
1987
1988 /* mic-boosts */
1989 for (i = 0; i < cfg->num_inputs; i++) {
1990 hda_nid_t pin = cfg->inputs[i].pin;
1991 unsigned int caps;
1992 const char *label;
1993 char name[32];
1994
1995 if (cfg->inputs[i].type != AUTO_PIN_MIC)
1996 continue;
1997 caps = query_amp_caps(codec, pin, HDA_INPUT);
1998 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
1999 continue;
2000 label = hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwai30f7c5d2011-06-21 08:37:41 +02002001 snprintf(name, sizeof(name), "%s Boost Volume", label);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002002 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2003 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2004 if (err < 0)
2005 return err;
2006 }
2007
Joseph Chanc577b8a2006-11-29 15:29:40 +01002008 return 0;
2009}
2010
Harald Welte76d9b0d2008-09-09 15:50:37 +08002011static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2012{
2013 unsigned int def_conf;
2014 unsigned char seqassoc;
2015
Takashi Iwai2f334f92009-02-20 14:37:42 +01002016 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002017 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2018 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002019 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2020 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2021 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2022 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002023 }
2024
2025 return;
2026}
2027
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002028static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002029 struct snd_ctl_elem_value *ucontrol)
2030{
2031 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2032 struct via_spec *spec = codec->spec;
2033
2034 if (spec->codec_type != VT1708)
2035 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002036 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002037 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002038 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002039 return 0;
2040}
2041
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002042static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002043 struct snd_ctl_elem_value *ucontrol)
2044{
2045 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2046 struct via_spec *spec = codec->spec;
2047 int change;
2048
2049 if (spec->codec_type != VT1708)
2050 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002051 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002052 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002053 == !spec->vt1708_jack_detect;
2054 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002055 mute_aa_path(codec, 1);
2056 notify_aa_path_ctls(codec);
2057 }
2058 return change;
2059}
2060
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002061static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2062 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2063 .name = "Jack Detect",
2064 .count = 1,
2065 .info = snd_ctl_boolean_mono_info,
2066 .get = vt1708_jack_detect_get,
2067 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002068};
2069
Takashi Iwai12daef62011-06-18 17:45:49 +02002070static void fill_dig_outs(struct hda_codec *codec);
2071static void fill_dig_in(struct hda_codec *codec);
2072
2073static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002074{
2075 struct via_spec *spec = codec->spec;
2076 int err;
2077
2078 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2079 if (err < 0)
2080 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002081 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002082 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002083
Takashi Iwai4a796162011-06-17 17:53:38 +02002084 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002085 if (err < 0)
2086 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002087 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002088 if (err < 0)
2089 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002090 err = via_auto_create_speaker_ctls(codec);
2091 if (err < 0)
2092 return err;
Takashi Iwai620e2b22011-06-17 17:19:19 +02002093 err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002094 if (err < 0)
2095 return err;
2096
2097 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2098
Takashi Iwai12daef62011-06-18 17:45:49 +02002099 fill_dig_outs(codec);
2100 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002101
Takashi Iwai603c4012008-07-30 15:01:44 +02002102 if (spec->kctls.list)
2103 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002104
Takashi Iwai096a8852011-06-20 12:09:02 +02002105 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002106
Harald Welte0aa62ae2008-09-09 15:58:27 +08002107 spec->input_mux = &spec->private_imux[0];
2108
Takashi Iwai8df2a312011-06-21 11:48:29 +02002109 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002110 err = via_hp_build(codec);
2111 if (err < 0)
2112 return err;
2113 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002114
Takashi Iwaif4a78282011-06-17 18:46:48 +02002115 err = via_smart51_build(codec);
2116 if (err < 0)
2117 return err;
2118
Takashi Iwai5d417622011-06-20 11:32:27 +02002119 /* assign slave outs */
2120 if (spec->slave_dig_outs[0])
2121 codec->slave_dig_outs = spec->slave_dig_outs;
2122
Joseph Chanc577b8a2006-11-29 15:29:40 +01002123 return 1;
2124}
2125
Takashi Iwai5d417622011-06-20 11:32:27 +02002126static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002127{
Lydia Wang25eaba22009-10-10 19:08:43 +08002128 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002129 if (spec->multiout.dig_out_nid)
2130 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2131 if (spec->slave_dig_outs[0])
2132 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2133}
Lydia Wang25eaba22009-10-10 19:08:43 +08002134
Takashi Iwai5d417622011-06-20 11:32:27 +02002135static void via_auto_init_dig_in(struct hda_codec *codec)
2136{
2137 struct via_spec *spec = codec->spec;
2138 if (!spec->dig_in_nid)
2139 return;
2140 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2141 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2142}
2143
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002144/* initialize the unsolicited events */
2145static void via_auto_init_unsol_event(struct hda_codec *codec)
2146{
2147 struct via_spec *spec = codec->spec;
2148 struct auto_pin_cfg *cfg = &spec->autocfg;
2149 unsigned int ev;
2150 int i;
2151
2152 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2153 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2154 AC_VERB_SET_UNSOLICITED_ENABLE,
2155 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2156
2157 if (cfg->speaker_pins[0])
2158 ev = VIA_LINE_EVENT;
2159 else
2160 ev = 0;
2161 for (i = 0; i < cfg->line_outs; i++) {
2162 if (cfg->line_out_pins[i] &&
2163 is_jack_detectable(codec, cfg->line_out_pins[i]))
2164 snd_hda_codec_write(codec, cfg->line_out_pins[0], 0,
2165 AC_VERB_SET_UNSOLICITED_ENABLE,
2166 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2167 }
2168
2169 for (i = 0; i < cfg->num_inputs; i++) {
2170 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2171 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2172 AC_VERB_SET_UNSOLICITED_ENABLE,
2173 AC_USRSP_EN | VIA_JACK_EVENT);
2174 }
2175}
2176
Takashi Iwai5d417622011-06-20 11:32:27 +02002177static int via_init(struct hda_codec *codec)
2178{
2179 struct via_spec *spec = codec->spec;
2180 int i;
2181
2182 for (i = 0; i < spec->num_iverbs; i++)
2183 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2184
Joseph Chanc577b8a2006-11-29 15:29:40 +01002185 via_auto_init_multi_out(codec);
2186 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002187 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002188 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002189 via_auto_init_dig_outs(codec);
2190 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002191
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002192 via_auto_init_unsol_event(codec);
2193
2194 via_hp_automute(codec);
2195 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002196
Joseph Chanc577b8a2006-11-29 15:29:40 +01002197 return 0;
2198}
2199
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002200static void vt1708_update_hp_jack_state(struct work_struct *work)
2201{
2202 struct via_spec *spec = container_of(work, struct via_spec,
2203 vt1708_hp_work.work);
2204 if (spec->codec_type != VT1708)
2205 return;
2206 /* if jack state toggled */
2207 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002208 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002209 spec->vt1708_hp_present ^= 1;
2210 via_hp_automute(spec->codec);
2211 }
2212 vt1708_start_hp_work(spec);
2213}
2214
Takashi Iwai337b9d02009-07-07 18:18:59 +02002215static int get_mux_nids(struct hda_codec *codec)
2216{
2217 struct via_spec *spec = codec->spec;
2218 hda_nid_t nid, conn[8];
2219 unsigned int type;
2220 int i, n;
2221
2222 for (i = 0; i < spec->num_adc_nids; i++) {
2223 nid = spec->adc_nids[i];
2224 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002225 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002226 if (type == AC_WID_PIN)
2227 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002228 n = snd_hda_get_connections(codec, nid, conn,
2229 ARRAY_SIZE(conn));
2230 if (n <= 0)
2231 break;
2232 if (n > 1) {
2233 spec->mux_nids[i] = nid;
2234 break;
2235 }
2236 nid = conn[0];
2237 }
2238 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002239 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002240}
2241
Joseph Chanc577b8a2006-11-29 15:29:40 +01002242static int patch_vt1708(struct hda_codec *codec)
2243{
2244 struct via_spec *spec;
2245 int err;
2246
2247 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002248 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002249 if (spec == NULL)
2250 return -ENOMEM;
2251
Takashi Iwai620e2b22011-06-17 17:19:19 +02002252 spec->aa_mix_nid = 0x17;
2253
Takashi Iwai12daef62011-06-18 17:45:49 +02002254 /* Add HP and CD pin config connect bit re-config action */
2255 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2256 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2257
Joseph Chanc577b8a2006-11-29 15:29:40 +01002258 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002259 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002260 if (err < 0) {
2261 via_free(codec);
2262 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002263 }
2264
Takashi Iwai12daef62011-06-18 17:45:49 +02002265 /* add jack detect on/off control */
2266 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2267 return -ENOMEM;
2268
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002269 /* disable 32bit format on VT1708 */
2270 if (codec->vendor_id == 0x11061708)
2271 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002272
Joseph Chanc577b8a2006-11-29 15:29:40 +01002273 codec->patch_ops = via_patch_ops;
2274
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002275 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002276 return 0;
2277}
2278
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002279static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002280{
2281 struct via_spec *spec;
2282 int err;
2283
2284 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002285 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002286 if (spec == NULL)
2287 return -ENOMEM;
2288
Takashi Iwai620e2b22011-06-17 17:19:19 +02002289 spec->aa_mix_nid = 0x18;
2290
Takashi Iwai12daef62011-06-18 17:45:49 +02002291 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002292 if (err < 0) {
2293 via_free(codec);
2294 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002295 }
2296
Joseph Chanc577b8a2006-11-29 15:29:40 +01002297 codec->patch_ops = via_patch_ops;
2298
Josepch Chanf7278fd2007-12-13 16:40:40 +01002299 return 0;
2300}
2301
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002302static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2303{
2304 struct via_spec *spec = codec->spec;
2305 int imux_is_smixer;
2306 unsigned int parm;
2307 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002308 if ((spec->codec_type != VT1708B_4CH) &&
2309 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002310 is_8ch = 1;
2311
2312 /* SW0 (17h) = stereo mixer */
2313 imux_is_smixer =
2314 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2315 == ((spec->codec_type == VT1708S) ? 5 : 0));
2316 /* inputs */
2317 /* PW 1/2/5 (1ah/1bh/1eh) */
2318 parm = AC_PWRST_D3;
2319 set_pin_power_state(codec, 0x1a, &parm);
2320 set_pin_power_state(codec, 0x1b, &parm);
2321 set_pin_power_state(codec, 0x1e, &parm);
2322 if (imux_is_smixer)
2323 parm = AC_PWRST_D0;
2324 /* SW0 (17h), AIW 0/1 (13h/14h) */
2325 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2326 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2327 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2328
2329 /* outputs */
2330 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2331 parm = AC_PWRST_D3;
2332 set_pin_power_state(codec, 0x19, &parm);
2333 if (spec->smart51_enabled)
2334 set_pin_power_state(codec, 0x1b, &parm);
2335 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2336 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2337
2338 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2339 if (is_8ch) {
2340 parm = AC_PWRST_D3;
2341 set_pin_power_state(codec, 0x22, &parm);
2342 if (spec->smart51_enabled)
2343 set_pin_power_state(codec, 0x1a, &parm);
2344 snd_hda_codec_write(codec, 0x26, 0,
2345 AC_VERB_SET_POWER_STATE, parm);
2346 snd_hda_codec_write(codec, 0x24, 0,
2347 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002348 } else if (codec->vendor_id == 0x11064397) {
2349 /* PW7(23h), SW2(27h), AOW2(25h) */
2350 parm = AC_PWRST_D3;
2351 set_pin_power_state(codec, 0x23, &parm);
2352 if (spec->smart51_enabled)
2353 set_pin_power_state(codec, 0x1a, &parm);
2354 snd_hda_codec_write(codec, 0x27, 0,
2355 AC_VERB_SET_POWER_STATE, parm);
2356 snd_hda_codec_write(codec, 0x25, 0,
2357 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002358 }
2359
2360 /* PW 3/4/7 (1ch/1dh/23h) */
2361 parm = AC_PWRST_D3;
2362 /* force to D0 for internal Speaker */
2363 set_pin_power_state(codec, 0x1c, &parm);
2364 set_pin_power_state(codec, 0x1d, &parm);
2365 if (is_8ch)
2366 set_pin_power_state(codec, 0x23, &parm);
2367
2368 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2369 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2370 imux_is_smixer ? AC_PWRST_D0 : parm);
2371 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2372 if (is_8ch) {
2373 snd_hda_codec_write(codec, 0x25, 0,
2374 AC_VERB_SET_POWER_STATE, parm);
2375 snd_hda_codec_write(codec, 0x27, 0,
2376 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002377 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2378 snd_hda_codec_write(codec, 0x25, 0,
2379 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002380}
2381
Lydia Wang518bf3b2009-10-10 19:07:29 +08002382static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002383static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002384{
2385 struct via_spec *spec;
2386 int err;
2387
Lydia Wang518bf3b2009-10-10 19:07:29 +08002388 if (get_codec_type(codec) == VT1708BCE)
2389 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002390
Josepch Chanf7278fd2007-12-13 16:40:40 +01002391 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002392 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002393 if (spec == NULL)
2394 return -ENOMEM;
2395
Takashi Iwai620e2b22011-06-17 17:19:19 +02002396 spec->aa_mix_nid = 0x16;
2397
Josepch Chanf7278fd2007-12-13 16:40:40 +01002398 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002399 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002400 if (err < 0) {
2401 via_free(codec);
2402 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002403 }
2404
Josepch Chanf7278fd2007-12-13 16:40:40 +01002405 codec->patch_ops = via_patch_ops;
2406
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002407 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2408
Josepch Chanf7278fd2007-12-13 16:40:40 +01002409 return 0;
2410}
2411
Harald Welted949cac2008-09-09 15:56:01 +08002412/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002413static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002414 /* Enable Mic Boost Volume backdoor */
2415 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002416 /* don't bybass mixer */
2417 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002418 { }
2419};
2420
Takashi Iwai9da29272009-05-07 16:31:14 +02002421/* fill out digital output widgets; one for master and one for slave outputs */
2422static void fill_dig_outs(struct hda_codec *codec)
2423{
2424 struct via_spec *spec = codec->spec;
2425 int i;
2426
2427 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2428 hda_nid_t nid;
2429 int conn;
2430
2431 nid = spec->autocfg.dig_out_pins[i];
2432 if (!nid)
2433 continue;
2434 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2435 if (conn < 1)
2436 continue;
2437 if (!spec->multiout.dig_out_nid)
2438 spec->multiout.dig_out_nid = nid;
2439 else {
2440 spec->slave_dig_outs[0] = nid;
2441 break; /* at most two dig outs */
2442 }
2443 }
2444}
2445
Takashi Iwai12daef62011-06-18 17:45:49 +02002446static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002447{
2448 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002449 hda_nid_t dig_nid;
2450 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002451
Takashi Iwai12daef62011-06-18 17:45:49 +02002452 if (!spec->autocfg.dig_in_pin)
2453 return;
Harald Welted949cac2008-09-09 15:56:01 +08002454
Takashi Iwai12daef62011-06-18 17:45:49 +02002455 dig_nid = codec->start_nid;
2456 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2457 unsigned int wcaps = get_wcaps(codec, dig_nid);
2458 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2459 continue;
2460 if (!(wcaps & AC_WCAP_DIGITAL))
2461 continue;
2462 if (!(wcaps & AC_WCAP_CONN_LIST))
2463 continue;
2464 err = get_connection_index(codec, dig_nid,
2465 spec->autocfg.dig_in_pin);
2466 if (err >= 0) {
2467 spec->dig_in_nid = dig_nid;
2468 break;
2469 }
2470 }
Harald Welted949cac2008-09-09 15:56:01 +08002471}
2472
Lydia Wang6369bcf2009-10-10 19:08:31 +08002473static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2474 int offset, int num_steps, int step_size)
2475{
2476 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2477 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2478 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2479 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2480 (0 << AC_AMPCAP_MUTE_SHIFT));
2481}
2482
Harald Welted949cac2008-09-09 15:56:01 +08002483static int patch_vt1708S(struct hda_codec *codec)
2484{
2485 struct via_spec *spec;
2486 int err;
2487
2488 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002489 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002490 if (spec == NULL)
2491 return -ENOMEM;
2492
Takashi Iwai620e2b22011-06-17 17:19:19 +02002493 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002494 override_mic_boost(codec, 0x1a, 0, 3, 40);
2495 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002496
Harald Welted949cac2008-09-09 15:56:01 +08002497 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002498 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002499 if (err < 0) {
2500 via_free(codec);
2501 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002502 }
2503
Takashi Iwai096a8852011-06-20 12:09:02 +02002504 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002505
Harald Welted949cac2008-09-09 15:56:01 +08002506 codec->patch_ops = via_patch_ops;
2507
Lydia Wang518bf3b2009-10-10 19:07:29 +08002508 /* correct names for VT1708BCE */
2509 if (get_codec_type(codec) == VT1708BCE) {
2510 kfree(codec->chip_name);
2511 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2512 snprintf(codec->bus->card->mixername,
2513 sizeof(codec->bus->card->mixername),
2514 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002515 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002516 /* correct names for VT1705 */
2517 if (codec->vendor_id == 0x11064397) {
2518 kfree(codec->chip_name);
2519 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2520 snprintf(codec->bus->card->mixername,
2521 sizeof(codec->bus->card->mixername),
2522 "%s %s", codec->vendor_name, codec->chip_name);
2523 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002524 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002525 return 0;
2526}
2527
2528/* Patch for VT1702 */
2529
Takashi Iwai096a8852011-06-20 12:09:02 +02002530static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002531 /* mixer enable */
2532 {0x1, 0xF88, 0x3},
2533 /* GPIO 0~2 */
2534 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002535 { }
2536};
2537
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002538static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2539{
2540 int imux_is_smixer =
2541 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2542 unsigned int parm;
2543 /* inputs */
2544 /* PW 1/2/5 (14h/15h/18h) */
2545 parm = AC_PWRST_D3;
2546 set_pin_power_state(codec, 0x14, &parm);
2547 set_pin_power_state(codec, 0x15, &parm);
2548 set_pin_power_state(codec, 0x18, &parm);
2549 if (imux_is_smixer)
2550 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2551 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2552 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2553 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2554 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2555 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2556
2557 /* outputs */
2558 /* PW 3/4 (16h/17h) */
2559 parm = AC_PWRST_D3;
2560 set_pin_power_state(codec, 0x17, &parm);
2561 set_pin_power_state(codec, 0x16, &parm);
2562 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2563 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2564 imux_is_smixer ? AC_PWRST_D0 : parm);
2565 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2566 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2567}
2568
Harald Welted949cac2008-09-09 15:56:01 +08002569static int patch_vt1702(struct hda_codec *codec)
2570{
2571 struct via_spec *spec;
2572 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002573
2574 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002575 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002576 if (spec == NULL)
2577 return -ENOMEM;
2578
Takashi Iwai620e2b22011-06-17 17:19:19 +02002579 spec->aa_mix_nid = 0x1a;
2580
Takashi Iwai12daef62011-06-18 17:45:49 +02002581 /* limit AA path volume to 0 dB */
2582 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2583 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2584 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2585 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2586 (1 << AC_AMPCAP_MUTE_SHIFT));
2587
Harald Welted949cac2008-09-09 15:56:01 +08002588 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002589 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002590 if (err < 0) {
2591 via_free(codec);
2592 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002593 }
2594
Takashi Iwai096a8852011-06-20 12:09:02 +02002595 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002596
Harald Welted949cac2008-09-09 15:56:01 +08002597 codec->patch_ops = via_patch_ops;
2598
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002599 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002600 return 0;
2601}
2602
Lydia Wangeb7188c2009-10-10 19:08:34 +08002603/* Patch for VT1718S */
2604
Takashi Iwai096a8852011-06-20 12:09:02 +02002605static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002606 /* Enable MW0 adjust Gain 5 */
2607 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002608 /* Enable Boost Volume backdoor */
2609 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002610
Lydia Wangeb7188c2009-10-10 19:08:34 +08002611 { }
2612};
2613
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002614static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2615{
2616 struct via_spec *spec = codec->spec;
2617 int imux_is_smixer;
2618 unsigned int parm;
2619 /* MUX6 (1eh) = stereo mixer */
2620 imux_is_smixer =
2621 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2622 /* inputs */
2623 /* PW 5/6/7 (29h/2ah/2bh) */
2624 parm = AC_PWRST_D3;
2625 set_pin_power_state(codec, 0x29, &parm);
2626 set_pin_power_state(codec, 0x2a, &parm);
2627 set_pin_power_state(codec, 0x2b, &parm);
2628 if (imux_is_smixer)
2629 parm = AC_PWRST_D0;
2630 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2631 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2632 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2633 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2634 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2635
2636 /* outputs */
2637 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2638 parm = AC_PWRST_D3;
2639 set_pin_power_state(codec, 0x27, &parm);
2640 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2641 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2642
2643 /* PW2 (26h), AOW2 (ah) */
2644 parm = AC_PWRST_D3;
2645 set_pin_power_state(codec, 0x26, &parm);
2646 if (spec->smart51_enabled)
2647 set_pin_power_state(codec, 0x2b, &parm);
2648 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2649
2650 /* PW0 (24h), AOW0 (8h) */
2651 parm = AC_PWRST_D3;
2652 set_pin_power_state(codec, 0x24, &parm);
2653 if (!spec->hp_independent_mode) /* check for redirected HP */
2654 set_pin_power_state(codec, 0x28, &parm);
2655 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2656 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2657 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2658 imux_is_smixer ? AC_PWRST_D0 : parm);
2659
2660 /* PW1 (25h), AOW1 (9h) */
2661 parm = AC_PWRST_D3;
2662 set_pin_power_state(codec, 0x25, &parm);
2663 if (spec->smart51_enabled)
2664 set_pin_power_state(codec, 0x2a, &parm);
2665 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2666
2667 if (spec->hp_independent_mode) {
2668 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2669 parm = AC_PWRST_D3;
2670 set_pin_power_state(codec, 0x28, &parm);
2671 snd_hda_codec_write(codec, 0x1b, 0,
2672 AC_VERB_SET_POWER_STATE, parm);
2673 snd_hda_codec_write(codec, 0x34, 0,
2674 AC_VERB_SET_POWER_STATE, parm);
2675 snd_hda_codec_write(codec, 0xc, 0,
2676 AC_VERB_SET_POWER_STATE, parm);
2677 }
2678}
2679
Lydia Wangeb7188c2009-10-10 19:08:34 +08002680static int patch_vt1718S(struct hda_codec *codec)
2681{
2682 struct via_spec *spec;
2683 int err;
2684
2685 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002686 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002687 if (spec == NULL)
2688 return -ENOMEM;
2689
Takashi Iwai620e2b22011-06-17 17:19:19 +02002690 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002691 override_mic_boost(codec, 0x2b, 0, 3, 40);
2692 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002693
Lydia Wangeb7188c2009-10-10 19:08:34 +08002694 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002695 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002696 if (err < 0) {
2697 via_free(codec);
2698 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002699 }
2700
Takashi Iwai096a8852011-06-20 12:09:02 +02002701 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002702
Lydia Wangeb7188c2009-10-10 19:08:34 +08002703 codec->patch_ops = via_patch_ops;
2704
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002705 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2706
Lydia Wangeb7188c2009-10-10 19:08:34 +08002707 return 0;
2708}
Lydia Wangf3db4232009-10-10 19:08:41 +08002709
2710/* Patch for VT1716S */
2711
2712static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2713 struct snd_ctl_elem_info *uinfo)
2714{
2715 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2716 uinfo->count = 1;
2717 uinfo->value.integer.min = 0;
2718 uinfo->value.integer.max = 1;
2719 return 0;
2720}
2721
2722static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2723 struct snd_ctl_elem_value *ucontrol)
2724{
2725 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2726 int index = 0;
2727
2728 index = snd_hda_codec_read(codec, 0x26, 0,
2729 AC_VERB_GET_CONNECT_SEL, 0);
2730 if (index != -1)
2731 *ucontrol->value.integer.value = index;
2732
2733 return 0;
2734}
2735
2736static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2737 struct snd_ctl_elem_value *ucontrol)
2738{
2739 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2740 struct via_spec *spec = codec->spec;
2741 int index = *ucontrol->value.integer.value;
2742
2743 snd_hda_codec_write(codec, 0x26, 0,
2744 AC_VERB_SET_CONNECT_SEL, index);
2745 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002746 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002747 return 1;
2748}
2749
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002750static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002751 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2752 {
2753 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2754 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002755 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08002756 .count = 1,
2757 .info = vt1716s_dmic_info,
2758 .get = vt1716s_dmic_get,
2759 .put = vt1716s_dmic_put,
2760 },
2761 {} /* end */
2762};
2763
2764
2765/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002766static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002767 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
2768 { } /* end */
2769};
2770
Takashi Iwai096a8852011-06-20 12:09:02 +02002771static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002772 /* Enable Boost Volume backdoor */
2773 {0x1, 0xf8a, 0x80},
2774 /* don't bybass mixer */
2775 {0x1, 0xf88, 0xc0},
2776 /* Enable mono output */
2777 {0x1, 0xf90, 0x08},
2778 { }
2779};
2780
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002781static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
2782{
2783 struct via_spec *spec = codec->spec;
2784 int imux_is_smixer;
2785 unsigned int parm;
2786 unsigned int mono_out, present;
2787 /* SW0 (17h) = stereo mixer */
2788 imux_is_smixer =
2789 (snd_hda_codec_read(codec, 0x17, 0,
2790 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
2791 /* inputs */
2792 /* PW 1/2/5 (1ah/1bh/1eh) */
2793 parm = AC_PWRST_D3;
2794 set_pin_power_state(codec, 0x1a, &parm);
2795 set_pin_power_state(codec, 0x1b, &parm);
2796 set_pin_power_state(codec, 0x1e, &parm);
2797 if (imux_is_smixer)
2798 parm = AC_PWRST_D0;
2799 /* SW0 (17h), AIW0(13h) */
2800 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2801 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2802
2803 parm = AC_PWRST_D3;
2804 set_pin_power_state(codec, 0x1e, &parm);
2805 /* PW11 (22h) */
2806 if (spec->dmic_enabled)
2807 set_pin_power_state(codec, 0x22, &parm);
2808 else
2809 snd_hda_codec_write(codec, 0x22, 0,
2810 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
2811
2812 /* SW2(26h), AIW1(14h) */
2813 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
2814 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2815
2816 /* outputs */
2817 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2818 parm = AC_PWRST_D3;
2819 set_pin_power_state(codec, 0x19, &parm);
2820 /* Smart 5.1 PW2(1bh) */
2821 if (spec->smart51_enabled)
2822 set_pin_power_state(codec, 0x1b, &parm);
2823 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2824 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2825
2826 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
2827 parm = AC_PWRST_D3;
2828 set_pin_power_state(codec, 0x23, &parm);
2829 /* Smart 5.1 PW1(1ah) */
2830 if (spec->smart51_enabled)
2831 set_pin_power_state(codec, 0x1a, &parm);
2832 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
2833
2834 /* Smart 5.1 PW5(1eh) */
2835 if (spec->smart51_enabled)
2836 set_pin_power_state(codec, 0x1e, &parm);
2837 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
2838
2839 /* Mono out */
2840 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
2841 present = snd_hda_jack_detect(codec, 0x1c);
2842
2843 if (present)
2844 mono_out = 0;
2845 else {
2846 present = snd_hda_jack_detect(codec, 0x1d);
2847 if (!spec->hp_independent_mode && present)
2848 mono_out = 0;
2849 else
2850 mono_out = 1;
2851 }
2852 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
2853 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
2854 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
2855 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
2856
2857 /* PW 3/4 (1ch/1dh) */
2858 parm = AC_PWRST_D3;
2859 set_pin_power_state(codec, 0x1c, &parm);
2860 set_pin_power_state(codec, 0x1d, &parm);
2861 /* HP Independent Mode, power on AOW3 */
2862 if (spec->hp_independent_mode)
2863 snd_hda_codec_write(codec, 0x25, 0,
2864 AC_VERB_SET_POWER_STATE, parm);
2865
2866 /* force to D0 for internal Speaker */
2867 /* MW0 (16h), AOW0 (10h) */
2868 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2869 imux_is_smixer ? AC_PWRST_D0 : parm);
2870 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
2871 mono_out ? AC_PWRST_D0 : parm);
2872}
2873
Lydia Wangf3db4232009-10-10 19:08:41 +08002874static int patch_vt1716S(struct hda_codec *codec)
2875{
2876 struct via_spec *spec;
2877 int err;
2878
2879 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002880 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002881 if (spec == NULL)
2882 return -ENOMEM;
2883
Takashi Iwai620e2b22011-06-17 17:19:19 +02002884 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002885 override_mic_boost(codec, 0x1a, 0, 3, 40);
2886 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002887
Lydia Wangf3db4232009-10-10 19:08:41 +08002888 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002889 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002890 if (err < 0) {
2891 via_free(codec);
2892 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08002893 }
2894
Takashi Iwai096a8852011-06-20 12:09:02 +02002895 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08002896
Lydia Wangf3db4232009-10-10 19:08:41 +08002897 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
2898 spec->num_mixers++;
2899
2900 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
2901
2902 codec->patch_ops = via_patch_ops;
2903
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002904 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08002905 return 0;
2906}
Lydia Wang25eaba22009-10-10 19:08:43 +08002907
2908/* for vt2002P */
2909
Takashi Iwai096a8852011-06-20 12:09:02 +02002910static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08002911 /* Class-D speaker related verbs */
2912 {0x1, 0xfe0, 0x4},
2913 {0x1, 0xfe9, 0x80},
2914 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08002915 /* Enable Boost Volume backdoor */
2916 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08002917 /* Enable AOW0 to MW9 */
2918 {0x1, 0xfb8, 0x88},
2919 { }
2920};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002921
Takashi Iwai096a8852011-06-20 12:09:02 +02002922static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08002923 /* Enable Boost Volume backdoor */
2924 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08002925 /* Enable AOW0 to MW9 */
2926 {0x1, 0xfb8, 0x88},
2927 { }
2928};
Lydia Wang25eaba22009-10-10 19:08:43 +08002929
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002930static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
2931{
2932 struct via_spec *spec = codec->spec;
2933 int imux_is_smixer;
2934 unsigned int parm;
2935 unsigned int present;
2936 /* MUX9 (1eh) = stereo mixer */
2937 imux_is_smixer =
2938 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2939 /* inputs */
2940 /* PW 5/6/7 (29h/2ah/2bh) */
2941 parm = AC_PWRST_D3;
2942 set_pin_power_state(codec, 0x29, &parm);
2943 set_pin_power_state(codec, 0x2a, &parm);
2944 set_pin_power_state(codec, 0x2b, &parm);
2945 parm = AC_PWRST_D0;
2946 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
2947 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2948 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2949 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2950 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2951
2952 /* outputs */
2953 /* AOW0 (8h)*/
2954 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2955
Lydia Wang118909562011-03-23 17:57:34 +08002956 if (spec->codec_type == VT1802) {
2957 /* PW4 (28h), MW4 (18h), MUX4(38h) */
2958 parm = AC_PWRST_D3;
2959 set_pin_power_state(codec, 0x28, &parm);
2960 snd_hda_codec_write(codec, 0x18, 0,
2961 AC_VERB_SET_POWER_STATE, parm);
2962 snd_hda_codec_write(codec, 0x38, 0,
2963 AC_VERB_SET_POWER_STATE, parm);
2964 } else {
2965 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
2966 parm = AC_PWRST_D3;
2967 set_pin_power_state(codec, 0x26, &parm);
2968 snd_hda_codec_write(codec, 0x1c, 0,
2969 AC_VERB_SET_POWER_STATE, parm);
2970 snd_hda_codec_write(codec, 0x37, 0,
2971 AC_VERB_SET_POWER_STATE, parm);
2972 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002973
Lydia Wang118909562011-03-23 17:57:34 +08002974 if (spec->codec_type == VT1802) {
2975 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
2976 parm = AC_PWRST_D3;
2977 set_pin_power_state(codec, 0x25, &parm);
2978 snd_hda_codec_write(codec, 0x15, 0,
2979 AC_VERB_SET_POWER_STATE, parm);
2980 snd_hda_codec_write(codec, 0x35, 0,
2981 AC_VERB_SET_POWER_STATE, parm);
2982 } else {
2983 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
2984 parm = AC_PWRST_D3;
2985 set_pin_power_state(codec, 0x25, &parm);
2986 snd_hda_codec_write(codec, 0x19, 0,
2987 AC_VERB_SET_POWER_STATE, parm);
2988 snd_hda_codec_write(codec, 0x35, 0,
2989 AC_VERB_SET_POWER_STATE, parm);
2990 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002991
2992 if (spec->hp_independent_mode)
2993 snd_hda_codec_write(codec, 0x9, 0,
2994 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2995
2996 /* Class-D */
2997 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
2998 present = snd_hda_jack_detect(codec, 0x25);
2999
3000 parm = AC_PWRST_D3;
3001 set_pin_power_state(codec, 0x24, &parm);
3002 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003003 if (spec->codec_type == VT1802)
3004 snd_hda_codec_write(codec, 0x14, 0,
3005 AC_VERB_SET_POWER_STATE, parm);
3006 else
3007 snd_hda_codec_write(codec, 0x18, 0,
3008 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003009 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3010
3011 /* Mono Out */
3012 present = snd_hda_jack_detect(codec, 0x26);
3013
3014 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003015 if (spec->codec_type == VT1802) {
3016 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3017 snd_hda_codec_write(codec, 0x33, 0,
3018 AC_VERB_SET_POWER_STATE, parm);
3019 snd_hda_codec_write(codec, 0x1c, 0,
3020 AC_VERB_SET_POWER_STATE, parm);
3021 snd_hda_codec_write(codec, 0x3c, 0,
3022 AC_VERB_SET_POWER_STATE, parm);
3023 } else {
3024 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3025 snd_hda_codec_write(codec, 0x31, 0,
3026 AC_VERB_SET_POWER_STATE, parm);
3027 snd_hda_codec_write(codec, 0x17, 0,
3028 AC_VERB_SET_POWER_STATE, parm);
3029 snd_hda_codec_write(codec, 0x3b, 0,
3030 AC_VERB_SET_POWER_STATE, parm);
3031 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003032 /* MW9 (21h) */
3033 if (imux_is_smixer || !is_aa_path_mute(codec))
3034 snd_hda_codec_write(codec, 0x21, 0,
3035 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3036 else
3037 snd_hda_codec_write(codec, 0x21, 0,
3038 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3039}
Lydia Wang25eaba22009-10-10 19:08:43 +08003040
3041/* patch for vt2002P */
3042static int patch_vt2002P(struct hda_codec *codec)
3043{
3044 struct via_spec *spec;
3045 int err;
3046
3047 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003048 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003049 if (spec == NULL)
3050 return -ENOMEM;
3051
Takashi Iwai620e2b22011-06-17 17:19:19 +02003052 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003053 override_mic_boost(codec, 0x2b, 0, 3, 40);
3054 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003055
Lydia Wang25eaba22009-10-10 19:08:43 +08003056 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003057 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003058 if (err < 0) {
3059 via_free(codec);
3060 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003061 }
3062
Lydia Wang118909562011-03-23 17:57:34 +08003063 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003064 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003065 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003066 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003067
Lydia Wang25eaba22009-10-10 19:08:43 +08003068 codec->patch_ops = via_patch_ops;
3069
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003070 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003071 return 0;
3072}
Lydia Wangab6734e2009-10-10 19:08:46 +08003073
3074/* for vt1812 */
3075
Takashi Iwai096a8852011-06-20 12:09:02 +02003076static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003077 /* Enable Boost Volume backdoor */
3078 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003079 /* Enable AOW0 to MW9 */
3080 {0x1, 0xfb8, 0xa8},
3081 { }
3082};
3083
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003084static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3085{
3086 struct via_spec *spec = codec->spec;
3087 int imux_is_smixer =
3088 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3089 unsigned int parm;
3090 unsigned int present;
3091 /* MUX10 (1eh) = stereo mixer */
3092 imux_is_smixer =
3093 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3094 /* inputs */
3095 /* PW 5/6/7 (29h/2ah/2bh) */
3096 parm = AC_PWRST_D3;
3097 set_pin_power_state(codec, 0x29, &parm);
3098 set_pin_power_state(codec, 0x2a, &parm);
3099 set_pin_power_state(codec, 0x2b, &parm);
3100 parm = AC_PWRST_D0;
3101 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3102 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3103 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3104 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3105 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3106
3107 /* outputs */
3108 /* AOW0 (8h)*/
3109 snd_hda_codec_write(codec, 0x8, 0,
3110 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3111
3112 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3113 parm = AC_PWRST_D3;
3114 set_pin_power_state(codec, 0x28, &parm);
3115 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3116 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3117
3118 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3119 parm = AC_PWRST_D3;
3120 set_pin_power_state(codec, 0x25, &parm);
3121 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3122 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3123 if (spec->hp_independent_mode)
3124 snd_hda_codec_write(codec, 0x9, 0,
3125 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3126
3127 /* Internal Speaker */
3128 /* PW0 (24h), MW0(14h), MUX0(34h) */
3129 present = snd_hda_jack_detect(codec, 0x25);
3130
3131 parm = AC_PWRST_D3;
3132 set_pin_power_state(codec, 0x24, &parm);
3133 if (present) {
3134 snd_hda_codec_write(codec, 0x14, 0,
3135 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3136 snd_hda_codec_write(codec, 0x34, 0,
3137 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3138 } else {
3139 snd_hda_codec_write(codec, 0x14, 0,
3140 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3141 snd_hda_codec_write(codec, 0x34, 0,
3142 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3143 }
3144
3145
3146 /* Mono Out */
3147 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3148 present = snd_hda_jack_detect(codec, 0x28);
3149
3150 parm = AC_PWRST_D3;
3151 set_pin_power_state(codec, 0x31, &parm);
3152 if (present) {
3153 snd_hda_codec_write(codec, 0x1c, 0,
3154 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3155 snd_hda_codec_write(codec, 0x3c, 0,
3156 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3157 snd_hda_codec_write(codec, 0x3e, 0,
3158 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3159 } else {
3160 snd_hda_codec_write(codec, 0x1c, 0,
3161 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3162 snd_hda_codec_write(codec, 0x3c, 0,
3163 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3164 snd_hda_codec_write(codec, 0x3e, 0,
3165 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3166 }
3167
3168 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3169 parm = AC_PWRST_D3;
3170 set_pin_power_state(codec, 0x33, &parm);
3171 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3172 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3173
3174}
Lydia Wangab6734e2009-10-10 19:08:46 +08003175
3176/* patch for vt1812 */
3177static int patch_vt1812(struct hda_codec *codec)
3178{
3179 struct via_spec *spec;
3180 int err;
3181
3182 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003183 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003184 if (spec == NULL)
3185 return -ENOMEM;
3186
Takashi Iwai620e2b22011-06-17 17:19:19 +02003187 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003188 override_mic_boost(codec, 0x2b, 0, 3, 40);
3189 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003190
Lydia Wangab6734e2009-10-10 19:08:46 +08003191 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003192 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003193 if (err < 0) {
3194 via_free(codec);
3195 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003196 }
3197
Takashi Iwai096a8852011-06-20 12:09:02 +02003198 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003199
Lydia Wangab6734e2009-10-10 19:08:46 +08003200 codec->patch_ops = via_patch_ops;
3201
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003202 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003203 return 0;
3204}
3205
Joseph Chanc577b8a2006-11-29 15:29:40 +01003206/*
3207 * patch entries
3208 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003209static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003210 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3211 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3212 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3213 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3214 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003215 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003216 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003217 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003218 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003219 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003220 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003221 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003222 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003223 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003224 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003225 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003226 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003227 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003228 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003229 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003230 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003231 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003232 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003233 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003234 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003235 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003236 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003237 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003238 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003239 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003240 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003241 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003242 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003243 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003244 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003245 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003246 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003247 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003248 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003249 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003250 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003251 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003252 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003253 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003254 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003255 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003256 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003257 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003258 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003259 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003260 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003261 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003262 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003263 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003264 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003265 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003266 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003267 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003268 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003269 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003270 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003271 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003272 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003273 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003274 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003275 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003276 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003277 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003278 { .id = 0x11060428, .name = "VT1718S",
3279 .patch = patch_vt1718S},
3280 { .id = 0x11064428, .name = "VT1718S",
3281 .patch = patch_vt1718S},
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08003282 { .id = 0x11060441, .name = "VT2020",
3283 .patch = patch_vt1718S},
3284 { .id = 0x11064441, .name = "VT1828S",
3285 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003286 { .id = 0x11060433, .name = "VT1716S",
3287 .patch = patch_vt1716S},
3288 { .id = 0x1106a721, .name = "VT1716S",
3289 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003290 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3291 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003292 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003293 { .id = 0x11060440, .name = "VT1818S",
3294 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003295 { .id = 0x11060446, .name = "VT1802",
3296 .patch = patch_vt2002P},
3297 { .id = 0x11068446, .name = "VT1802",
3298 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003299 {} /* terminator */
3300};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003301
3302MODULE_ALIAS("snd-hda-codec-id:1106*");
3303
3304static struct hda_codec_preset_list via_list = {
3305 .preset = snd_hda_preset_via,
3306 .owner = THIS_MODULE,
3307};
3308
3309MODULE_LICENSE("GPL");
3310MODULE_DESCRIPTION("VIA HD-audio codec");
3311
3312static int __init patch_via_init(void)
3313{
3314 return snd_hda_add_codec_preset(&via_list);
3315}
3316
3317static void __exit patch_via_exit(void)
3318{
3319 snd_hda_delete_codec_preset(&via_list);
3320}
3321
3322module_init(patch_via_init)
3323module_exit(patch_via_exit)