blob: 899b96631312203dae3aa0d50e6267706bf49f6f [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 Iwai7eb56e82011-06-18 16:40:14 +0200111 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200112 const struct hda_pcm_stream *stream_analog_playback;
113 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800114
Takashi Iwai82673bc2011-06-17 16:24:21 +0200115 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200116 const struct hda_pcm_stream *stream_digital_playback;
117 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800118
119 /* playback */
120 struct hda_multi_out multiout;
121 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200122 hda_nid_t hp_dac_nid;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200123 int num_active_streams;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800124
Takashi Iwai4a796162011-06-17 17:53:38 +0200125 struct nid_path out_path[4];
126 struct nid_path hp_path;
127 struct nid_path hp_dep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200128 struct nid_path speaker_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200129
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800130 /* capture */
131 unsigned int num_adc_nids;
Takashi Iwaia766d0d2011-06-17 09:01:29 +0200132 hda_nid_t adc_nids[3];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800133 hda_nid_t mux_nids[3];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200134 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800135 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800136
137 /* capture source */
138 const struct hda_input_mux *input_mux;
139 unsigned int cur_mux[3];
140
141 /* PCM information */
142 struct hda_pcm pcm_rec[3];
143
144 /* dynamic controls, init_verbs and input_mux */
145 struct auto_pin_cfg autocfg;
146 struct snd_array kctls;
147 struct hda_input_mux private_imux[2];
148 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
149
150 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800151 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800152 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200153 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800154 enum VIA_HDA_CODEC codec_type;
155
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200156 /* smart51 setup */
157 unsigned int smart51_nums;
158 hda_nid_t smart51_pins[2];
159 int smart51_idxs[2];
160 const char *smart51_labels[2];
161 unsigned int smart51_enabled;
162
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800163 /* work to check hp jack state */
164 struct hda_codec *codec;
165 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200166 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800167 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800168
169 void (*set_widgets_power_state)(struct hda_codec *codec);
170
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800171 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200172 int num_loopbacks;
173 struct hda_amp_list loopback_list[8];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800174};
175
Lydia Wang0341ccd2011-03-22 16:25:03 +0800176static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100177static struct via_spec * via_new_spec(struct hda_codec *codec)
178{
179 struct via_spec *spec;
180
181 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
182 if (spec == NULL)
183 return NULL;
184
185 codec->spec = spec;
186 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800187 spec->codec_type = get_codec_type(codec);
188 /* VT1708BCE & VT1708S are almost same */
189 if (spec->codec_type == VT1708BCE)
190 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100191 return spec;
192}
193
Lydia Wang744ff5f2009-10-10 19:07:26 +0800194static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800195{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800196 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800197 u16 ven_id = vendor_id >> 16;
198 u16 dev_id = vendor_id & 0xffff;
199 enum VIA_HDA_CODEC codec_type;
200
201 /* get codec type */
202 if (ven_id != 0x1106)
203 codec_type = UNKNOWN;
204 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
205 codec_type = VT1708;
206 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
207 codec_type = VT1709_10CH;
208 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
209 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800210 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800211 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800212 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
213 codec_type = VT1708BCE;
214 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800215 codec_type = VT1708B_4CH;
216 else if ((dev_id & 0xfff) == 0x397
217 && (dev_id >> 12) < 8)
218 codec_type = VT1708S;
219 else if ((dev_id & 0xfff) == 0x398
220 && (dev_id >> 12) < 8)
221 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800222 else if ((dev_id & 0xfff) == 0x428
223 && (dev_id >> 12) < 8)
224 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800225 else if (dev_id == 0x0433 || dev_id == 0xa721)
226 codec_type = VT1716S;
Lydia 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 Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-06-18 16:40:14 +02001041
Takashi Iwaiece8d042011-06-19 16:24:21 +02001042 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-06-18 16:40:14 +02001068 vt1708_start_hp_work(spec);
1069 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001070}
1071
Takashi Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-06-18 16:40:14 +02001199 .substreams = 1,
Takashi Iwaibc9b5622008-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 Iwaibc9b5622008-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 Iwaibc9b5622008-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 Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwaibc9b5622008-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
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002294static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002295{
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
Josepch Chanf7278fd2007-12-13 16:40:40 +01002314 return 0;
2315}
2316
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002317static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2318{
2319 struct via_spec *spec = codec->spec;
2320 int imux_is_smixer;
2321 unsigned int parm;
2322 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002323 if ((spec->codec_type != VT1708B_4CH) &&
2324 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002325 is_8ch = 1;
2326
2327 /* SW0 (17h) = stereo mixer */
2328 imux_is_smixer =
2329 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2330 == ((spec->codec_type == VT1708S) ? 5 : 0));
2331 /* inputs */
2332 /* PW 1/2/5 (1ah/1bh/1eh) */
2333 parm = AC_PWRST_D3;
2334 set_pin_power_state(codec, 0x1a, &parm);
2335 set_pin_power_state(codec, 0x1b, &parm);
2336 set_pin_power_state(codec, 0x1e, &parm);
2337 if (imux_is_smixer)
2338 parm = AC_PWRST_D0;
2339 /* SW0 (17h), AIW 0/1 (13h/14h) */
2340 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2341 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2342 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2343
2344 /* outputs */
2345 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2346 parm = AC_PWRST_D3;
2347 set_pin_power_state(codec, 0x19, &parm);
2348 if (spec->smart51_enabled)
2349 set_pin_power_state(codec, 0x1b, &parm);
2350 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2351 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2352
2353 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2354 if (is_8ch) {
2355 parm = AC_PWRST_D3;
2356 set_pin_power_state(codec, 0x22, &parm);
2357 if (spec->smart51_enabled)
2358 set_pin_power_state(codec, 0x1a, &parm);
2359 snd_hda_codec_write(codec, 0x26, 0,
2360 AC_VERB_SET_POWER_STATE, parm);
2361 snd_hda_codec_write(codec, 0x24, 0,
2362 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002363 } else if (codec->vendor_id == 0x11064397) {
2364 /* PW7(23h), SW2(27h), AOW2(25h) */
2365 parm = AC_PWRST_D3;
2366 set_pin_power_state(codec, 0x23, &parm);
2367 if (spec->smart51_enabled)
2368 set_pin_power_state(codec, 0x1a, &parm);
2369 snd_hda_codec_write(codec, 0x27, 0,
2370 AC_VERB_SET_POWER_STATE, parm);
2371 snd_hda_codec_write(codec, 0x25, 0,
2372 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002373 }
2374
2375 /* PW 3/4/7 (1ch/1dh/23h) */
2376 parm = AC_PWRST_D3;
2377 /* force to D0 for internal Speaker */
2378 set_pin_power_state(codec, 0x1c, &parm);
2379 set_pin_power_state(codec, 0x1d, &parm);
2380 if (is_8ch)
2381 set_pin_power_state(codec, 0x23, &parm);
2382
2383 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2384 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2385 imux_is_smixer ? AC_PWRST_D0 : parm);
2386 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2387 if (is_8ch) {
2388 snd_hda_codec_write(codec, 0x25, 0,
2389 AC_VERB_SET_POWER_STATE, parm);
2390 snd_hda_codec_write(codec, 0x27, 0,
2391 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002392 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2393 snd_hda_codec_write(codec, 0x25, 0,
2394 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002395}
2396
Lydia Wang518bf3b2009-10-10 19:07:29 +08002397static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002398static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002399{
2400 struct via_spec *spec;
2401 int err;
2402
Lydia Wang518bf3b2009-10-10 19:07:29 +08002403 if (get_codec_type(codec) == VT1708BCE)
2404 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002405
Josepch Chanf7278fd2007-12-13 16:40:40 +01002406 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002407 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002408 if (spec == NULL)
2409 return -ENOMEM;
2410
Takashi Iwai620e2b22011-06-17 17:19:19 +02002411 spec->aa_mix_nid = 0x16;
2412
Josepch Chanf7278fd2007-12-13 16:40:40 +01002413 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002414 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002415 if (err < 0) {
2416 via_free(codec);
2417 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002418 }
2419
Josepch Chanf7278fd2007-12-13 16:40:40 +01002420 codec->patch_ops = via_patch_ops;
2421
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002422 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2423
Josepch Chanf7278fd2007-12-13 16:40:40 +01002424 return 0;
2425}
2426
Harald Welted949cac2008-09-09 15:56:01 +08002427/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002428static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002429 /* Enable Mic Boost Volume backdoor */
2430 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002431 /* don't bybass mixer */
2432 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002433 { }
2434};
2435
Takashi Iwai9da29272009-05-07 16:31:14 +02002436/* fill out digital output widgets; one for master and one for slave outputs */
2437static void fill_dig_outs(struct hda_codec *codec)
2438{
2439 struct via_spec *spec = codec->spec;
2440 int i;
2441
2442 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2443 hda_nid_t nid;
2444 int conn;
2445
2446 nid = spec->autocfg.dig_out_pins[i];
2447 if (!nid)
2448 continue;
2449 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2450 if (conn < 1)
2451 continue;
2452 if (!spec->multiout.dig_out_nid)
2453 spec->multiout.dig_out_nid = nid;
2454 else {
2455 spec->slave_dig_outs[0] = nid;
2456 break; /* at most two dig outs */
2457 }
2458 }
2459}
2460
Takashi Iwai12daef62011-06-18 17:45:49 +02002461static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002462{
2463 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002464 hda_nid_t dig_nid;
2465 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002466
Takashi Iwai12daef62011-06-18 17:45:49 +02002467 if (!spec->autocfg.dig_in_pin)
2468 return;
Harald Welted949cac2008-09-09 15:56:01 +08002469
Takashi Iwai12daef62011-06-18 17:45:49 +02002470 dig_nid = codec->start_nid;
2471 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2472 unsigned int wcaps = get_wcaps(codec, dig_nid);
2473 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2474 continue;
2475 if (!(wcaps & AC_WCAP_DIGITAL))
2476 continue;
2477 if (!(wcaps & AC_WCAP_CONN_LIST))
2478 continue;
2479 err = get_connection_index(codec, dig_nid,
2480 spec->autocfg.dig_in_pin);
2481 if (err >= 0) {
2482 spec->dig_in_nid = dig_nid;
2483 break;
2484 }
2485 }
Harald Welted949cac2008-09-09 15:56:01 +08002486}
2487
Lydia Wang6369bcf2009-10-10 19:08:31 +08002488static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2489 int offset, int num_steps, int step_size)
2490{
2491 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2492 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2493 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2494 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2495 (0 << AC_AMPCAP_MUTE_SHIFT));
2496}
2497
Harald Welted949cac2008-09-09 15:56:01 +08002498static int patch_vt1708S(struct hda_codec *codec)
2499{
2500 struct via_spec *spec;
2501 int err;
2502
2503 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002504 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002505 if (spec == NULL)
2506 return -ENOMEM;
2507
Takashi Iwai620e2b22011-06-17 17:19:19 +02002508 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002509 override_mic_boost(codec, 0x1a, 0, 3, 40);
2510 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002511
Harald Welted949cac2008-09-09 15:56:01 +08002512 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002513 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002514 if (err < 0) {
2515 via_free(codec);
2516 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002517 }
2518
Takashi Iwai096a8852011-06-20 12:09:02 +02002519 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002520
Harald Welted949cac2008-09-09 15:56:01 +08002521 codec->patch_ops = via_patch_ops;
2522
Lydia Wang518bf3b2009-10-10 19:07:29 +08002523 /* correct names for VT1708BCE */
2524 if (get_codec_type(codec) == VT1708BCE) {
2525 kfree(codec->chip_name);
2526 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2527 snprintf(codec->bus->card->mixername,
2528 sizeof(codec->bus->card->mixername),
2529 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002530 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002531 /* correct names for VT1705 */
2532 if (codec->vendor_id == 0x11064397) {
2533 kfree(codec->chip_name);
2534 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2535 snprintf(codec->bus->card->mixername,
2536 sizeof(codec->bus->card->mixername),
2537 "%s %s", codec->vendor_name, codec->chip_name);
2538 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002539 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002540 return 0;
2541}
2542
2543/* Patch for VT1702 */
2544
Takashi Iwai096a8852011-06-20 12:09:02 +02002545static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002546 /* mixer enable */
2547 {0x1, 0xF88, 0x3},
2548 /* GPIO 0~2 */
2549 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002550 { }
2551};
2552
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002553static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2554{
2555 int imux_is_smixer =
2556 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2557 unsigned int parm;
2558 /* inputs */
2559 /* PW 1/2/5 (14h/15h/18h) */
2560 parm = AC_PWRST_D3;
2561 set_pin_power_state(codec, 0x14, &parm);
2562 set_pin_power_state(codec, 0x15, &parm);
2563 set_pin_power_state(codec, 0x18, &parm);
2564 if (imux_is_smixer)
2565 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2566 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2567 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2568 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2569 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2570 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2571
2572 /* outputs */
2573 /* PW 3/4 (16h/17h) */
2574 parm = AC_PWRST_D3;
2575 set_pin_power_state(codec, 0x17, &parm);
2576 set_pin_power_state(codec, 0x16, &parm);
2577 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2578 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2579 imux_is_smixer ? AC_PWRST_D0 : parm);
2580 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2581 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2582}
2583
Harald Welted949cac2008-09-09 15:56:01 +08002584static int patch_vt1702(struct hda_codec *codec)
2585{
2586 struct via_spec *spec;
2587 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002588
2589 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002590 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002591 if (spec == NULL)
2592 return -ENOMEM;
2593
Takashi Iwai620e2b22011-06-17 17:19:19 +02002594 spec->aa_mix_nid = 0x1a;
2595
Takashi Iwai12daef62011-06-18 17:45:49 +02002596 /* limit AA path volume to 0 dB */
2597 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2598 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2599 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2600 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2601 (1 << AC_AMPCAP_MUTE_SHIFT));
2602
Harald Welted949cac2008-09-09 15:56:01 +08002603 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002604 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002605 if (err < 0) {
2606 via_free(codec);
2607 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002608 }
2609
Takashi Iwai096a8852011-06-20 12:09:02 +02002610 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002611
Harald Welted949cac2008-09-09 15:56:01 +08002612 codec->patch_ops = via_patch_ops;
2613
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002614 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002615 return 0;
2616}
2617
Lydia Wangeb7188c2009-10-10 19:08:34 +08002618/* Patch for VT1718S */
2619
Takashi Iwai096a8852011-06-20 12:09:02 +02002620static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002621 /* Enable MW0 adjust Gain 5 */
2622 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002623 /* Enable Boost Volume backdoor */
2624 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002625
Lydia Wangeb7188c2009-10-10 19:08:34 +08002626 { }
2627};
2628
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002629static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2630{
2631 struct via_spec *spec = codec->spec;
2632 int imux_is_smixer;
2633 unsigned int parm;
2634 /* MUX6 (1eh) = stereo mixer */
2635 imux_is_smixer =
2636 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2637 /* inputs */
2638 /* PW 5/6/7 (29h/2ah/2bh) */
2639 parm = AC_PWRST_D3;
2640 set_pin_power_state(codec, 0x29, &parm);
2641 set_pin_power_state(codec, 0x2a, &parm);
2642 set_pin_power_state(codec, 0x2b, &parm);
2643 if (imux_is_smixer)
2644 parm = AC_PWRST_D0;
2645 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2646 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2647 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2648 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2649 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2650
2651 /* outputs */
2652 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2653 parm = AC_PWRST_D3;
2654 set_pin_power_state(codec, 0x27, &parm);
2655 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2656 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2657
2658 /* PW2 (26h), AOW2 (ah) */
2659 parm = AC_PWRST_D3;
2660 set_pin_power_state(codec, 0x26, &parm);
2661 if (spec->smart51_enabled)
2662 set_pin_power_state(codec, 0x2b, &parm);
2663 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2664
2665 /* PW0 (24h), AOW0 (8h) */
2666 parm = AC_PWRST_D3;
2667 set_pin_power_state(codec, 0x24, &parm);
2668 if (!spec->hp_independent_mode) /* check for redirected HP */
2669 set_pin_power_state(codec, 0x28, &parm);
2670 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2671 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2672 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2673 imux_is_smixer ? AC_PWRST_D0 : parm);
2674
2675 /* PW1 (25h), AOW1 (9h) */
2676 parm = AC_PWRST_D3;
2677 set_pin_power_state(codec, 0x25, &parm);
2678 if (spec->smart51_enabled)
2679 set_pin_power_state(codec, 0x2a, &parm);
2680 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2681
2682 if (spec->hp_independent_mode) {
2683 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2684 parm = AC_PWRST_D3;
2685 set_pin_power_state(codec, 0x28, &parm);
2686 snd_hda_codec_write(codec, 0x1b, 0,
2687 AC_VERB_SET_POWER_STATE, parm);
2688 snd_hda_codec_write(codec, 0x34, 0,
2689 AC_VERB_SET_POWER_STATE, parm);
2690 snd_hda_codec_write(codec, 0xc, 0,
2691 AC_VERB_SET_POWER_STATE, parm);
2692 }
2693}
2694
Lydia Wangeb7188c2009-10-10 19:08:34 +08002695static int patch_vt1718S(struct hda_codec *codec)
2696{
2697 struct via_spec *spec;
2698 int err;
2699
2700 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002701 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002702 if (spec == NULL)
2703 return -ENOMEM;
2704
Takashi Iwai620e2b22011-06-17 17:19:19 +02002705 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002706 override_mic_boost(codec, 0x2b, 0, 3, 40);
2707 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002708
Lydia Wangeb7188c2009-10-10 19:08:34 +08002709 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002710 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002711 if (err < 0) {
2712 via_free(codec);
2713 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002714 }
2715
Takashi Iwai096a8852011-06-20 12:09:02 +02002716 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002717
Lydia Wangeb7188c2009-10-10 19:08:34 +08002718 codec->patch_ops = via_patch_ops;
2719
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002720 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2721
Lydia Wangeb7188c2009-10-10 19:08:34 +08002722 return 0;
2723}
Lydia Wangf3db4232009-10-10 19:08:41 +08002724
2725/* Patch for VT1716S */
2726
2727static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2728 struct snd_ctl_elem_info *uinfo)
2729{
2730 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2731 uinfo->count = 1;
2732 uinfo->value.integer.min = 0;
2733 uinfo->value.integer.max = 1;
2734 return 0;
2735}
2736
2737static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2738 struct snd_ctl_elem_value *ucontrol)
2739{
2740 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2741 int index = 0;
2742
2743 index = snd_hda_codec_read(codec, 0x26, 0,
2744 AC_VERB_GET_CONNECT_SEL, 0);
2745 if (index != -1)
2746 *ucontrol->value.integer.value = index;
2747
2748 return 0;
2749}
2750
2751static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2752 struct snd_ctl_elem_value *ucontrol)
2753{
2754 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2755 struct via_spec *spec = codec->spec;
2756 int index = *ucontrol->value.integer.value;
2757
2758 snd_hda_codec_write(codec, 0x26, 0,
2759 AC_VERB_SET_CONNECT_SEL, index);
2760 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002761 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002762 return 1;
2763}
2764
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002765static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002766 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2767 {
2768 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2769 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002770 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08002771 .count = 1,
2772 .info = vt1716s_dmic_info,
2773 .get = vt1716s_dmic_get,
2774 .put = vt1716s_dmic_put,
2775 },
2776 {} /* end */
2777};
2778
2779
2780/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002781static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002782 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
2783 { } /* end */
2784};
2785
Takashi Iwai096a8852011-06-20 12:09:02 +02002786static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002787 /* Enable Boost Volume backdoor */
2788 {0x1, 0xf8a, 0x80},
2789 /* don't bybass mixer */
2790 {0x1, 0xf88, 0xc0},
2791 /* Enable mono output */
2792 {0x1, 0xf90, 0x08},
2793 { }
2794};
2795
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002796static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
2797{
2798 struct via_spec *spec = codec->spec;
2799 int imux_is_smixer;
2800 unsigned int parm;
2801 unsigned int mono_out, present;
2802 /* SW0 (17h) = stereo mixer */
2803 imux_is_smixer =
2804 (snd_hda_codec_read(codec, 0x17, 0,
2805 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
2806 /* inputs */
2807 /* PW 1/2/5 (1ah/1bh/1eh) */
2808 parm = AC_PWRST_D3;
2809 set_pin_power_state(codec, 0x1a, &parm);
2810 set_pin_power_state(codec, 0x1b, &parm);
2811 set_pin_power_state(codec, 0x1e, &parm);
2812 if (imux_is_smixer)
2813 parm = AC_PWRST_D0;
2814 /* SW0 (17h), AIW0(13h) */
2815 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2816 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2817
2818 parm = AC_PWRST_D3;
2819 set_pin_power_state(codec, 0x1e, &parm);
2820 /* PW11 (22h) */
2821 if (spec->dmic_enabled)
2822 set_pin_power_state(codec, 0x22, &parm);
2823 else
2824 snd_hda_codec_write(codec, 0x22, 0,
2825 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
2826
2827 /* SW2(26h), AIW1(14h) */
2828 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
2829 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2830
2831 /* outputs */
2832 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2833 parm = AC_PWRST_D3;
2834 set_pin_power_state(codec, 0x19, &parm);
2835 /* Smart 5.1 PW2(1bh) */
2836 if (spec->smart51_enabled)
2837 set_pin_power_state(codec, 0x1b, &parm);
2838 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2839 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2840
2841 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
2842 parm = AC_PWRST_D3;
2843 set_pin_power_state(codec, 0x23, &parm);
2844 /* Smart 5.1 PW1(1ah) */
2845 if (spec->smart51_enabled)
2846 set_pin_power_state(codec, 0x1a, &parm);
2847 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
2848
2849 /* Smart 5.1 PW5(1eh) */
2850 if (spec->smart51_enabled)
2851 set_pin_power_state(codec, 0x1e, &parm);
2852 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
2853
2854 /* Mono out */
2855 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
2856 present = snd_hda_jack_detect(codec, 0x1c);
2857
2858 if (present)
2859 mono_out = 0;
2860 else {
2861 present = snd_hda_jack_detect(codec, 0x1d);
2862 if (!spec->hp_independent_mode && present)
2863 mono_out = 0;
2864 else
2865 mono_out = 1;
2866 }
2867 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
2868 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
2869 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
2870 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
2871
2872 /* PW 3/4 (1ch/1dh) */
2873 parm = AC_PWRST_D3;
2874 set_pin_power_state(codec, 0x1c, &parm);
2875 set_pin_power_state(codec, 0x1d, &parm);
2876 /* HP Independent Mode, power on AOW3 */
2877 if (spec->hp_independent_mode)
2878 snd_hda_codec_write(codec, 0x25, 0,
2879 AC_VERB_SET_POWER_STATE, parm);
2880
2881 /* force to D0 for internal Speaker */
2882 /* MW0 (16h), AOW0 (10h) */
2883 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2884 imux_is_smixer ? AC_PWRST_D0 : parm);
2885 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
2886 mono_out ? AC_PWRST_D0 : parm);
2887}
2888
Lydia Wangf3db4232009-10-10 19:08:41 +08002889static int patch_vt1716S(struct hda_codec *codec)
2890{
2891 struct via_spec *spec;
2892 int err;
2893
2894 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002895 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002896 if (spec == NULL)
2897 return -ENOMEM;
2898
Takashi Iwai620e2b22011-06-17 17:19:19 +02002899 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002900 override_mic_boost(codec, 0x1a, 0, 3, 40);
2901 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002902
Lydia Wangf3db4232009-10-10 19:08:41 +08002903 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002904 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002905 if (err < 0) {
2906 via_free(codec);
2907 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08002908 }
2909
Takashi Iwai096a8852011-06-20 12:09:02 +02002910 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08002911
Lydia Wangf3db4232009-10-10 19:08:41 +08002912 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
2913 spec->num_mixers++;
2914
2915 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
2916
2917 codec->patch_ops = via_patch_ops;
2918
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002919 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08002920 return 0;
2921}
Lydia Wang25eaba22009-10-10 19:08:43 +08002922
2923/* for vt2002P */
2924
Takashi Iwai096a8852011-06-20 12:09:02 +02002925static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08002926 /* Class-D speaker related verbs */
2927 {0x1, 0xfe0, 0x4},
2928 {0x1, 0xfe9, 0x80},
2929 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08002930 /* Enable Boost Volume backdoor */
2931 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08002932 /* Enable AOW0 to MW9 */
2933 {0x1, 0xfb8, 0x88},
2934 { }
2935};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002936
Takashi Iwai096a8852011-06-20 12:09:02 +02002937static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08002938 /* Enable Boost Volume backdoor */
2939 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08002940 /* Enable AOW0 to MW9 */
2941 {0x1, 0xfb8, 0x88},
2942 { }
2943};
Lydia Wang25eaba22009-10-10 19:08:43 +08002944
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002945static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
2946{
2947 struct via_spec *spec = codec->spec;
2948 int imux_is_smixer;
2949 unsigned int parm;
2950 unsigned int present;
2951 /* MUX9 (1eh) = stereo mixer */
2952 imux_is_smixer =
2953 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2954 /* inputs */
2955 /* PW 5/6/7 (29h/2ah/2bh) */
2956 parm = AC_PWRST_D3;
2957 set_pin_power_state(codec, 0x29, &parm);
2958 set_pin_power_state(codec, 0x2a, &parm);
2959 set_pin_power_state(codec, 0x2b, &parm);
2960 parm = AC_PWRST_D0;
2961 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
2962 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2963 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2964 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2965 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2966
2967 /* outputs */
2968 /* AOW0 (8h)*/
2969 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2970
Lydia Wang118909562011-03-23 17:57:34 +08002971 if (spec->codec_type == VT1802) {
2972 /* PW4 (28h), MW4 (18h), MUX4(38h) */
2973 parm = AC_PWRST_D3;
2974 set_pin_power_state(codec, 0x28, &parm);
2975 snd_hda_codec_write(codec, 0x18, 0,
2976 AC_VERB_SET_POWER_STATE, parm);
2977 snd_hda_codec_write(codec, 0x38, 0,
2978 AC_VERB_SET_POWER_STATE, parm);
2979 } else {
2980 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
2981 parm = AC_PWRST_D3;
2982 set_pin_power_state(codec, 0x26, &parm);
2983 snd_hda_codec_write(codec, 0x1c, 0,
2984 AC_VERB_SET_POWER_STATE, parm);
2985 snd_hda_codec_write(codec, 0x37, 0,
2986 AC_VERB_SET_POWER_STATE, parm);
2987 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002988
Lydia Wang118909562011-03-23 17:57:34 +08002989 if (spec->codec_type == VT1802) {
2990 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
2991 parm = AC_PWRST_D3;
2992 set_pin_power_state(codec, 0x25, &parm);
2993 snd_hda_codec_write(codec, 0x15, 0,
2994 AC_VERB_SET_POWER_STATE, parm);
2995 snd_hda_codec_write(codec, 0x35, 0,
2996 AC_VERB_SET_POWER_STATE, parm);
2997 } else {
2998 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
2999 parm = AC_PWRST_D3;
3000 set_pin_power_state(codec, 0x25, &parm);
3001 snd_hda_codec_write(codec, 0x19, 0,
3002 AC_VERB_SET_POWER_STATE, parm);
3003 snd_hda_codec_write(codec, 0x35, 0,
3004 AC_VERB_SET_POWER_STATE, parm);
3005 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003006
3007 if (spec->hp_independent_mode)
3008 snd_hda_codec_write(codec, 0x9, 0,
3009 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3010
3011 /* Class-D */
3012 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3013 present = snd_hda_jack_detect(codec, 0x25);
3014
3015 parm = AC_PWRST_D3;
3016 set_pin_power_state(codec, 0x24, &parm);
3017 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003018 if (spec->codec_type == VT1802)
3019 snd_hda_codec_write(codec, 0x14, 0,
3020 AC_VERB_SET_POWER_STATE, parm);
3021 else
3022 snd_hda_codec_write(codec, 0x18, 0,
3023 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003024 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3025
3026 /* Mono Out */
3027 present = snd_hda_jack_detect(codec, 0x26);
3028
3029 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003030 if (spec->codec_type == VT1802) {
3031 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3032 snd_hda_codec_write(codec, 0x33, 0,
3033 AC_VERB_SET_POWER_STATE, parm);
3034 snd_hda_codec_write(codec, 0x1c, 0,
3035 AC_VERB_SET_POWER_STATE, parm);
3036 snd_hda_codec_write(codec, 0x3c, 0,
3037 AC_VERB_SET_POWER_STATE, parm);
3038 } else {
3039 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3040 snd_hda_codec_write(codec, 0x31, 0,
3041 AC_VERB_SET_POWER_STATE, parm);
3042 snd_hda_codec_write(codec, 0x17, 0,
3043 AC_VERB_SET_POWER_STATE, parm);
3044 snd_hda_codec_write(codec, 0x3b, 0,
3045 AC_VERB_SET_POWER_STATE, parm);
3046 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003047 /* MW9 (21h) */
3048 if (imux_is_smixer || !is_aa_path_mute(codec))
3049 snd_hda_codec_write(codec, 0x21, 0,
3050 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3051 else
3052 snd_hda_codec_write(codec, 0x21, 0,
3053 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3054}
Lydia Wang25eaba22009-10-10 19:08:43 +08003055
3056/* patch for vt2002P */
3057static int patch_vt2002P(struct hda_codec *codec)
3058{
3059 struct via_spec *spec;
3060 int err;
3061
3062 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003063 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003064 if (spec == NULL)
3065 return -ENOMEM;
3066
Takashi Iwai620e2b22011-06-17 17:19:19 +02003067 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003068 override_mic_boost(codec, 0x2b, 0, 3, 40);
3069 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003070
Lydia Wang25eaba22009-10-10 19:08:43 +08003071 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003072 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003073 if (err < 0) {
3074 via_free(codec);
3075 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003076 }
3077
Lydia Wang118909562011-03-23 17:57:34 +08003078 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003079 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003080 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003081 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003082
Lydia Wang25eaba22009-10-10 19:08:43 +08003083 codec->patch_ops = via_patch_ops;
3084
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003085 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003086 return 0;
3087}
Lydia Wangab6734e2009-10-10 19:08:46 +08003088
3089/* for vt1812 */
3090
Takashi Iwai096a8852011-06-20 12:09:02 +02003091static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003092 /* Enable Boost Volume backdoor */
3093 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003094 /* Enable AOW0 to MW9 */
3095 {0x1, 0xfb8, 0xa8},
3096 { }
3097};
3098
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003099static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3100{
3101 struct via_spec *spec = codec->spec;
3102 int imux_is_smixer =
3103 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3104 unsigned int parm;
3105 unsigned int present;
3106 /* MUX10 (1eh) = stereo mixer */
3107 imux_is_smixer =
3108 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3109 /* inputs */
3110 /* PW 5/6/7 (29h/2ah/2bh) */
3111 parm = AC_PWRST_D3;
3112 set_pin_power_state(codec, 0x29, &parm);
3113 set_pin_power_state(codec, 0x2a, &parm);
3114 set_pin_power_state(codec, 0x2b, &parm);
3115 parm = AC_PWRST_D0;
3116 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3117 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3118 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3119 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3120 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3121
3122 /* outputs */
3123 /* AOW0 (8h)*/
3124 snd_hda_codec_write(codec, 0x8, 0,
3125 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3126
3127 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3128 parm = AC_PWRST_D3;
3129 set_pin_power_state(codec, 0x28, &parm);
3130 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3131 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3132
3133 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3134 parm = AC_PWRST_D3;
3135 set_pin_power_state(codec, 0x25, &parm);
3136 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3137 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3138 if (spec->hp_independent_mode)
3139 snd_hda_codec_write(codec, 0x9, 0,
3140 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3141
3142 /* Internal Speaker */
3143 /* PW0 (24h), MW0(14h), MUX0(34h) */
3144 present = snd_hda_jack_detect(codec, 0x25);
3145
3146 parm = AC_PWRST_D3;
3147 set_pin_power_state(codec, 0x24, &parm);
3148 if (present) {
3149 snd_hda_codec_write(codec, 0x14, 0,
3150 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3151 snd_hda_codec_write(codec, 0x34, 0,
3152 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3153 } else {
3154 snd_hda_codec_write(codec, 0x14, 0,
3155 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3156 snd_hda_codec_write(codec, 0x34, 0,
3157 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3158 }
3159
3160
3161 /* Mono Out */
3162 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3163 present = snd_hda_jack_detect(codec, 0x28);
3164
3165 parm = AC_PWRST_D3;
3166 set_pin_power_state(codec, 0x31, &parm);
3167 if (present) {
3168 snd_hda_codec_write(codec, 0x1c, 0,
3169 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3170 snd_hda_codec_write(codec, 0x3c, 0,
3171 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3172 snd_hda_codec_write(codec, 0x3e, 0,
3173 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3174 } else {
3175 snd_hda_codec_write(codec, 0x1c, 0,
3176 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3177 snd_hda_codec_write(codec, 0x3c, 0,
3178 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3179 snd_hda_codec_write(codec, 0x3e, 0,
3180 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3181 }
3182
3183 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3184 parm = AC_PWRST_D3;
3185 set_pin_power_state(codec, 0x33, &parm);
3186 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3187 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3188
3189}
Lydia Wangab6734e2009-10-10 19:08:46 +08003190
3191/* patch for vt1812 */
3192static int patch_vt1812(struct hda_codec *codec)
3193{
3194 struct via_spec *spec;
3195 int err;
3196
3197 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003198 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003199 if (spec == NULL)
3200 return -ENOMEM;
3201
Takashi Iwai620e2b22011-06-17 17:19:19 +02003202 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003203 override_mic_boost(codec, 0x2b, 0, 3, 40);
3204 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003205
Lydia Wangab6734e2009-10-10 19:08:46 +08003206 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003207 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003208 if (err < 0) {
3209 via_free(codec);
3210 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003211 }
3212
Takashi Iwai096a8852011-06-20 12:09:02 +02003213 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003214
Lydia Wangab6734e2009-10-10 19:08:46 +08003215 codec->patch_ops = via_patch_ops;
3216
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003217 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003218 return 0;
3219}
3220
Joseph Chanc577b8a2006-11-29 15:29:40 +01003221/*
3222 * patch entries
3223 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003224static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003225 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3226 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3227 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3228 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3229 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003230 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003231 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003232 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003233 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003234 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003235 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003236 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003237 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003238 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003239 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003240 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003241 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003242 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003243 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003244 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003245 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003246 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003247 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003248 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003249 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003250 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003251 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003252 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003253 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003254 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003255 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003256 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003257 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003258 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003259 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003260 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003261 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003262 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003263 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003264 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003265 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003266 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003267 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003268 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003269 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003270 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003271 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003272 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003273 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003274 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003275 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003276 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003277 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003278 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003279 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003280 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003281 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003282 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003283 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003284 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003285 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003286 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003287 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003288 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003289 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003290 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003291 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003292 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003293 { .id = 0x11060428, .name = "VT1718S",
3294 .patch = patch_vt1718S},
3295 { .id = 0x11064428, .name = "VT1718S",
3296 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003297 { .id = 0x11060441, .name = "VT2020",
3298 .patch = patch_vt1718S},
3299 { .id = 0x11064441, .name = "VT1828S",
3300 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003301 { .id = 0x11060433, .name = "VT1716S",
3302 .patch = patch_vt1716S},
3303 { .id = 0x1106a721, .name = "VT1716S",
3304 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003305 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3306 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003307 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003308 { .id = 0x11060440, .name = "VT1818S",
3309 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003310 { .id = 0x11060446, .name = "VT1802",
3311 .patch = patch_vt2002P},
3312 { .id = 0x11068446, .name = "VT1802",
3313 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003314 {} /* terminator */
3315};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003316
3317MODULE_ALIAS("snd-hda-codec-id:1106*");
3318
3319static struct hda_codec_preset_list via_list = {
3320 .preset = snd_hda_preset_via,
3321 .owner = THIS_MODULE,
3322};
3323
3324MODULE_LICENSE("GPL");
3325MODULE_DESCRIPTION("VIA HD-audio codec");
3326
3327static int __init patch_via_init(void)
3328{
3329 return snd_hda_add_codec_preset(&via_list);
3330}
3331
3332static void __exit patch_via_exit(void)
3333{
3334 snd_hda_delete_codec_preset(&via_list);
3335}
3336
3337module_init(patch_via_init)
3338module_exit(patch_via_exit)