blob: bceb6b2364fe86484f03bd4af4616c2531ed6838 [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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai7eb56e842011-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 Wangbb3c6bfc2009-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-06-21 15:57:44 +0200489 hda_nid_t pin, nid;
490 int i, idx;
Takashi Iwai5d417622011-06-20 11:32:27 +0200491
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200492 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200493 return;
Takashi Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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_info(struct snd_kcontrol *kcontrol,
848 struct snd_ctl_elem_info *uinfo)
849{
850 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
851 uinfo->count = 1;
852 uinfo->value.integer.min = 0;
853 uinfo->value.integer.max = 1;
854 return 0;
855}
856
857static int via_smart51_get(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;
Lydia Wang1564b282009-10-10 19:07:52 +0800862 int on = 1;
863 int i;
864
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200865 for (i = 0; i < spec->smart51_nums; i++) {
866 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwaif4a78282011-06-17 18:46:48 +0200867 unsigned int ctl;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200868 ctl = snd_hda_codec_read(codec, nid, 0,
869 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200870 if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
871 on = 0;
Lydia Wang1564b282009-10-10 19:07:52 +0800872 }
873 *ucontrol->value.integer.value = on;
874 return 0;
875}
876
877static int via_smart51_put(struct snd_kcontrol *kcontrol,
878 struct snd_ctl_elem_value *ucontrol)
879{
880 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
881 struct via_spec *spec = codec->spec;
882 int out_in = *ucontrol->value.integer.value
883 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800884 int i;
885
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200886 for (i = 0; i < spec->smart51_nums; i++) {
887 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200888 unsigned int parm;
889
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200890 parm = snd_hda_codec_read(codec, nid, 0,
891 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
892 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
893 parm |= out_in;
894 snd_hda_codec_write(codec, nid, 0,
895 AC_VERB_SET_PIN_WIDGET_CONTROL,
896 parm);
897 if (out_in == AC_PINCTL_OUT_EN) {
898 mute_aa_path(codec, 1);
899 notify_aa_path_ctls(codec);
900 }
Lydia Wang1564b282009-10-10 19:07:52 +0800901 }
902 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800903 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800904 return 1;
905}
906
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200907static const struct snd_kcontrol_new via_smart51_mixer = {
908 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
909 .name = "Smart 5.1",
910 .count = 1,
911 .info = via_smart51_info,
912 .get = via_smart51_get,
913 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800914};
915
Takashi Iwaif4a78282011-06-17 18:46:48 +0200916static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100917{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200918 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100919
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200920 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800921 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200922 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100923 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100924 return 0;
925}
926
Takashi Iwaiada509e2011-06-20 15:40:19 +0200927/* check AA path's mute status */
928static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800929{
Lydia Wangf5271102009-10-10 19:07:35 +0800930 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200931 const struct hda_amp_list *p;
932 int i, ch, v;
933
934 for (i = 0; i < spec->num_loopbacks; i++) {
935 p = &spec->loopback_list[i];
936 for (ch = 0; ch < 2; ch++) {
937 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
938 p->idx);
939 if (!(v & HDA_AMP_MUTE) && v > 0)
940 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800941 }
942 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200943 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800944}
945
946/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200947static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800948{
949 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200950 bool enable;
951 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800952
Takashi Iwaiada509e2011-06-20 15:40:19 +0200953 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800954
955 /* decide low current mode's verb & parameter */
956 switch (spec->codec_type) {
957 case VT1708B_8CH:
958 case VT1708B_4CH:
959 verb = 0xf70;
960 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
961 break;
962 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800963 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800964 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800965 verb = 0xf73;
966 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
967 break;
968 case VT1702:
969 verb = 0xf73;
970 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
971 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800972 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800973 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800974 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800975 verb = 0xf93;
976 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
977 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800978 default:
979 return; /* other codecs are not supported */
980 }
981 /* send verb */
982 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
983}
984
Joseph Chanc577b8a2006-11-29 15:29:40 +0100985/*
986 * generic initialization of ADC, input mixers and output mixers
987 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200988static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800989 /* power down jack detect function */
990 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +0100991 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100992};
993
Takashi Iwaiada509e2011-06-20 15:40:19 +0200994static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e842011-06-18 16:40:14 +0200995{
Takashi Iwaiada509e2011-06-20 15:40:19 +0200996 struct via_spec *spec = codec->spec;
997
998 if (active)
999 spec->num_active_streams++;
1000 else
1001 spec->num_active_streams--;
1002 analog_low_current_mode(codec);
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001003}
1004
Takashi Iwaiece8d042011-06-19 16:24:21 +02001005static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001006 struct hda_codec *codec,
1007 struct snd_pcm_substream *substream)
1008{
1009 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001010 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001011
1012 if (!spec->hp_independent_mode)
1013 spec->multiout.hp_nid = spec->hp_dac_nid;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001014 set_stream_active(codec, true);
1015 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1016 hinfo);
1017 if (err < 0) {
1018 spec->multiout.hp_nid = 0;
1019 set_stream_active(codec, false);
1020 return err;
1021 }
1022 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001023}
1024
Takashi Iwaiece8d042011-06-19 16:24:21 +02001025static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001026 struct hda_codec *codec,
1027 struct snd_pcm_substream *substream)
1028{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001029 struct via_spec *spec = codec->spec;
1030
1031 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001032 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001033 return 0;
1034}
1035
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001036static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1037 struct hda_codec *codec,
1038 struct snd_pcm_substream *substream)
1039{
1040 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001041
Takashi Iwaiece8d042011-06-19 16:24:21 +02001042 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001043 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001044 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1045 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001046 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001047 return 0;
1048}
1049
1050static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1051 struct hda_codec *codec,
1052 struct snd_pcm_substream *substream)
1053{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001054 set_stream_active(codec, false);
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001055 return 0;
1056}
1057
1058static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1059 struct hda_codec *codec,
1060 unsigned int stream_tag,
1061 unsigned int format,
1062 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001063{
1064 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001065
Takashi Iwaiece8d042011-06-19 16:24:21 +02001066 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1067 format, substream);
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001068 vt1708_start_hp_work(spec);
1069 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001070}
1071
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001072static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1073 struct hda_codec *codec,
1074 unsigned int stream_tag,
1075 unsigned int format,
1076 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001077{
1078 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001079
Takashi Iwaiece8d042011-06-19 16:24:21 +02001080 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1081 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001082 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001083 return 0;
1084}
1085
1086static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1087 struct hda_codec *codec,
1088 struct snd_pcm_substream *substream)
1089{
1090 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001091
Takashi Iwaiece8d042011-06-19 16:24:21 +02001092 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001093 vt1708_stop_hp_work(spec);
1094 return 0;
1095}
1096
1097static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1098 struct hda_codec *codec,
1099 struct snd_pcm_substream *substream)
1100{
1101 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001102
Takashi Iwaiece8d042011-06-19 16:24:21 +02001103 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001104 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001105 return 0;
1106}
1107
Joseph Chanc577b8a2006-11-29 15:29:40 +01001108/*
1109 * Digital out
1110 */
1111static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1112 struct hda_codec *codec,
1113 struct snd_pcm_substream *substream)
1114{
1115 struct via_spec *spec = codec->spec;
1116 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1117}
1118
1119static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1120 struct hda_codec *codec,
1121 struct snd_pcm_substream *substream)
1122{
1123 struct via_spec *spec = codec->spec;
1124 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1125}
1126
Harald Welte5691ec72008-09-15 22:42:26 +08001127static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001128 struct hda_codec *codec,
1129 unsigned int stream_tag,
1130 unsigned int format,
1131 struct snd_pcm_substream *substream)
1132{
1133 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001134 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1135 stream_tag, format, substream);
1136}
Harald Welte5691ec72008-09-15 22:42:26 +08001137
Takashi Iwai9da29272009-05-07 16:31:14 +02001138static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1139 struct hda_codec *codec,
1140 struct snd_pcm_substream *substream)
1141{
1142 struct via_spec *spec = codec->spec;
1143 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001144 return 0;
1145}
1146
Joseph Chanc577b8a2006-11-29 15:29:40 +01001147/*
1148 * Analog capture
1149 */
1150static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1151 struct hda_codec *codec,
1152 unsigned int stream_tag,
1153 unsigned int format,
1154 struct snd_pcm_substream *substream)
1155{
1156 struct via_spec *spec = codec->spec;
1157
1158 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1159 stream_tag, 0, format);
1160 return 0;
1161}
1162
1163static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1164 struct hda_codec *codec,
1165 struct snd_pcm_substream *substream)
1166{
1167 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001168 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001169 return 0;
1170}
1171
Takashi Iwai9af74212011-06-18 16:17:45 +02001172static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001173 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001174 .channels_min = 2,
1175 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001176 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001177 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001178 .open = via_playback_multi_pcm_open,
1179 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001180 .prepare = via_playback_multi_pcm_prepare,
1181 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001182 },
1183};
1184
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001185static const struct hda_pcm_stream via_pcm_hp_playback = {
1186 .substreams = 1,
1187 .channels_min = 2,
1188 .channels_max = 2,
1189 /* NID is set in via_build_pcms */
1190 .ops = {
1191 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001192 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001193 .prepare = via_playback_hp_pcm_prepare,
1194 .cleanup = via_playback_hp_pcm_cleanup
1195 },
1196};
1197
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001198static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001199 .substreams = 1,
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001200 .channels_min = 2,
1201 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001202 /* NID is set in via_build_pcms */
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001203 /* We got noisy outputs on the right channel on VT1708 when
1204 * 24bit samples are used. Until any workaround is found,
1205 * disable the 24bit format, so far.
1206 */
1207 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1208 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001209 .open = via_playback_multi_pcm_open,
1210 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001211 .prepare = via_playback_multi_pcm_prepare,
1212 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001213 },
1214};
1215
Takashi Iwai9af74212011-06-18 16:17:45 +02001216static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001217 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001218 .channels_min = 2,
1219 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001220 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001221 .ops = {
1222 .prepare = via_capture_pcm_prepare,
1223 .cleanup = via_capture_pcm_cleanup
1224 },
1225};
1226
Takashi Iwai9af74212011-06-18 16:17:45 +02001227static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001228 .substreams = 1,
1229 .channels_min = 2,
1230 .channels_max = 2,
1231 /* NID is set in via_build_pcms */
1232 .ops = {
1233 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001234 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001235 .prepare = via_dig_playback_pcm_prepare,
1236 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001237 },
1238};
1239
Takashi Iwai9af74212011-06-18 16:17:45 +02001240static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001241 .substreams = 1,
1242 .channels_min = 2,
1243 .channels_max = 2,
1244};
1245
Takashi Iwai370bafb2011-06-20 12:47:45 +02001246/*
1247 * slave controls for virtual master
1248 */
1249static const char * const via_slave_vols[] = {
1250 "Front Playback Volume",
1251 "Surround Playback Volume",
1252 "Center Playback Volume",
1253 "LFE Playback Volume",
1254 "Side Playback Volume",
1255 "Headphone Playback Volume",
1256 "Speaker Playback Volume",
1257 NULL,
1258};
1259
1260static const char * const via_slave_sws[] = {
1261 "Front Playback Switch",
1262 "Surround Playback Switch",
1263 "Center Playback Switch",
1264 "LFE Playback Switch",
1265 "Side Playback Switch",
1266 "Headphone Playback Switch",
1267 "Speaker Playback Switch",
1268 NULL,
1269};
1270
Joseph Chanc577b8a2006-11-29 15:29:40 +01001271static int via_build_controls(struct hda_codec *codec)
1272{
1273 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001274 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001275 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001276
Takashi Iwai24088a52011-06-17 16:59:21 +02001277 if (spec->set_widgets_power_state)
1278 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1279 return -ENOMEM;
1280
Joseph Chanc577b8a2006-11-29 15:29:40 +01001281 for (i = 0; i < spec->num_mixers; i++) {
1282 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1283 if (err < 0)
1284 return err;
1285 }
1286
1287 if (spec->multiout.dig_out_nid) {
1288 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001289 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001290 spec->multiout.dig_out_nid);
1291 if (err < 0)
1292 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001293 err = snd_hda_create_spdif_share_sw(codec,
1294 &spec->multiout);
1295 if (err < 0)
1296 return err;
1297 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001298 }
1299 if (spec->dig_in_nid) {
1300 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1301 if (err < 0)
1302 return err;
1303 }
Lydia Wang17314372009-10-10 19:07:37 +08001304
Takashi Iwai370bafb2011-06-20 12:47:45 +02001305 /* if we have no master control, let's create it */
1306 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1307 unsigned int vmaster_tlv[4];
1308 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1309 HDA_OUTPUT, vmaster_tlv);
1310 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1311 vmaster_tlv, via_slave_vols);
1312 if (err < 0)
1313 return err;
1314 }
1315 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1316 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1317 NULL, via_slave_sws);
1318 if (err < 0)
1319 return err;
1320 }
1321
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001322 /* assign Capture Source enums to NID */
1323 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1324 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001325 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001326 if (err < 0)
1327 return err;
1328 }
1329
Lydia Wang17314372009-10-10 19:07:37 +08001330 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001331 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001332 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001333
Takashi Iwai603c4012008-07-30 15:01:44 +02001334 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001335 return 0;
1336}
1337
1338static int via_build_pcms(struct hda_codec *codec)
1339{
1340 struct via_spec *spec = codec->spec;
1341 struct hda_pcm *info = spec->pcm_rec;
1342
1343 codec->num_pcms = 1;
1344 codec->pcm_info = info;
1345
Takashi Iwai82673bc2011-06-17 16:24:21 +02001346 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1347 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001348 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001349
1350 if (!spec->stream_analog_playback)
1351 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001352 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001353 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001354 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1355 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001356 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1357 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001358
1359 if (!spec->stream_analog_capture)
1360 spec->stream_analog_capture = &via_pcm_analog_capture;
1361 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1362 *spec->stream_analog_capture;
1363 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1364 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1365 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001366
1367 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1368 codec->num_pcms++;
1369 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001370 snprintf(spec->stream_name_digital,
1371 sizeof(spec->stream_name_digital),
1372 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001373 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001374 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001375 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001376 if (!spec->stream_digital_playback)
1377 spec->stream_digital_playback =
1378 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001379 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001380 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001381 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1382 spec->multiout.dig_out_nid;
1383 }
1384 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001385 if (!spec->stream_digital_capture)
1386 spec->stream_digital_capture =
1387 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001388 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001389 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001390 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1391 spec->dig_in_nid;
1392 }
1393 }
1394
Takashi Iwaiece8d042011-06-19 16:24:21 +02001395 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001396 codec->num_pcms++;
1397 info++;
1398 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1399 "%s HP", codec->chip_name);
1400 info->name = spec->stream_name_hp;
1401 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1402 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001403 spec->hp_dac_nid;
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001404 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001405 return 0;
1406}
1407
1408static void via_free(struct hda_codec *codec)
1409{
1410 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001411
1412 if (!spec)
1413 return;
1414
Takashi Iwai603c4012008-07-30 15:01:44 +02001415 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001416 vt1708_stop_hp_work(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001417 kfree(codec->spec);
1418}
1419
Takashi Iwai64be2852011-06-17 16:51:39 +02001420/* mute/unmute outputs */
1421static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1422 hda_nid_t *pins, bool mute)
1423{
1424 int i;
1425 for (i = 0; i < num_pins; i++)
1426 snd_hda_codec_write(codec, pins[i], 0,
1427 AC_VERB_SET_PIN_WIDGET_CONTROL,
1428 mute ? 0 : PIN_OUT);
1429}
1430
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001431/* mute internal speaker if line-out is plugged */
1432static void via_line_automute(struct hda_codec *codec, int present)
1433{
1434 struct via_spec *spec = codec->spec;
1435
1436 if (!spec->autocfg.speaker_outs)
1437 return;
1438 if (!present)
1439 present = snd_hda_jack_detect(codec,
1440 spec->autocfg.line_out_pins[0]);
1441 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1442 spec->autocfg.speaker_pins,
1443 present);
1444}
1445
Harald Welte69e52a82008-09-09 15:57:32 +08001446/* mute internal speaker if HP is plugged */
1447static void via_hp_automute(struct hda_codec *codec)
1448{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001449 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001450 struct via_spec *spec = codec->spec;
1451
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001452 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
1453 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai64be2852011-06-17 16:51:39 +02001454 toggle_output_mutes(codec, spec->autocfg.line_outs,
1455 spec->autocfg.line_out_pins,
1456 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001457 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001458 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001459}
1460
Harald Welte69e52a82008-09-09 15:57:32 +08001461static void via_gpio_control(struct hda_codec *codec)
1462{
1463 unsigned int gpio_data;
1464 unsigned int vol_counter;
1465 unsigned int vol;
1466 unsigned int master_vol;
1467
1468 struct via_spec *spec = codec->spec;
1469
1470 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1471 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1472
1473 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1474 0xF84, 0) & 0x3F0000) >> 16;
1475
1476 vol = vol_counter & 0x1F;
1477 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1478 AC_VERB_GET_AMP_GAIN_MUTE,
1479 AC_AMP_GET_INPUT);
1480
1481 if (gpio_data == 0x02) {
1482 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001483 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1484 AC_VERB_SET_PIN_WIDGET_CONTROL,
1485 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001486 if (vol_counter & 0x20) {
1487 /* decrease volume */
1488 if (vol > master_vol)
1489 vol = master_vol;
1490 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1491 0, HDA_AMP_VOLMASK,
1492 master_vol-vol);
1493 } else {
1494 /* increase volume */
1495 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1496 HDA_AMP_VOLMASK,
1497 ((master_vol+vol) > 0x2A) ? 0x2A :
1498 (master_vol+vol));
1499 }
1500 } else if (!(gpio_data & 0x02)) {
1501 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001502 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1503 AC_VERB_SET_PIN_WIDGET_CONTROL,
1504 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001505 }
1506}
1507
1508/* unsolicited event for jack sensing */
1509static void via_unsol_event(struct hda_codec *codec,
1510 unsigned int res)
1511{
1512 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001513
Lydia Wanga34df192009-10-10 19:08:01 +08001514 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001515 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001516
1517 res &= ~VIA_JACK_EVENT;
1518
1519 if (res == VIA_HP_EVENT)
1520 via_hp_automute(codec);
1521 else if (res == VIA_GPIO_EVENT)
1522 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001523 else if (res == VIA_LINE_EVENT)
1524 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001525}
1526
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001527#ifdef SND_HDA_NEEDS_RESUME
1528static int via_suspend(struct hda_codec *codec, pm_message_t state)
1529{
1530 struct via_spec *spec = codec->spec;
1531 vt1708_stop_hp_work(spec);
1532 return 0;
1533}
1534#endif
1535
Takashi Iwaicb53c622007-08-10 17:21:45 +02001536#ifdef CONFIG_SND_HDA_POWER_SAVE
1537static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1538{
1539 struct via_spec *spec = codec->spec;
1540 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1541}
1542#endif
1543
Joseph Chanc577b8a2006-11-29 15:29:40 +01001544/*
1545 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001546
1547static int via_init(struct hda_codec *codec);
1548
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001549static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001550 .build_controls = via_build_controls,
1551 .build_pcms = via_build_pcms,
1552 .init = via_init,
1553 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001554 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001555#ifdef SND_HDA_NEEDS_RESUME
1556 .suspend = via_suspend,
1557#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001558#ifdef CONFIG_SND_HDA_POWER_SAVE
1559 .check_power_status = via_check_power_status,
1560#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001561};
1562
Takashi Iwai4a796162011-06-17 17:53:38 +02001563static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001564{
Takashi Iwai4a796162011-06-17 17:53:38 +02001565 struct via_spec *spec = codec->spec;
1566 int i;
1567
1568 for (i = 0; i < spec->multiout.num_dacs; i++) {
1569 if (spec->multiout.dac_nids[i] == dac)
1570 return false;
1571 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001572 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001573 return false;
1574 return true;
1575}
1576
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001577static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001578 hda_nid_t target_dac, struct nid_path *path,
1579 int depth, int wid_type)
1580{
1581 hda_nid_t conn[8];
1582 int i, nums;
1583
1584 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1585 for (i = 0; i < nums; i++) {
1586 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1587 continue;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001588 if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
1589 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001590 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001591 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001592 return false;
1593 for (i = 0; i < nums; i++) {
1594 unsigned int type;
1595 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1596 if (type == AC_WID_AUD_OUT ||
1597 (wid_type != -1 && type != wid_type))
1598 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001599 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001600 path, depth + 1, AC_WID_AUD_SEL))
1601 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001602 }
1603 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001604
1605 found:
1606 path->path[path->depth] = conn[i];
1607 path->idx[path->depth] = i;
1608 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1609 path->multi[path->depth] = 1;
1610 path->depth++;
1611 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001612}
1613
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001614static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1615 hda_nid_t target_dac, struct nid_path *path)
1616{
1617 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1618 path->path[path->depth] = nid;
1619 path->depth++;
1620 return true;
1621 }
1622 return false;
1623}
1624
Takashi Iwai4a796162011-06-17 17:53:38 +02001625static int via_auto_fill_dac_nids(struct hda_codec *codec)
1626{
1627 struct via_spec *spec = codec->spec;
1628 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001629 int i;
1630 hda_nid_t nid;
1631
Joseph Chanc577b8a2006-11-29 15:29:40 +01001632 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001633 spec->multiout.num_dacs = cfg->line_outs;
1634 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001635 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001636 if (!nid)
1637 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001638 if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
1639 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001640 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001641 return 0;
1642}
1643
Takashi Iwai4a796162011-06-17 17:53:38 +02001644static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001645 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001646{
Takashi Iwai4a796162011-06-17 17:53:38 +02001647 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001648 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001649 hda_nid_t dac, pin, sel, nid;
1650 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001651
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001652 dac = check_dac ? path->path[0] : 0;
1653 pin = path->path[path->depth - 1];
1654 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001655
Takashi Iwai8df2a312011-06-21 11:48:29 +02001656 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001657 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001658 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001659 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001660 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1661 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001662 else
1663 nid = 0;
1664 if (nid) {
1665 sprintf(name, "%s Playback Volume", pfx);
1666 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001667 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001668 if (err < 0)
1669 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001670 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001671 }
1672
Takashi Iwai8df2a312011-06-21 11:48:29 +02001673 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001674 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001675 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001676 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001677 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1678 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001679 else
1680 nid = 0;
1681 if (nid) {
1682 sprintf(name, "%s Playback Switch", pfx);
1683 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1684 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1685 if (err < 0)
1686 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001687 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001688 }
1689 return 0;
1690}
1691
Takashi Iwaif4a78282011-06-17 18:46:48 +02001692static void mangle_smart51(struct hda_codec *codec)
1693{
1694 struct via_spec *spec = codec->spec;
1695 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001696 struct auto_pin_cfg_item *ins = cfg->inputs;
1697 int i, j, nums, attr;
1698 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001699
Takashi Iwai0f98c242011-06-21 12:51:33 +02001700 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1701 nums = 0;
1702 for (i = 0; i < cfg->num_inputs; i++) {
1703 unsigned int def;
1704 if (ins[i].type > AUTO_PIN_LINE_IN)
1705 continue;
1706 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1707 if (snd_hda_get_input_pin_attr(def) != attr)
1708 continue;
1709 for (j = 0; j < nums; j++)
1710 if (ins[pins[j]].type < ins[i].type) {
1711 memmove(pins + j + 1, pins + j,
1712 (nums - j - 1) * sizeof(int));
1713 break;
1714 }
1715 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001716 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001717 }
1718 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001719 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001720 for (i = 0; i < nums; i++) {
1721 hda_nid_t pin = ins[pins[i]].pin;
1722 spec->smart51_pins[spec->smart51_nums++] = pin;
1723 cfg->line_out_pins[cfg->line_outs++] = pin;
1724 if (cfg->line_outs == 3)
1725 break;
1726 }
1727 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001728 }
1729}
1730
Takashi Iwai4a796162011-06-17 17:53:38 +02001731/* add playback controls from the parsed DAC table */
1732static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1733{
1734 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001735 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001736 static const char * const chname[4] = {
1737 "Front", "Surround", "C/LFE", "Side"
1738 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001739 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001740 int old_line_outs;
1741
1742 /* check smart51 */
1743 old_line_outs = cfg->line_outs;
1744 if (cfg->line_outs == 1)
1745 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001746
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001747 err = via_auto_fill_dac_nids(codec);
1748 if (err < 0)
1749 return err;
1750
Takashi Iwai4a796162011-06-17 17:53:38 +02001751 for (i = 0; i < cfg->line_outs; i++) {
1752 hda_nid_t pin, dac;
1753 pin = cfg->line_out_pins[i];
1754 dac = spec->multiout.dac_nids[i];
1755 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001756 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001757 if (i == HDA_CLFE) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001758 err = create_ch_ctls(codec, "Center", 1, true,
1759 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001760 if (err < 0)
1761 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001762 err = create_ch_ctls(codec, "LFE", 2, true,
1763 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001764 if (err < 0)
1765 return err;
1766 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001767 const char *pfx = chname[i];
1768 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1769 cfg->line_outs == 1)
1770 pfx = "Speaker";
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001771 err = create_ch_ctls(codec, pfx, 3, true,
1772 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001773 if (err < 0)
1774 return err;
1775 }
1776 }
1777
Takashi Iwai4a796162011-06-17 17:53:38 +02001778 idx = get_connection_index(codec, spec->aa_mix_nid,
1779 spec->multiout.dac_nids[0]);
1780 if (idx >= 0) {
1781 /* add control to mixer */
1782 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1783 "PCM Playback Volume",
1784 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1785 idx, HDA_INPUT));
1786 if (err < 0)
1787 return err;
1788 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1789 "PCM Playback Switch",
1790 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1791 idx, HDA_INPUT));
1792 if (err < 0)
1793 return err;
1794 }
1795
Takashi Iwaif4a78282011-06-17 18:46:48 +02001796 cfg->line_outs = old_line_outs;
1797
Joseph Chanc577b8a2006-11-29 15:29:40 +01001798 return 0;
1799}
1800
Takashi Iwai4a796162011-06-17 17:53:38 +02001801static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001802{
Takashi Iwai4a796162011-06-17 17:53:38 +02001803 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001804 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001805 int err;
1806
1807 if (!pin)
1808 return 0;
1809
Takashi Iwai8df2a312011-06-21 11:48:29 +02001810 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001811 spec->hp_dac_nid = spec->hp_path.path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001812
Takashi Iwaiece8d042011-06-19 16:24:21 +02001813 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001814 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001815 !spec->hp_dac_nid)
1816 return 0;
1817
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001818 if (spec->hp_dac_nid)
1819 path = &spec->hp_path;
1820 else
1821 path = &spec->hp_dep_path;
1822 err = create_ch_ctls(codec, "Headphone", 3, false, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001823 if (err < 0)
1824 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001825 if (spec->hp_dac_nid) {
1826 spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
1827 spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
1828 }
Harald Welte0aa62ae2008-09-09 15:58:27 +08001829
Joseph Chanc577b8a2006-11-29 15:29:40 +01001830 return 0;
1831}
1832
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001833static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1834{
1835 struct via_spec *spec = codec->spec;
1836 hda_nid_t pin, dac;
1837
1838 pin = spec->autocfg.speaker_pins[0];
1839 if (!spec->autocfg.speaker_outs || !pin)
1840 return 0;
1841
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001842 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1843 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001844 spec->multiout.extra_out_nid[0] = dac;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001845 return create_ch_ctls(codec, "Speaker", 3, true,
1846 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001847 }
1848 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001849 &spec->speaker_path))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001850 return create_ch_ctls(codec, "Speaker", 3, false,
1851 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001852
1853 return 0;
1854}
1855
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001856/* look for ADCs */
1857static int via_fill_adcs(struct hda_codec *codec)
1858{
1859 struct via_spec *spec = codec->spec;
1860 hda_nid_t nid = codec->start_nid;
1861 int i;
1862
1863 for (i = 0; i < codec->num_nodes; i++, nid++) {
1864 unsigned int wcaps = get_wcaps(codec, nid);
1865 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1866 continue;
1867 if (wcaps & AC_WCAP_DIGITAL)
1868 continue;
1869 if (!(wcaps & AC_WCAP_CONN_LIST))
1870 continue;
1871 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1872 return -ENOMEM;
1873 spec->adc_nids[spec->num_adc_nids++] = nid;
1874 }
1875 return 0;
1876}
1877
1878static int get_mux_nids(struct hda_codec *codec);
1879
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001880static const struct snd_kcontrol_new via_input_src_ctl = {
1881 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1882 /* The multiple "Capture Source" controls confuse alsamixer
1883 * So call somewhat different..
1884 */
1885 /* .name = "Capture Source", */
1886 .name = "Input Source",
1887 .info = via_mux_enum_info,
1888 .get = via_mux_enum_get,
1889 .put = via_mux_enum_put,
1890};
1891
Takashi Iwai13af8e72011-06-20 14:05:46 +02001892static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
1893{
1894 struct hda_amp_list *list;
1895
1896 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
1897 return;
1898 list = spec->loopback_list + spec->num_loopbacks;
1899 list->nid = mix;
1900 list->dir = HDA_INPUT;
1901 list->idx = idx;
1902 spec->num_loopbacks++;
1903 spec->loopback.amplist = spec->loopback_list;
1904}
Takashi Iwai13af8e72011-06-20 14:05:46 +02001905
Joseph Chanc577b8a2006-11-29 15:29:40 +01001906/* create playback/capture controls for input pins */
Takashi Iwai620e2b22011-06-17 17:19:19 +02001907static int via_auto_create_analog_input_ctls(struct hda_codec *codec,
1908 const struct auto_pin_cfg *cfg)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001909{
Takashi Iwai10a20af2010-09-09 16:28:02 +02001910 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001911 struct hda_input_mux *imux = &spec->private_imux[0];
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001912 int i, j, err, idx, idx2, type, type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02001913 const char *prev_label = NULL;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001914 hda_nid_t cap_nid;
1915 hda_nid_t pin_idxs[8];
1916 int num_idxs;
1917
1918 err = via_fill_adcs(codec);
1919 if (err < 0)
1920 return err;
1921 err = get_mux_nids(codec);
1922 if (err < 0)
1923 return err;
1924 cap_nid = spec->mux_nids[0];
1925
1926 num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
1927 ARRAY_SIZE(pin_idxs));
1928 if (num_idxs <= 0)
1929 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001930
1931 /* for internal loopback recording select */
Takashi Iwaif3268512010-08-30 11:00:19 +02001932 for (idx = 0; idx < num_idxs; idx++) {
Takashi Iwai620e2b22011-06-17 17:19:19 +02001933 if (pin_idxs[idx] == spec->aa_mix_nid) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001934 snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
Takashi Iwaif3268512010-08-30 11:00:19 +02001935 break;
1936 }
1937 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001938
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001939 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001940 const char *label;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001941 type = cfg->inputs[i].type;
Takashi Iwaif3268512010-08-30 11:00:19 +02001942 for (idx = 0; idx < num_idxs; idx++)
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001943 if (pin_idxs[idx] == cfg->inputs[i].pin)
Takashi Iwaif3268512010-08-30 11:00:19 +02001944 break;
1945 if (idx >= num_idxs)
1946 continue;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02001947 label = hda_get_autocfg_input_label(codec, cfg, i);
1948 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001949 type_idx++;
1950 else
1951 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02001952 prev_label = label;
Takashi Iwai620e2b22011-06-17 17:19:19 +02001953 idx2 = get_connection_index(codec, spec->aa_mix_nid,
1954 pin_idxs[idx]);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001955 if (idx2 >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08001956 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwai620e2b22011-06-17 17:19:19 +02001957 idx2, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001958 if (err < 0)
1959 return err;
1960 add_loopback_list(spec, spec->aa_mix_nid, idx2);
1961 }
Takashi Iwai10a20af2010-09-09 16:28:02 +02001962 snd_hda_add_imux_item(imux, label, idx, NULL);
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001963
1964 /* remember the label for smart51 control */
1965 for (j = 0; j < spec->smart51_nums; j++) {
1966 if (spec->smart51_pins[j] == cfg->inputs[i].pin) {
1967 spec->smart51_idxs[j] = idx;
1968 spec->smart51_labels[j] = label;
1969 break;
1970 }
1971 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001972 }
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001973
1974 /* create capture mixer elements */
1975 for (i = 0; i < spec->num_adc_nids; i++) {
1976 hda_nid_t adc = spec->adc_nids[i];
1977 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
1978 "Capture Volume", i,
1979 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1980 HDA_INPUT));
1981 if (err < 0)
1982 return err;
1983 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1984 "Capture Switch", i,
1985 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1986 HDA_INPUT));
1987 if (err < 0)
1988 return err;
1989 }
1990
1991 /* input-source control */
1992 for (i = 0; i < spec->num_adc_nids; i++)
1993 if (!spec->mux_nids[i])
1994 break;
1995 if (i) {
1996 struct snd_kcontrol_new *knew;
1997 knew = via_clone_control(spec, &via_input_src_ctl);
1998 if (!knew)
1999 return -ENOMEM;
2000 knew->count = i;
2001 }
2002
2003 /* mic-boosts */
2004 for (i = 0; i < cfg->num_inputs; i++) {
2005 hda_nid_t pin = cfg->inputs[i].pin;
2006 unsigned int caps;
2007 const char *label;
2008 char name[32];
2009
2010 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2011 continue;
2012 caps = query_amp_caps(codec, pin, HDA_INPUT);
2013 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2014 continue;
2015 label = hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwai30f7c5d2011-06-21 08:37:41 +02002016 snprintf(name, sizeof(name), "%s Boost Volume", label);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002017 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2018 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2019 if (err < 0)
2020 return err;
2021 }
2022
Joseph Chanc577b8a2006-11-29 15:29:40 +01002023 return 0;
2024}
2025
Harald Welte76d9b0d2008-09-09 15:50:37 +08002026static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2027{
2028 unsigned int def_conf;
2029 unsigned char seqassoc;
2030
Takashi Iwai2f334f92009-02-20 14:37:42 +01002031 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002032 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2033 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002034 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2035 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2036 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2037 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002038 }
2039
2040 return;
2041}
2042
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002043static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002044 struct snd_ctl_elem_value *ucontrol)
2045{
2046 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2047 struct via_spec *spec = codec->spec;
2048
2049 if (spec->codec_type != VT1708)
2050 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002051 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002052 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002053 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002054 return 0;
2055}
2056
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002057static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002058 struct snd_ctl_elem_value *ucontrol)
2059{
2060 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2061 struct via_spec *spec = codec->spec;
2062 int change;
2063
2064 if (spec->codec_type != VT1708)
2065 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002066 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002067 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002068 == !spec->vt1708_jack_detect;
2069 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002070 mute_aa_path(codec, 1);
2071 notify_aa_path_ctls(codec);
2072 }
2073 return change;
2074}
2075
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002076static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2077 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2078 .name = "Jack Detect",
2079 .count = 1,
2080 .info = snd_ctl_boolean_mono_info,
2081 .get = vt1708_jack_detect_get,
2082 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002083};
2084
Takashi Iwai12daef62011-06-18 17:45:49 +02002085static void fill_dig_outs(struct hda_codec *codec);
2086static void fill_dig_in(struct hda_codec *codec);
2087
2088static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002089{
2090 struct via_spec *spec = codec->spec;
2091 int err;
2092
2093 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2094 if (err < 0)
2095 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002096 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002097 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002098
Takashi Iwai4a796162011-06-17 17:53:38 +02002099 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002100 if (err < 0)
2101 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002102 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002103 if (err < 0)
2104 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002105 err = via_auto_create_speaker_ctls(codec);
2106 if (err < 0)
2107 return err;
Takashi Iwai620e2b22011-06-17 17:19:19 +02002108 err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002109 if (err < 0)
2110 return err;
2111
2112 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2113
Takashi Iwai12daef62011-06-18 17:45:49 +02002114 fill_dig_outs(codec);
2115 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002116
Takashi Iwai603c4012008-07-30 15:01:44 +02002117 if (spec->kctls.list)
2118 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002119
Takashi Iwai096a8852011-06-20 12:09:02 +02002120 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002121
Harald Welte0aa62ae2008-09-09 15:58:27 +08002122 spec->input_mux = &spec->private_imux[0];
2123
Takashi Iwai8df2a312011-06-21 11:48:29 +02002124 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002125 err = via_hp_build(codec);
2126 if (err < 0)
2127 return err;
2128 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002129
Takashi Iwaif4a78282011-06-17 18:46:48 +02002130 err = via_smart51_build(codec);
2131 if (err < 0)
2132 return err;
2133
Takashi Iwai5d417622011-06-20 11:32:27 +02002134 /* assign slave outs */
2135 if (spec->slave_dig_outs[0])
2136 codec->slave_dig_outs = spec->slave_dig_outs;
2137
Joseph Chanc577b8a2006-11-29 15:29:40 +01002138 return 1;
2139}
2140
Takashi Iwai5d417622011-06-20 11:32:27 +02002141static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002142{
Lydia Wang25eaba22009-10-10 19:08:43 +08002143 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002144 if (spec->multiout.dig_out_nid)
2145 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2146 if (spec->slave_dig_outs[0])
2147 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2148}
Lydia Wang25eaba22009-10-10 19:08:43 +08002149
Takashi Iwai5d417622011-06-20 11:32:27 +02002150static void via_auto_init_dig_in(struct hda_codec *codec)
2151{
2152 struct via_spec *spec = codec->spec;
2153 if (!spec->dig_in_nid)
2154 return;
2155 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2156 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2157}
2158
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002159/* initialize the unsolicited events */
2160static void via_auto_init_unsol_event(struct hda_codec *codec)
2161{
2162 struct via_spec *spec = codec->spec;
2163 struct auto_pin_cfg *cfg = &spec->autocfg;
2164 unsigned int ev;
2165 int i;
2166
2167 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2168 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2169 AC_VERB_SET_UNSOLICITED_ENABLE,
2170 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2171
2172 if (cfg->speaker_pins[0])
2173 ev = VIA_LINE_EVENT;
2174 else
2175 ev = 0;
2176 for (i = 0; i < cfg->line_outs; i++) {
2177 if (cfg->line_out_pins[i] &&
2178 is_jack_detectable(codec, cfg->line_out_pins[i]))
2179 snd_hda_codec_write(codec, cfg->line_out_pins[0], 0,
2180 AC_VERB_SET_UNSOLICITED_ENABLE,
2181 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2182 }
2183
2184 for (i = 0; i < cfg->num_inputs; i++) {
2185 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2186 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2187 AC_VERB_SET_UNSOLICITED_ENABLE,
2188 AC_USRSP_EN | VIA_JACK_EVENT);
2189 }
2190}
2191
Takashi Iwai5d417622011-06-20 11:32:27 +02002192static int via_init(struct hda_codec *codec)
2193{
2194 struct via_spec *spec = codec->spec;
2195 int i;
2196
2197 for (i = 0; i < spec->num_iverbs; i++)
2198 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2199
Joseph Chanc577b8a2006-11-29 15:29:40 +01002200 via_auto_init_multi_out(codec);
2201 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002202 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002203 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002204 via_auto_init_dig_outs(codec);
2205 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002206
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002207 via_auto_init_unsol_event(codec);
2208
2209 via_hp_automute(codec);
2210 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002211
Joseph Chanc577b8a2006-11-29 15:29:40 +01002212 return 0;
2213}
2214
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002215static void vt1708_update_hp_jack_state(struct work_struct *work)
2216{
2217 struct via_spec *spec = container_of(work, struct via_spec,
2218 vt1708_hp_work.work);
2219 if (spec->codec_type != VT1708)
2220 return;
2221 /* if jack state toggled */
2222 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002223 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002224 spec->vt1708_hp_present ^= 1;
2225 via_hp_automute(spec->codec);
2226 }
2227 vt1708_start_hp_work(spec);
2228}
2229
Takashi Iwai337b9d02009-07-07 18:18:59 +02002230static int get_mux_nids(struct hda_codec *codec)
2231{
2232 struct via_spec *spec = codec->spec;
2233 hda_nid_t nid, conn[8];
2234 unsigned int type;
2235 int i, n;
2236
2237 for (i = 0; i < spec->num_adc_nids; i++) {
2238 nid = spec->adc_nids[i];
2239 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002240 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002241 if (type == AC_WID_PIN)
2242 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002243 n = snd_hda_get_connections(codec, nid, conn,
2244 ARRAY_SIZE(conn));
2245 if (n <= 0)
2246 break;
2247 if (n > 1) {
2248 spec->mux_nids[i] = nid;
2249 break;
2250 }
2251 nid = conn[0];
2252 }
2253 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002254 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002255}
2256
Joseph Chanc577b8a2006-11-29 15:29:40 +01002257static int patch_vt1708(struct hda_codec *codec)
2258{
2259 struct via_spec *spec;
2260 int err;
2261
2262 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002263 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002264 if (spec == NULL)
2265 return -ENOMEM;
2266
Takashi Iwai620e2b22011-06-17 17:19:19 +02002267 spec->aa_mix_nid = 0x17;
2268
Takashi Iwai12daef62011-06-18 17:45:49 +02002269 /* Add HP and CD pin config connect bit re-config action */
2270 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2271 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2272
Joseph Chanc577b8a2006-11-29 15:29:40 +01002273 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002274 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002275 if (err < 0) {
2276 via_free(codec);
2277 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002278 }
2279
Takashi Iwai12daef62011-06-18 17:45:49 +02002280 /* add jack detect on/off control */
2281 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2282 return -ENOMEM;
2283
Takashi Iwaibc9b562382008-05-23 17:50:27 +02002284 /* disable 32bit format on VT1708 */
2285 if (codec->vendor_id == 0x11061708)
2286 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002287
Joseph Chanc577b8a2006-11-29 15:29:40 +01002288 codec->patch_ops = via_patch_ops;
2289
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002290 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002291 return 0;
2292}
2293
Joseph Chanc577b8a2006-11-29 15:29:40 +01002294static int patch_vt1709_10ch(struct hda_codec *codec)
2295{
2296 struct via_spec *spec;
2297 int err;
2298
2299 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002300 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002301 if (spec == NULL)
2302 return -ENOMEM;
2303
Takashi Iwai620e2b22011-06-17 17:19:19 +02002304 spec->aa_mix_nid = 0x18;
2305
Takashi Iwai12daef62011-06-18 17:45:49 +02002306 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002307 if (err < 0) {
2308 via_free(codec);
2309 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002310 }
2311
Joseph Chanc577b8a2006-11-29 15:29:40 +01002312 codec->patch_ops = via_patch_ops;
2313
Joseph Chanc577b8a2006-11-29 15:29:40 +01002314 return 0;
2315}
2316/*
2317 * generic initialization of ADC, input mixers and output mixers
2318 */
Joseph Chanc577b8a2006-11-29 15:29:40 +01002319static int patch_vt1709_6ch(struct hda_codec *codec)
2320{
2321 struct via_spec *spec;
2322 int err;
2323
2324 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002325 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002326 if (spec == NULL)
2327 return -ENOMEM;
2328
Takashi Iwai620e2b22011-06-17 17:19:19 +02002329 spec->aa_mix_nid = 0x18;
2330
Takashi Iwai12daef62011-06-18 17:45:49 +02002331 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002332 if (err < 0) {
2333 via_free(codec);
2334 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002335 }
2336
Joseph Chanc577b8a2006-11-29 15:29:40 +01002337 codec->patch_ops = via_patch_ops;
2338
Josepch Chanf7278fd2007-12-13 16:40:40 +01002339 return 0;
2340}
2341
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002342static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2343{
2344 struct via_spec *spec = codec->spec;
2345 int imux_is_smixer;
2346 unsigned int parm;
2347 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002348 if ((spec->codec_type != VT1708B_4CH) &&
2349 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002350 is_8ch = 1;
2351
2352 /* SW0 (17h) = stereo mixer */
2353 imux_is_smixer =
2354 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2355 == ((spec->codec_type == VT1708S) ? 5 : 0));
2356 /* inputs */
2357 /* PW 1/2/5 (1ah/1bh/1eh) */
2358 parm = AC_PWRST_D3;
2359 set_pin_power_state(codec, 0x1a, &parm);
2360 set_pin_power_state(codec, 0x1b, &parm);
2361 set_pin_power_state(codec, 0x1e, &parm);
2362 if (imux_is_smixer)
2363 parm = AC_PWRST_D0;
2364 /* SW0 (17h), AIW 0/1 (13h/14h) */
2365 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2366 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2367 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2368
2369 /* outputs */
2370 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2371 parm = AC_PWRST_D3;
2372 set_pin_power_state(codec, 0x19, &parm);
2373 if (spec->smart51_enabled)
2374 set_pin_power_state(codec, 0x1b, &parm);
2375 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2376 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2377
2378 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2379 if (is_8ch) {
2380 parm = AC_PWRST_D3;
2381 set_pin_power_state(codec, 0x22, &parm);
2382 if (spec->smart51_enabled)
2383 set_pin_power_state(codec, 0x1a, &parm);
2384 snd_hda_codec_write(codec, 0x26, 0,
2385 AC_VERB_SET_POWER_STATE, parm);
2386 snd_hda_codec_write(codec, 0x24, 0,
2387 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002388 } else if (codec->vendor_id == 0x11064397) {
2389 /* PW7(23h), SW2(27h), AOW2(25h) */
2390 parm = AC_PWRST_D3;
2391 set_pin_power_state(codec, 0x23, &parm);
2392 if (spec->smart51_enabled)
2393 set_pin_power_state(codec, 0x1a, &parm);
2394 snd_hda_codec_write(codec, 0x27, 0,
2395 AC_VERB_SET_POWER_STATE, parm);
2396 snd_hda_codec_write(codec, 0x25, 0,
2397 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002398 }
2399
2400 /* PW 3/4/7 (1ch/1dh/23h) */
2401 parm = AC_PWRST_D3;
2402 /* force to D0 for internal Speaker */
2403 set_pin_power_state(codec, 0x1c, &parm);
2404 set_pin_power_state(codec, 0x1d, &parm);
2405 if (is_8ch)
2406 set_pin_power_state(codec, 0x23, &parm);
2407
2408 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2409 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2410 imux_is_smixer ? AC_PWRST_D0 : parm);
2411 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2412 if (is_8ch) {
2413 snd_hda_codec_write(codec, 0x25, 0,
2414 AC_VERB_SET_POWER_STATE, parm);
2415 snd_hda_codec_write(codec, 0x27, 0,
2416 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002417 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2418 snd_hda_codec_write(codec, 0x25, 0,
2419 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002420}
2421
Lydia Wang518bf3b2009-10-10 19:07:29 +08002422static int patch_vt1708S(struct hda_codec *codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002423static int patch_vt1708B_8ch(struct hda_codec *codec)
2424{
2425 struct via_spec *spec;
2426 int err;
2427
Lydia Wang518bf3b2009-10-10 19:07:29 +08002428 if (get_codec_type(codec) == VT1708BCE)
2429 return patch_vt1708S(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002430 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002431 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002432 if (spec == NULL)
2433 return -ENOMEM;
2434
Takashi Iwai620e2b22011-06-17 17:19:19 +02002435 spec->aa_mix_nid = 0x16;
2436
Josepch Chanf7278fd2007-12-13 16:40:40 +01002437 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002438 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002439 if (err < 0) {
2440 via_free(codec);
2441 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002442 }
2443
Josepch Chanf7278fd2007-12-13 16:40:40 +01002444 codec->patch_ops = via_patch_ops;
2445
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002446 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2447
Josepch Chanf7278fd2007-12-13 16:40:40 +01002448 return 0;
2449}
2450
2451static int patch_vt1708B_4ch(struct hda_codec *codec)
2452{
2453 struct via_spec *spec;
2454 int err;
2455
2456 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002457 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002458 if (spec == NULL)
2459 return -ENOMEM;
2460
Josepch Chanf7278fd2007-12-13 16:40:40 +01002461 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002462 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002463 if (err < 0) {
2464 via_free(codec);
2465 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002466 }
2467
Josepch Chanf7278fd2007-12-13 16:40:40 +01002468 codec->patch_ops = via_patch_ops;
2469
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002470 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2471
Joseph Chanc577b8a2006-11-29 15:29:40 +01002472 return 0;
2473}
2474
Harald Welted949cac2008-09-09 15:56:01 +08002475/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002476static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002477 /* Enable Mic Boost Volume backdoor */
2478 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002479 /* don't bybass mixer */
2480 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002481 { }
2482};
2483
Takashi Iwai9da29272009-05-07 16:31:14 +02002484/* fill out digital output widgets; one for master and one for slave outputs */
2485static void fill_dig_outs(struct hda_codec *codec)
2486{
2487 struct via_spec *spec = codec->spec;
2488 int i;
2489
2490 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2491 hda_nid_t nid;
2492 int conn;
2493
2494 nid = spec->autocfg.dig_out_pins[i];
2495 if (!nid)
2496 continue;
2497 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2498 if (conn < 1)
2499 continue;
2500 if (!spec->multiout.dig_out_nid)
2501 spec->multiout.dig_out_nid = nid;
2502 else {
2503 spec->slave_dig_outs[0] = nid;
2504 break; /* at most two dig outs */
2505 }
2506 }
2507}
2508
Takashi Iwai12daef62011-06-18 17:45:49 +02002509static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002510{
2511 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002512 hda_nid_t dig_nid;
2513 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002514
Takashi Iwai12daef62011-06-18 17:45:49 +02002515 if (!spec->autocfg.dig_in_pin)
2516 return;
Harald Welted949cac2008-09-09 15:56:01 +08002517
Takashi Iwai12daef62011-06-18 17:45:49 +02002518 dig_nid = codec->start_nid;
2519 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2520 unsigned int wcaps = get_wcaps(codec, dig_nid);
2521 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2522 continue;
2523 if (!(wcaps & AC_WCAP_DIGITAL))
2524 continue;
2525 if (!(wcaps & AC_WCAP_CONN_LIST))
2526 continue;
2527 err = get_connection_index(codec, dig_nid,
2528 spec->autocfg.dig_in_pin);
2529 if (err >= 0) {
2530 spec->dig_in_nid = dig_nid;
2531 break;
2532 }
2533 }
Harald Welted949cac2008-09-09 15:56:01 +08002534}
2535
Lydia Wang6369bcf2009-10-10 19:08:31 +08002536static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2537 int offset, int num_steps, int step_size)
2538{
2539 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2540 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2541 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2542 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2543 (0 << AC_AMPCAP_MUTE_SHIFT));
2544}
2545
Harald Welted949cac2008-09-09 15:56:01 +08002546static int patch_vt1708S(struct hda_codec *codec)
2547{
2548 struct via_spec *spec;
2549 int err;
2550
2551 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002552 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002553 if (spec == NULL)
2554 return -ENOMEM;
2555
Takashi Iwai620e2b22011-06-17 17:19:19 +02002556 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002557 override_mic_boost(codec, 0x1a, 0, 3, 40);
2558 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002559
Harald Welted949cac2008-09-09 15:56:01 +08002560 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002561 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002562 if (err < 0) {
2563 via_free(codec);
2564 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002565 }
2566
Takashi Iwai096a8852011-06-20 12:09:02 +02002567 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002568
Harald Welted949cac2008-09-09 15:56:01 +08002569 codec->patch_ops = via_patch_ops;
2570
Lydia Wang518bf3b2009-10-10 19:07:29 +08002571 /* correct names for VT1708BCE */
2572 if (get_codec_type(codec) == VT1708BCE) {
2573 kfree(codec->chip_name);
2574 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2575 snprintf(codec->bus->card->mixername,
2576 sizeof(codec->bus->card->mixername),
2577 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f630f2011-03-22 16:25:56 +08002578 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002579 /* correct names for VT1705 */
2580 if (codec->vendor_id == 0x11064397) {
2581 kfree(codec->chip_name);
2582 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2583 snprintf(codec->bus->card->mixername,
2584 sizeof(codec->bus->card->mixername),
2585 "%s %s", codec->vendor_name, codec->chip_name);
2586 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002587 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002588 return 0;
2589}
2590
2591/* Patch for VT1702 */
2592
Takashi Iwai096a8852011-06-20 12:09:02 +02002593static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002594 /* mixer enable */
2595 {0x1, 0xF88, 0x3},
2596 /* GPIO 0~2 */
2597 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002598 { }
2599};
2600
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002601static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2602{
2603 int imux_is_smixer =
2604 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2605 unsigned int parm;
2606 /* inputs */
2607 /* PW 1/2/5 (14h/15h/18h) */
2608 parm = AC_PWRST_D3;
2609 set_pin_power_state(codec, 0x14, &parm);
2610 set_pin_power_state(codec, 0x15, &parm);
2611 set_pin_power_state(codec, 0x18, &parm);
2612 if (imux_is_smixer)
2613 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2614 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2615 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2616 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2617 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2618 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2619
2620 /* outputs */
2621 /* PW 3/4 (16h/17h) */
2622 parm = AC_PWRST_D3;
2623 set_pin_power_state(codec, 0x17, &parm);
2624 set_pin_power_state(codec, 0x16, &parm);
2625 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2626 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2627 imux_is_smixer ? AC_PWRST_D0 : parm);
2628 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2629 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2630}
2631
Harald Welted949cac2008-09-09 15:56:01 +08002632static int patch_vt1702(struct hda_codec *codec)
2633{
2634 struct via_spec *spec;
2635 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002636
2637 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002638 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002639 if (spec == NULL)
2640 return -ENOMEM;
2641
Takashi Iwai620e2b22011-06-17 17:19:19 +02002642 spec->aa_mix_nid = 0x1a;
2643
Takashi Iwai12daef62011-06-18 17:45:49 +02002644 /* limit AA path volume to 0 dB */
2645 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2646 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2647 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2648 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2649 (1 << AC_AMPCAP_MUTE_SHIFT));
2650
Harald Welted949cac2008-09-09 15:56:01 +08002651 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002652 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002653 if (err < 0) {
2654 via_free(codec);
2655 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002656 }
2657
Takashi Iwai096a8852011-06-20 12:09:02 +02002658 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002659
Harald Welted949cac2008-09-09 15:56:01 +08002660 codec->patch_ops = via_patch_ops;
2661
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002662 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002663 return 0;
2664}
2665
Lydia Wangeb7188c2009-10-10 19:08:34 +08002666/* Patch for VT1718S */
2667
Takashi Iwai096a8852011-06-20 12:09:02 +02002668static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002669 /* Enable MW0 adjust Gain 5 */
2670 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002671 /* Enable Boost Volume backdoor */
2672 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002673
Lydia Wangeb7188c2009-10-10 19:08:34 +08002674 { }
2675};
2676
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002677static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2678{
2679 struct via_spec *spec = codec->spec;
2680 int imux_is_smixer;
2681 unsigned int parm;
2682 /* MUX6 (1eh) = stereo mixer */
2683 imux_is_smixer =
2684 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2685 /* inputs */
2686 /* PW 5/6/7 (29h/2ah/2bh) */
2687 parm = AC_PWRST_D3;
2688 set_pin_power_state(codec, 0x29, &parm);
2689 set_pin_power_state(codec, 0x2a, &parm);
2690 set_pin_power_state(codec, 0x2b, &parm);
2691 if (imux_is_smixer)
2692 parm = AC_PWRST_D0;
2693 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2694 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2695 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2696 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2697 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2698
2699 /* outputs */
2700 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2701 parm = AC_PWRST_D3;
2702 set_pin_power_state(codec, 0x27, &parm);
2703 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2704 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2705
2706 /* PW2 (26h), AOW2 (ah) */
2707 parm = AC_PWRST_D3;
2708 set_pin_power_state(codec, 0x26, &parm);
2709 if (spec->smart51_enabled)
2710 set_pin_power_state(codec, 0x2b, &parm);
2711 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2712
2713 /* PW0 (24h), AOW0 (8h) */
2714 parm = AC_PWRST_D3;
2715 set_pin_power_state(codec, 0x24, &parm);
2716 if (!spec->hp_independent_mode) /* check for redirected HP */
2717 set_pin_power_state(codec, 0x28, &parm);
2718 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2719 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2720 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2721 imux_is_smixer ? AC_PWRST_D0 : parm);
2722
2723 /* PW1 (25h), AOW1 (9h) */
2724 parm = AC_PWRST_D3;
2725 set_pin_power_state(codec, 0x25, &parm);
2726 if (spec->smart51_enabled)
2727 set_pin_power_state(codec, 0x2a, &parm);
2728 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2729
2730 if (spec->hp_independent_mode) {
2731 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2732 parm = AC_PWRST_D3;
2733 set_pin_power_state(codec, 0x28, &parm);
2734 snd_hda_codec_write(codec, 0x1b, 0,
2735 AC_VERB_SET_POWER_STATE, parm);
2736 snd_hda_codec_write(codec, 0x34, 0,
2737 AC_VERB_SET_POWER_STATE, parm);
2738 snd_hda_codec_write(codec, 0xc, 0,
2739 AC_VERB_SET_POWER_STATE, parm);
2740 }
2741}
2742
Lydia Wangeb7188c2009-10-10 19:08:34 +08002743static int patch_vt1718S(struct hda_codec *codec)
2744{
2745 struct via_spec *spec;
2746 int err;
2747
2748 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002749 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002750 if (spec == NULL)
2751 return -ENOMEM;
2752
Takashi Iwai620e2b22011-06-17 17:19:19 +02002753 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002754 override_mic_boost(codec, 0x2b, 0, 3, 40);
2755 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002756
Lydia Wangeb7188c2009-10-10 19:08:34 +08002757 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002758 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002759 if (err < 0) {
2760 via_free(codec);
2761 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002762 }
2763
Takashi Iwai096a8852011-06-20 12:09:02 +02002764 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002765
Lydia Wangeb7188c2009-10-10 19:08:34 +08002766 codec->patch_ops = via_patch_ops;
2767
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002768 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2769
Lydia Wangeb7188c2009-10-10 19:08:34 +08002770 return 0;
2771}
Lydia Wangf3db4232009-10-10 19:08:41 +08002772
2773/* Patch for VT1716S */
2774
2775static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2776 struct snd_ctl_elem_info *uinfo)
2777{
2778 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2779 uinfo->count = 1;
2780 uinfo->value.integer.min = 0;
2781 uinfo->value.integer.max = 1;
2782 return 0;
2783}
2784
2785static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2786 struct snd_ctl_elem_value *ucontrol)
2787{
2788 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2789 int index = 0;
2790
2791 index = snd_hda_codec_read(codec, 0x26, 0,
2792 AC_VERB_GET_CONNECT_SEL, 0);
2793 if (index != -1)
2794 *ucontrol->value.integer.value = index;
2795
2796 return 0;
2797}
2798
2799static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2800 struct snd_ctl_elem_value *ucontrol)
2801{
2802 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2803 struct via_spec *spec = codec->spec;
2804 int index = *ucontrol->value.integer.value;
2805
2806 snd_hda_codec_write(codec, 0x26, 0,
2807 AC_VERB_SET_CONNECT_SEL, index);
2808 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002809 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002810 return 1;
2811}
2812
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002813static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002814 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2815 {
2816 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2817 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002818 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08002819 .count = 1,
2820 .info = vt1716s_dmic_info,
2821 .get = vt1716s_dmic_get,
2822 .put = vt1716s_dmic_put,
2823 },
2824 {} /* end */
2825};
2826
2827
2828/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002829static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002830 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
2831 { } /* end */
2832};
2833
Takashi Iwai096a8852011-06-20 12:09:02 +02002834static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002835 /* Enable Boost Volume backdoor */
2836 {0x1, 0xf8a, 0x80},
2837 /* don't bybass mixer */
2838 {0x1, 0xf88, 0xc0},
2839 /* Enable mono output */
2840 {0x1, 0xf90, 0x08},
2841 { }
2842};
2843
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002844static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
2845{
2846 struct via_spec *spec = codec->spec;
2847 int imux_is_smixer;
2848 unsigned int parm;
2849 unsigned int mono_out, present;
2850 /* SW0 (17h) = stereo mixer */
2851 imux_is_smixer =
2852 (snd_hda_codec_read(codec, 0x17, 0,
2853 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
2854 /* inputs */
2855 /* PW 1/2/5 (1ah/1bh/1eh) */
2856 parm = AC_PWRST_D3;
2857 set_pin_power_state(codec, 0x1a, &parm);
2858 set_pin_power_state(codec, 0x1b, &parm);
2859 set_pin_power_state(codec, 0x1e, &parm);
2860 if (imux_is_smixer)
2861 parm = AC_PWRST_D0;
2862 /* SW0 (17h), AIW0(13h) */
2863 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2864 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2865
2866 parm = AC_PWRST_D3;
2867 set_pin_power_state(codec, 0x1e, &parm);
2868 /* PW11 (22h) */
2869 if (spec->dmic_enabled)
2870 set_pin_power_state(codec, 0x22, &parm);
2871 else
2872 snd_hda_codec_write(codec, 0x22, 0,
2873 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
2874
2875 /* SW2(26h), AIW1(14h) */
2876 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
2877 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2878
2879 /* outputs */
2880 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2881 parm = AC_PWRST_D3;
2882 set_pin_power_state(codec, 0x19, &parm);
2883 /* Smart 5.1 PW2(1bh) */
2884 if (spec->smart51_enabled)
2885 set_pin_power_state(codec, 0x1b, &parm);
2886 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2887 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2888
2889 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
2890 parm = AC_PWRST_D3;
2891 set_pin_power_state(codec, 0x23, &parm);
2892 /* Smart 5.1 PW1(1ah) */
2893 if (spec->smart51_enabled)
2894 set_pin_power_state(codec, 0x1a, &parm);
2895 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
2896
2897 /* Smart 5.1 PW5(1eh) */
2898 if (spec->smart51_enabled)
2899 set_pin_power_state(codec, 0x1e, &parm);
2900 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
2901
2902 /* Mono out */
2903 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
2904 present = snd_hda_jack_detect(codec, 0x1c);
2905
2906 if (present)
2907 mono_out = 0;
2908 else {
2909 present = snd_hda_jack_detect(codec, 0x1d);
2910 if (!spec->hp_independent_mode && present)
2911 mono_out = 0;
2912 else
2913 mono_out = 1;
2914 }
2915 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
2916 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
2917 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
2918 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
2919
2920 /* PW 3/4 (1ch/1dh) */
2921 parm = AC_PWRST_D3;
2922 set_pin_power_state(codec, 0x1c, &parm);
2923 set_pin_power_state(codec, 0x1d, &parm);
2924 /* HP Independent Mode, power on AOW3 */
2925 if (spec->hp_independent_mode)
2926 snd_hda_codec_write(codec, 0x25, 0,
2927 AC_VERB_SET_POWER_STATE, parm);
2928
2929 /* force to D0 for internal Speaker */
2930 /* MW0 (16h), AOW0 (10h) */
2931 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2932 imux_is_smixer ? AC_PWRST_D0 : parm);
2933 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
2934 mono_out ? AC_PWRST_D0 : parm);
2935}
2936
Lydia Wangf3db4232009-10-10 19:08:41 +08002937static int patch_vt1716S(struct hda_codec *codec)
2938{
2939 struct via_spec *spec;
2940 int err;
2941
2942 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002943 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002944 if (spec == NULL)
2945 return -ENOMEM;
2946
Takashi Iwai620e2b22011-06-17 17:19:19 +02002947 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002948 override_mic_boost(codec, 0x1a, 0, 3, 40);
2949 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002950
Lydia Wangf3db4232009-10-10 19:08:41 +08002951 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002952 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002953 if (err < 0) {
2954 via_free(codec);
2955 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08002956 }
2957
Takashi Iwai096a8852011-06-20 12:09:02 +02002958 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08002959
Lydia Wangf3db4232009-10-10 19:08:41 +08002960 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
2961 spec->num_mixers++;
2962
2963 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
2964
2965 codec->patch_ops = via_patch_ops;
2966
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002967 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08002968 return 0;
2969}
Lydia Wang25eaba22009-10-10 19:08:43 +08002970
2971/* for vt2002P */
2972
Takashi Iwai096a8852011-06-20 12:09:02 +02002973static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08002974 /* Class-D speaker related verbs */
2975 {0x1, 0xfe0, 0x4},
2976 {0x1, 0xfe9, 0x80},
2977 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08002978 /* Enable Boost Volume backdoor */
2979 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08002980 /* Enable AOW0 to MW9 */
2981 {0x1, 0xfb8, 0x88},
2982 { }
2983};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002984
Takashi Iwai096a8852011-06-20 12:09:02 +02002985static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08002986 /* Enable Boost Volume backdoor */
2987 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08002988 /* Enable AOW0 to MW9 */
2989 {0x1, 0xfb8, 0x88},
2990 { }
2991};
Lydia Wang25eaba22009-10-10 19:08:43 +08002992
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002993static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
2994{
2995 struct via_spec *spec = codec->spec;
2996 int imux_is_smixer;
2997 unsigned int parm;
2998 unsigned int present;
2999 /* MUX9 (1eh) = stereo mixer */
3000 imux_is_smixer =
3001 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3002 /* inputs */
3003 /* PW 5/6/7 (29h/2ah/2bh) */
3004 parm = AC_PWRST_D3;
3005 set_pin_power_state(codec, 0x29, &parm);
3006 set_pin_power_state(codec, 0x2a, &parm);
3007 set_pin_power_state(codec, 0x2b, &parm);
3008 parm = AC_PWRST_D0;
3009 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3010 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3011 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3012 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3013 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3014
3015 /* outputs */
3016 /* AOW0 (8h)*/
3017 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3018
Lydia Wang118909562011-03-23 17:57:34 +08003019 if (spec->codec_type == VT1802) {
3020 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3021 parm = AC_PWRST_D3;
3022 set_pin_power_state(codec, 0x28, &parm);
3023 snd_hda_codec_write(codec, 0x18, 0,
3024 AC_VERB_SET_POWER_STATE, parm);
3025 snd_hda_codec_write(codec, 0x38, 0,
3026 AC_VERB_SET_POWER_STATE, parm);
3027 } else {
3028 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3029 parm = AC_PWRST_D3;
3030 set_pin_power_state(codec, 0x26, &parm);
3031 snd_hda_codec_write(codec, 0x1c, 0,
3032 AC_VERB_SET_POWER_STATE, parm);
3033 snd_hda_codec_write(codec, 0x37, 0,
3034 AC_VERB_SET_POWER_STATE, parm);
3035 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003036
Lydia Wang118909562011-03-23 17:57:34 +08003037 if (spec->codec_type == VT1802) {
3038 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3039 parm = AC_PWRST_D3;
3040 set_pin_power_state(codec, 0x25, &parm);
3041 snd_hda_codec_write(codec, 0x15, 0,
3042 AC_VERB_SET_POWER_STATE, parm);
3043 snd_hda_codec_write(codec, 0x35, 0,
3044 AC_VERB_SET_POWER_STATE, parm);
3045 } else {
3046 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3047 parm = AC_PWRST_D3;
3048 set_pin_power_state(codec, 0x25, &parm);
3049 snd_hda_codec_write(codec, 0x19, 0,
3050 AC_VERB_SET_POWER_STATE, parm);
3051 snd_hda_codec_write(codec, 0x35, 0,
3052 AC_VERB_SET_POWER_STATE, parm);
3053 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003054
3055 if (spec->hp_independent_mode)
3056 snd_hda_codec_write(codec, 0x9, 0,
3057 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3058
3059 /* Class-D */
3060 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3061 present = snd_hda_jack_detect(codec, 0x25);
3062
3063 parm = AC_PWRST_D3;
3064 set_pin_power_state(codec, 0x24, &parm);
3065 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003066 if (spec->codec_type == VT1802)
3067 snd_hda_codec_write(codec, 0x14, 0,
3068 AC_VERB_SET_POWER_STATE, parm);
3069 else
3070 snd_hda_codec_write(codec, 0x18, 0,
3071 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003072 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3073
3074 /* Mono Out */
3075 present = snd_hda_jack_detect(codec, 0x26);
3076
3077 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003078 if (spec->codec_type == VT1802) {
3079 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3080 snd_hda_codec_write(codec, 0x33, 0,
3081 AC_VERB_SET_POWER_STATE, parm);
3082 snd_hda_codec_write(codec, 0x1c, 0,
3083 AC_VERB_SET_POWER_STATE, parm);
3084 snd_hda_codec_write(codec, 0x3c, 0,
3085 AC_VERB_SET_POWER_STATE, parm);
3086 } else {
3087 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3088 snd_hda_codec_write(codec, 0x31, 0,
3089 AC_VERB_SET_POWER_STATE, parm);
3090 snd_hda_codec_write(codec, 0x17, 0,
3091 AC_VERB_SET_POWER_STATE, parm);
3092 snd_hda_codec_write(codec, 0x3b, 0,
3093 AC_VERB_SET_POWER_STATE, parm);
3094 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003095 /* MW9 (21h) */
3096 if (imux_is_smixer || !is_aa_path_mute(codec))
3097 snd_hda_codec_write(codec, 0x21, 0,
3098 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3099 else
3100 snd_hda_codec_write(codec, 0x21, 0,
3101 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3102}
Lydia Wang25eaba22009-10-10 19:08:43 +08003103
3104/* patch for vt2002P */
3105static int patch_vt2002P(struct hda_codec *codec)
3106{
3107 struct via_spec *spec;
3108 int err;
3109
3110 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003111 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003112 if (spec == NULL)
3113 return -ENOMEM;
3114
Takashi Iwai620e2b22011-06-17 17:19:19 +02003115 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003116 override_mic_boost(codec, 0x2b, 0, 3, 40);
3117 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003118
Lydia Wang25eaba22009-10-10 19:08:43 +08003119 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003120 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003121 if (err < 0) {
3122 via_free(codec);
3123 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003124 }
3125
Lydia Wang118909562011-03-23 17:57:34 +08003126 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003127 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003128 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003129 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003130
Lydia Wang25eaba22009-10-10 19:08:43 +08003131 codec->patch_ops = via_patch_ops;
3132
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003133 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003134 return 0;
3135}
Lydia Wangab6734e2009-10-10 19:08:46 +08003136
3137/* for vt1812 */
3138
Takashi Iwai096a8852011-06-20 12:09:02 +02003139static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003140 /* Enable Boost Volume backdoor */
3141 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003142 /* Enable AOW0 to MW9 */
3143 {0x1, 0xfb8, 0xa8},
3144 { }
3145};
3146
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003147static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3148{
3149 struct via_spec *spec = codec->spec;
3150 int imux_is_smixer =
3151 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3152 unsigned int parm;
3153 unsigned int present;
3154 /* MUX10 (1eh) = stereo mixer */
3155 imux_is_smixer =
3156 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3157 /* inputs */
3158 /* PW 5/6/7 (29h/2ah/2bh) */
3159 parm = AC_PWRST_D3;
3160 set_pin_power_state(codec, 0x29, &parm);
3161 set_pin_power_state(codec, 0x2a, &parm);
3162 set_pin_power_state(codec, 0x2b, &parm);
3163 parm = AC_PWRST_D0;
3164 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3165 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3166 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3167 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3168 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3169
3170 /* outputs */
3171 /* AOW0 (8h)*/
3172 snd_hda_codec_write(codec, 0x8, 0,
3173 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3174
3175 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3176 parm = AC_PWRST_D3;
3177 set_pin_power_state(codec, 0x28, &parm);
3178 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3179 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3180
3181 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3182 parm = AC_PWRST_D3;
3183 set_pin_power_state(codec, 0x25, &parm);
3184 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3185 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3186 if (spec->hp_independent_mode)
3187 snd_hda_codec_write(codec, 0x9, 0,
3188 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3189
3190 /* Internal Speaker */
3191 /* PW0 (24h), MW0(14h), MUX0(34h) */
3192 present = snd_hda_jack_detect(codec, 0x25);
3193
3194 parm = AC_PWRST_D3;
3195 set_pin_power_state(codec, 0x24, &parm);
3196 if (present) {
3197 snd_hda_codec_write(codec, 0x14, 0,
3198 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3199 snd_hda_codec_write(codec, 0x34, 0,
3200 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3201 } else {
3202 snd_hda_codec_write(codec, 0x14, 0,
3203 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3204 snd_hda_codec_write(codec, 0x34, 0,
3205 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3206 }
3207
3208
3209 /* Mono Out */
3210 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3211 present = snd_hda_jack_detect(codec, 0x28);
3212
3213 parm = AC_PWRST_D3;
3214 set_pin_power_state(codec, 0x31, &parm);
3215 if (present) {
3216 snd_hda_codec_write(codec, 0x1c, 0,
3217 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3218 snd_hda_codec_write(codec, 0x3c, 0,
3219 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3220 snd_hda_codec_write(codec, 0x3e, 0,
3221 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3222 } else {
3223 snd_hda_codec_write(codec, 0x1c, 0,
3224 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3225 snd_hda_codec_write(codec, 0x3c, 0,
3226 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3227 snd_hda_codec_write(codec, 0x3e, 0,
3228 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3229 }
3230
3231 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3232 parm = AC_PWRST_D3;
3233 set_pin_power_state(codec, 0x33, &parm);
3234 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3235 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3236
3237}
Lydia Wangab6734e2009-10-10 19:08:46 +08003238
3239/* patch for vt1812 */
3240static int patch_vt1812(struct hda_codec *codec)
3241{
3242 struct via_spec *spec;
3243 int err;
3244
3245 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003246 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003247 if (spec == NULL)
3248 return -ENOMEM;
3249
Takashi Iwai620e2b22011-06-17 17:19:19 +02003250 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003251 override_mic_boost(codec, 0x2b, 0, 3, 40);
3252 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003253
Lydia Wangab6734e2009-10-10 19:08:46 +08003254 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003255 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003256 if (err < 0) {
3257 via_free(codec);
3258 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003259 }
3260
Takashi Iwai096a8852011-06-20 12:09:02 +02003261 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003262
Lydia Wangab6734e2009-10-10 19:08:46 +08003263 codec->patch_ops = via_patch_ops;
3264
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003265 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003266 return 0;
3267}
3268
Joseph Chanc577b8a2006-11-29 15:29:40 +01003269/*
3270 * patch entries
3271 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003272static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003273 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3274 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3275 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3276 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3277 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003278 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003279 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003280 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003281 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003282 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003283 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003284 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003285 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003286 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003287 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003288 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003289 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003290 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003291 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003292 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003293 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003294 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003295 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003296 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003297 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003298 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003299 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003300 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003301 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003302 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003303 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003304 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003305 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003306 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003307 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003308 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003309 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003310 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003311 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003312 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003313 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003314 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003315 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003316 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003317 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003318 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003319 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003320 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003321 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003322 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003323 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003324 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003325 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003326 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003327 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003328 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003329 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003330 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003331 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003332 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003333 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003334 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003335 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003336 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003337 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003338 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003339 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003340 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003341 { .id = 0x11060428, .name = "VT1718S",
3342 .patch = patch_vt1718S},
3343 { .id = 0x11064428, .name = "VT1718S",
3344 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003345 { .id = 0x11060441, .name = "VT2020",
3346 .patch = patch_vt1718S},
3347 { .id = 0x11064441, .name = "VT1828S",
3348 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003349 { .id = 0x11060433, .name = "VT1716S",
3350 .patch = patch_vt1716S},
3351 { .id = 0x1106a721, .name = "VT1716S",
3352 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003353 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3354 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003355 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003356 { .id = 0x11060440, .name = "VT1818S",
3357 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003358 { .id = 0x11060446, .name = "VT1802",
3359 .patch = patch_vt2002P},
3360 { .id = 0x11068446, .name = "VT1802",
3361 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003362 {} /* terminator */
3363};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003364
3365MODULE_ALIAS("snd-hda-codec-id:1106*");
3366
3367static struct hda_codec_preset_list via_list = {
3368 .preset = snd_hda_preset_via,
3369 .owner = THIS_MODULE,
3370};
3371
3372MODULE_LICENSE("GPL");
3373MODULE_DESCRIPTION("VIA HD-audio codec");
3374
3375static int __init patch_via_init(void)
3376{
3377 return snd_hda_add_codec_preset(&via_list);
3378}
3379
3380static void __exit patch_via_exit(void)
3381{
3382 snd_hda_delete_codec_preset(&via_list);
3383}
3384
3385module_init(patch_via_init)
3386module_exit(patch_via_exit)