blob: 7b353405e06850bdc4870c7e0346f7043ebcefe9 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010052#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080053#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010054#include "hda_codec.h"
55#include "hda_local.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010056
Joseph Chanc577b8a2006-11-29 15:29:40 +010057/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080058#define VT1708_HP_PIN_NID 0x20
59#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010060
Harald Welted7426322008-09-15 22:43:23 +080061enum VIA_HDA_CODEC {
62 UNKNOWN = -1,
63 VT1708,
64 VT1709_10CH,
65 VT1709_6CH,
66 VT1708B_8CH,
67 VT1708B_4CH,
68 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080069 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080070 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080071 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080072 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080073 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080074 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080075 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080076 CODEC_TYPES,
77};
78
Lydia Wang118909562011-03-23 17:57:34 +080079#define VT2002P_COMPATIBLE(spec) \
80 ((spec)->codec_type == VT2002P ||\
81 (spec)->codec_type == VT1812 ||\
82 (spec)->codec_type == VT1802)
83
Takashi Iwai8e3679d2011-06-21 09:01:36 +020084#define MAX_NID_PATH_DEPTH 5
85
Takashi Iwai4a796162011-06-17 17:53:38 +020086struct nid_path {
87 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020088 hda_nid_t path[MAX_NID_PATH_DEPTH];
89 short idx[MAX_NID_PATH_DEPTH];
Takashi Iwai4a796162011-06-17 17:53:38 +020090};
91
Lydia Wang1f2e99f2009-10-10 19:08:17 +080092struct via_spec {
93 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +020094 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +080095 unsigned int num_mixers;
96
Takashi Iwai90dd48a2011-05-02 12:38:19 +020097 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +080098 unsigned int num_iverbs;
99
Takashi Iwai82673bc2011-06-17 16:24:21 +0200100 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200101 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200102 const struct hda_pcm_stream *stream_analog_playback;
103 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800104
Takashi Iwai82673bc2011-06-17 16:24:21 +0200105 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200106 const struct hda_pcm_stream *stream_digital_playback;
107 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800108
109 /* playback */
110 struct hda_multi_out multiout;
111 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200112 hda_nid_t hp_dac_nid;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200113 int num_active_streams;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800114
Takashi Iwai4a796162011-06-17 17:53:38 +0200115 struct nid_path out_path[4];
116 struct nid_path hp_path;
117 struct nid_path hp_dep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200118 struct nid_path speaker_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200119
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800120 /* capture */
121 unsigned int num_adc_nids;
Takashi Iwaia766d0d2011-06-17 09:01:29 +0200122 hda_nid_t adc_nids[3];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800123 hda_nid_t mux_nids[3];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200124 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800125 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800126
127 /* capture source */
128 const struct hda_input_mux *input_mux;
129 unsigned int cur_mux[3];
130
131 /* PCM information */
132 struct hda_pcm pcm_rec[3];
133
134 /* dynamic controls, init_verbs and input_mux */
135 struct auto_pin_cfg autocfg;
136 struct snd_array kctls;
137 struct hda_input_mux private_imux[2];
138 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
139
140 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800141 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800142 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200143 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800144 enum VIA_HDA_CODEC codec_type;
145
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200146 /* smart51 setup */
147 unsigned int smart51_nums;
148 hda_nid_t smart51_pins[2];
149 int smart51_idxs[2];
150 const char *smart51_labels[2];
151 unsigned int smart51_enabled;
152
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800153 /* work to check hp jack state */
154 struct hda_codec *codec;
155 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200156 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800157 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800158
159 void (*set_widgets_power_state)(struct hda_codec *codec);
160
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800161 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200162 int num_loopbacks;
163 struct hda_amp_list loopback_list[8];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800164};
165
Lydia Wang0341ccd2011-03-22 16:25:03 +0800166static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100167static struct via_spec * via_new_spec(struct hda_codec *codec)
168{
169 struct via_spec *spec;
170
171 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
172 if (spec == NULL)
173 return NULL;
174
175 codec->spec = spec;
176 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800177 spec->codec_type = get_codec_type(codec);
178 /* VT1708BCE & VT1708S are almost same */
179 if (spec->codec_type == VT1708BCE)
180 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100181 return spec;
182}
183
Lydia Wang744ff5f2009-10-10 19:07:26 +0800184static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800185{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800186 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800187 u16 ven_id = vendor_id >> 16;
188 u16 dev_id = vendor_id & 0xffff;
189 enum VIA_HDA_CODEC codec_type;
190
191 /* get codec type */
192 if (ven_id != 0x1106)
193 codec_type = UNKNOWN;
194 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
195 codec_type = VT1708;
196 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
197 codec_type = VT1709_10CH;
198 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
199 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800200 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800201 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800202 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
203 codec_type = VT1708BCE;
204 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800205 codec_type = VT1708B_4CH;
206 else if ((dev_id & 0xfff) == 0x397
207 && (dev_id >> 12) < 8)
208 codec_type = VT1708S;
209 else if ((dev_id & 0xfff) == 0x398
210 && (dev_id >> 12) < 8)
211 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800212 else if ((dev_id & 0xfff) == 0x428
213 && (dev_id >> 12) < 8)
214 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800215 else if (dev_id == 0x0433 || dev_id == 0xa721)
216 codec_type = VT1716S;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +0800217 else if (dev_id == 0x0441 || dev_id == 0x4441)
218 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800219 else if (dev_id == 0x0438 || dev_id == 0x4438)
220 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800221 else if (dev_id == 0x0448)
222 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800223 else if (dev_id == 0x0440)
224 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800225 else if ((dev_id & 0xfff) == 0x446)
226 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800227 else
228 codec_type = UNKNOWN;
229 return codec_type;
230};
231
Lydia Wangec7e7e42011-03-24 12:43:44 +0800232#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800233#define VIA_HP_EVENT 0x01
234#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200235#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800236
Joseph Chanc577b8a2006-11-29 15:29:40 +0100237enum {
238 VIA_CTL_WIDGET_VOL,
239 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800240 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100241};
242
Takashi Iwaiada509e2011-06-20 15:40:19 +0200243static void analog_low_current_mode(struct hda_codec *codec);
244static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800245
246static void vt1708_start_hp_work(struct via_spec *spec)
247{
248 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
249 return;
250 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200251 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800252 if (!delayed_work_pending(&spec->vt1708_hp_work))
253 schedule_delayed_work(&spec->vt1708_hp_work,
254 msecs_to_jiffies(100));
255}
256
257static void vt1708_stop_hp_work(struct via_spec *spec)
258{
259 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
260 return;
261 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
262 && !is_aa_path_mute(spec->codec))
263 return;
264 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200265 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100266 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800267}
Lydia Wangf5271102009-10-10 19:07:35 +0800268
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800269static void set_widgets_power_state(struct hda_codec *codec)
270{
271 struct via_spec *spec = codec->spec;
272 if (spec->set_widgets_power_state)
273 spec->set_widgets_power_state(codec);
274}
Lydia Wang25eaba22009-10-10 19:08:43 +0800275
Lydia Wangf5271102009-10-10 19:07:35 +0800276static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
277 struct snd_ctl_elem_value *ucontrol)
278{
279 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
280 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
281
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800282 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200283 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800284 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
285 if (is_aa_path_mute(codec))
286 vt1708_start_hp_work(codec->spec);
287 else
288 vt1708_stop_hp_work(codec->spec);
289 }
Lydia Wangf5271102009-10-10 19:07:35 +0800290 return change;
291}
292
293/* modify .put = snd_hda_mixer_amp_switch_put */
294#define ANALOG_INPUT_MUTE \
295 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
296 .name = NULL, \
297 .index = 0, \
298 .info = snd_hda_mixer_amp_switch_info, \
299 .get = snd_hda_mixer_amp_switch_get, \
300 .put = analog_input_switch_put, \
301 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
302
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200303static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100304 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
305 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800306 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100307};
308
Lydia Wangab6734e2009-10-10 19:08:46 +0800309
Joseph Chanc577b8a2006-11-29 15:29:40 +0100310/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200311static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
312 const struct snd_kcontrol_new *tmpl,
313 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100314{
315 struct snd_kcontrol_new *knew;
316
Takashi Iwai603c4012008-07-30 15:01:44 +0200317 snd_array_init(&spec->kctls, sizeof(*knew), 32);
318 knew = snd_array_new(&spec->kctls);
319 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200320 return NULL;
321 *knew = *tmpl;
322 if (!name)
323 name = tmpl->name;
324 if (name) {
325 knew->name = kstrdup(name, GFP_KERNEL);
326 if (!knew->name)
327 return NULL;
328 }
329 return knew;
330}
331
332static int __via_add_control(struct via_spec *spec, int type, const char *name,
333 int idx, unsigned long val)
334{
335 struct snd_kcontrol_new *knew;
336
337 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
338 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100339 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200340 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100341 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100342 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100343 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100344 return 0;
345}
346
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200347#define via_add_control(spec, type, name, val) \
348 __via_add_control(spec, type, name, 0, val)
349
Takashi Iwai291c9e32011-06-17 16:15:26 +0200350#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100351
Takashi Iwai603c4012008-07-30 15:01:44 +0200352static void via_free_kctls(struct hda_codec *codec)
353{
354 struct via_spec *spec = codec->spec;
355
356 if (spec->kctls.list) {
357 struct snd_kcontrol_new *kctl = spec->kctls.list;
358 int i;
359 for (i = 0; i < spec->kctls.used; i++)
360 kfree(kctl[i].name);
361 }
362 snd_array_free(&spec->kctls);
363}
364
Joseph Chanc577b8a2006-11-29 15:29:40 +0100365/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800366static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200367 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100368{
369 char name[32];
370 int err;
371
372 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200373 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100374 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
375 if (err < 0)
376 return err;
377 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200378 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100379 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
380 if (err < 0)
381 return err;
382 return 0;
383}
384
Takashi Iwai5d417622011-06-20 11:32:27 +0200385/* return the index of the given widget nid as the source of mux;
386 * return -1 if not found;
387 * if num_conns is non-NULL, set the total number of connections
388 */
389static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux,
390 hda_nid_t nid, int *num_conns)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100391{
Takashi Iwai5d417622011-06-20 11:32:27 +0200392 hda_nid_t conn[HDA_MAX_NUM_INPUTS];
393 int i, nums;
394
395 nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
396 if (num_conns)
397 *num_conns = nums;
398 for (i = 0; i < nums; i++)
399 if (conn[i] == nid)
400 return i;
401 return -1;
402}
403
404#define get_connection_index(codec, mux, nid) \
405 __get_connection_index(codec, mux, nid, NULL)
406
Takashi Iwai8df2a312011-06-21 11:48:29 +0200407static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
408 unsigned int mask)
409{
410 unsigned int caps = get_wcaps(codec, nid);
411 if (dir == HDA_INPUT)
412 caps &= AC_WCAP_IN_AMP;
413 else
414 caps &= AC_WCAP_OUT_AMP;
415 if (!caps)
416 return false;
417 if (query_amp_caps(codec, nid, dir) & mask)
418 return true;
419 return false;
420}
421
422#define have_vol_or_mute(codec, nid, dir) \
423 check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)
424
Takashi Iwai5d417622011-06-20 11:32:27 +0200425/* unmute input amp and select the specificed source */
426static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid,
427 hda_nid_t src, hda_nid_t mix)
428{
429 int idx, num_conns;
430
431 idx = __get_connection_index(codec, nid, src, &num_conns);
432 if (idx < 0)
433 return;
434
435 /* select the route explicitly when multiple connections exist */
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200436 if (num_conns > 1 &&
437 get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
Lydia Wang377ff312009-10-10 19:08:55 +0800438 snd_hda_codec_write(codec, nid, 0,
Takashi Iwai5d417622011-06-20 11:32:27 +0200439 AC_VERB_SET_CONNECT_SEL, idx);
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200440
Takashi Iwai5d417622011-06-20 11:32:27 +0200441 /* unmute if the input amp is present */
Takashi Iwai8df2a312011-06-21 11:48:29 +0200442 if (have_vol_or_mute(codec, nid, HDA_INPUT))
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200443 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
444 AMP_IN_UNMUTE(idx));
445
446 /* unmute the src output */
Takashi Iwai8df2a312011-06-21 11:48:29 +0200447 if (have_vol_or_mute(codec, src, HDA_OUTPUT))
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200448 snd_hda_codec_write(codec, src, 0, AC_VERB_SET_AMP_GAIN_MUTE,
449 AMP_OUT_UNMUTE);
Takashi Iwai5d417622011-06-20 11:32:27 +0200450
451 /* unmute AA-path if present */
Takashi Iwai8df2a312011-06-21 11:48:29 +0200452 if (!mix || mix == src)
Takashi Iwai5d417622011-06-20 11:32:27 +0200453 return;
454 idx = __get_connection_index(codec, nid, mix, NULL);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200455 if (idx >= 0 && have_vol_or_mute(codec, nid, HDA_INPUT))
Takashi Iwai5d417622011-06-20 11:32:27 +0200456 snd_hda_codec_write(codec, nid, 0,
457 AC_VERB_SET_AMP_GAIN_MUTE,
458 AMP_IN_UNMUTE(idx));
459}
460
461/* set the given pin as output */
462static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
463 int pin_type)
464{
465 if (!pin)
466 return;
467 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
468 pin_type);
469 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
470 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200471 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100472}
473
Takashi Iwai5d417622011-06-20 11:32:27 +0200474static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin,
475 int pin_type, struct nid_path *path)
476{
477 struct via_spec *spec = codec->spec;
478 unsigned int caps;
479 hda_nid_t nid;
480 int i;
481
482 if (!pin)
483 return;
484
485 init_output_pin(codec, pin, pin_type);
486 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
487 if (caps & AC_AMPCAP_MUTE) {
488 unsigned int val;
489 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
490 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
491 AMP_OUT_MUTE | val);
492 }
493
494 /* initialize the output path */
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200495 for (i = path->depth - 1; i > 0; i--) {
496 nid = path->path[i - 1];
497 unmute_and_select(codec, path->path[i], nid, spec->aa_mix_nid);
Takashi Iwai5d417622011-06-20 11:32:27 +0200498 }
499}
500
Joseph Chanc577b8a2006-11-29 15:29:40 +0100501
502static void via_auto_init_multi_out(struct hda_codec *codec)
503{
504 struct via_spec *spec = codec->spec;
505 int i;
506
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200507 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwai5d417622011-06-20 11:32:27 +0200508 via_auto_init_output(codec, spec->autocfg.line_out_pins[i],
509 PIN_OUT, &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100510}
511
512static void via_auto_init_hp_out(struct hda_codec *codec)
513{
514 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100515
Takashi Iwai5d417622011-06-20 11:32:27 +0200516 if (spec->hp_dac_nid)
517 via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
518 &spec->hp_path);
519 else
520 via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
521 &spec->hp_dep_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100522}
523
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200524static void via_auto_init_speaker_out(struct hda_codec *codec)
525{
526 struct via_spec *spec = codec->spec;
527
528 if (spec->autocfg.speaker_outs)
529 via_auto_init_output(codec, spec->autocfg.speaker_pins[0],
530 PIN_OUT, &spec->speaker_path);
531}
532
Takashi Iwaif4a78282011-06-17 18:46:48 +0200533static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200534
Joseph Chanc577b8a2006-11-29 15:29:40 +0100535static void via_auto_init_analog_input(struct hda_codec *codec)
536{
537 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200538 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200539 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200540 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200541 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100542
Takashi Iwai096a8852011-06-20 12:09:02 +0200543 /* init ADCs */
544 for (i = 0; i < spec->num_adc_nids; i++) {
545 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
546 AC_VERB_SET_AMP_GAIN_MUTE,
547 AMP_IN_UNMUTE(0));
548 }
549
550 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200551 for (i = 0; i < cfg->num_inputs; i++) {
552 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200553 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200554 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100555 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200556 ctl = PIN_VREF50;
557 else
558 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100559 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200560 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100561 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200562
563 /* init input-src */
564 for (i = 0; i < spec->num_adc_nids; i++) {
565 const struct hda_input_mux *imux = spec->input_mux;
566 if (!imux || !spec->mux_nids[i])
567 continue;
568 snd_hda_codec_write(codec, spec->mux_nids[i], 0,
569 AC_VERB_SET_CONNECT_SEL,
570 imux->items[spec->cur_mux[i]].index);
571 }
572
573 /* init aa-mixer */
574 if (!spec->aa_mix_nid)
575 return;
576 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
577 ARRAY_SIZE(conn));
578 for (i = 0; i < num_conns; i++) {
579 unsigned int caps = get_wcaps(codec, conn[i]);
580 if (get_wcaps_type(caps) == AC_WID_PIN)
581 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
582 AC_VERB_SET_AMP_GAIN_MUTE,
583 AMP_IN_MUTE(i));
584 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100585}
Lydia Wangf5271102009-10-10 19:07:35 +0800586
587static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
588 unsigned int *affected_parm)
589{
590 unsigned parm;
591 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
592 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
593 >> AC_DEFCFG_MISC_SHIFT
594 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800595 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200596 unsigned present = 0;
597
598 no_presence |= spec->no_pin_power_ctl;
599 if (!no_presence)
600 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200601 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800602 || ((no_presence || present)
603 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800604 *affected_parm = AC_PWRST_D0; /* if it's connected */
605 parm = AC_PWRST_D0;
606 } else
607 parm = AC_PWRST_D3;
608
609 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
610}
611
Takashi Iwai24088a52011-06-17 16:59:21 +0200612static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
613 struct snd_ctl_elem_info *uinfo)
614{
615 static const char * const texts[] = {
616 "Disabled", "Enabled"
617 };
618
619 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
620 uinfo->count = 1;
621 uinfo->value.enumerated.items = 2;
622 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
623 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
624 strcpy(uinfo->value.enumerated.name,
625 texts[uinfo->value.enumerated.item]);
626 return 0;
627}
628
629static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
630 struct snd_ctl_elem_value *ucontrol)
631{
632 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
633 struct via_spec *spec = codec->spec;
634 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
635 return 0;
636}
637
638static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
639 struct snd_ctl_elem_value *ucontrol)
640{
641 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
642 struct via_spec *spec = codec->spec;
643 unsigned int val = !ucontrol->value.enumerated.item[0];
644
645 if (val == spec->no_pin_power_ctl)
646 return 0;
647 spec->no_pin_power_ctl = val;
648 set_widgets_power_state(codec);
649 return 1;
650}
651
652static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
653 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
654 .name = "Dynamic Power-Control",
655 .info = via_pin_power_ctl_info,
656 .get = via_pin_power_ctl_get,
657 .put = via_pin_power_ctl_put,
658};
659
660
Joseph Chanc577b8a2006-11-29 15:29:40 +0100661/*
662 * input MUX handling
663 */
664static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
665 struct snd_ctl_elem_info *uinfo)
666{
667 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
668 struct via_spec *spec = codec->spec;
669 return snd_hda_input_mux_info(spec->input_mux, uinfo);
670}
671
672static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
673 struct snd_ctl_elem_value *ucontrol)
674{
675 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
676 struct via_spec *spec = codec->spec;
677 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
678
679 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
680 return 0;
681}
682
683static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
684 struct snd_ctl_elem_value *ucontrol)
685{
686 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
687 struct via_spec *spec = codec->spec;
688 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800689 int ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100690
Takashi Iwai337b9d02009-07-07 18:18:59 +0200691 if (!spec->mux_nids[adc_idx])
692 return -EINVAL;
Lydia Wanga80e6e32009-10-10 19:07:55 +0800693 /* switch to D0 beofre change index */
694 if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
695 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
696 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
697 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800698
699 ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
700 spec->mux_nids[adc_idx],
701 &spec->cur_mux[adc_idx]);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800702 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800703 set_widgets_power_state(codec);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800704
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800705 return ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100706}
707
Harald Welte0aa62ae2008-09-09 15:58:27 +0800708static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
709 struct snd_ctl_elem_info *uinfo)
710{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200711 static const char * const texts[] = { "OFF", "ON" };
712
713 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
714 uinfo->count = 1;
715 uinfo->value.enumerated.items = 2;
716 if (uinfo->value.enumerated.item >= 2)
717 uinfo->value.enumerated.item = 1;
718 strcpy(uinfo->value.enumerated.name,
719 texts[uinfo->value.enumerated.item]);
720 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800721}
722
723static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
724 struct snd_ctl_elem_value *ucontrol)
725{
726 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800727 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800728
Takashi Iwaiece8d042011-06-19 16:24:21 +0200729 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800730 return 0;
731}
732
Harald Welte0aa62ae2008-09-09 15:58:27 +0800733static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
734 struct snd_ctl_elem_value *ucontrol)
735{
736 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
737 struct via_spec *spec = codec->spec;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200738 hda_nid_t nid, src;
739 int i, idx, num_conns;
740 struct nid_path *path;
741
742 spec->hp_independent_mode = !!ucontrol->value.enumerated.item[0];
743 if (spec->hp_independent_mode)
744 path = &spec->hp_path;
745 else
746 path = &spec->hp_dep_path;
747
748 /* re-route the output path */
749 for (i = path->depth - 1; i > 0; i--) {
750 nid = path->path[i];
751 src = path->path[i - 1];
752 idx = __get_connection_index(codec, nid, src, &num_conns);
753 if (idx < 0)
754 continue;
755 if (num_conns > 1 &&
756 get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
757 snd_hda_codec_write(codec, nid, 0,
758 AC_VERB_SET_CONNECT_SEL, idx);
759 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800760
Lydia Wangce0e5a92011-03-22 16:22:37 +0800761 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800762 set_widgets_power_state(codec);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800763 return 0;
764}
765
Takashi Iwaiece8d042011-06-19 16:24:21 +0200766static const struct snd_kcontrol_new via_hp_mixer = {
767 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
768 .name = "Independent HP",
769 .info = via_independent_hp_info,
770 .get = via_independent_hp_get,
771 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800772};
773
Takashi Iwai3d83e572010-04-14 14:36:23 +0200774static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100775{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200776 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100777 struct snd_kcontrol_new *knew;
778 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100779
Takashi Iwaiece8d042011-06-19 16:24:21 +0200780 nid = spec->autocfg.hp_pins[0];
781 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200782 if (knew == NULL)
783 return -ENOMEM;
784
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100785 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100786
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100787 return 0;
788}
789
Lydia Wang1564b282009-10-10 19:07:52 +0800790static void notify_aa_path_ctls(struct hda_codec *codec)
791{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200792 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800793 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800794
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200795 for (i = 0; i < spec->smart51_nums; i++) {
796 struct snd_kcontrol *ctl;
797 struct snd_ctl_elem_id id;
798 memset(&id, 0, sizeof(id));
799 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
800 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800801 ctl = snd_hda_find_mixer_ctl(codec, id.name);
802 if (ctl)
803 snd_ctl_notify(codec->bus->card,
804 SNDRV_CTL_EVENT_MASK_VALUE,
805 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800806 }
807}
808
809static void mute_aa_path(struct hda_codec *codec, int mute)
810{
811 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200812 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800813 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200814
Lydia Wang1564b282009-10-10 19:07:52 +0800815 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200816 for (i = 0; i < spec->smart51_nums; i++) {
817 if (spec->smart51_idxs[i] < 0)
818 continue;
819 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
820 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800821 HDA_AMP_MUTE, val);
822 }
823}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200824
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200825static bool is_smart51_candidate(struct hda_codec *codec, hda_nid_t pin)
Lydia Wang1564b282009-10-10 19:07:52 +0800826{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200827 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200828 const struct auto_pin_cfg *cfg = &spec->autocfg;
829 int i;
830
831 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaif4a78282011-06-17 18:46:48 +0200832 unsigned int defcfg;
833 if (pin != cfg->inputs[i].pin)
834 continue;
835 if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
836 return false;
837 defcfg = snd_hda_codec_get_pincfg(codec, pin);
838 if (snd_hda_get_input_pin_attr(defcfg) < INPUT_PIN_ATTR_NORMAL)
839 return false;
840 return true;
Lydia Wang1564b282009-10-10 19:07:52 +0800841 }
Takashi Iwaif4a78282011-06-17 18:46:48 +0200842 return false;
Lydia Wang1564b282009-10-10 19:07:52 +0800843}
844
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200845static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
846{
847 struct via_spec *spec = codec->spec;
848 int i;
849
850 for (i = 0; i < spec->smart51_nums; i++)
851 if (spec->smart51_pins[i] == pin)
852 return true;
853 return false;
854}
855
Lydia Wang1564b282009-10-10 19:07:52 +0800856static int via_smart51_info(struct snd_kcontrol *kcontrol,
857 struct snd_ctl_elem_info *uinfo)
858{
859 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
860 uinfo->count = 1;
861 uinfo->value.integer.min = 0;
862 uinfo->value.integer.max = 1;
863 return 0;
864}
865
866static int via_smart51_get(struct snd_kcontrol *kcontrol,
867 struct snd_ctl_elem_value *ucontrol)
868{
869 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
870 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800871 int on = 1;
872 int i;
873
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200874 for (i = 0; i < spec->smart51_nums; i++) {
875 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwaif4a78282011-06-17 18:46:48 +0200876 unsigned int ctl;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200877 ctl = snd_hda_codec_read(codec, nid, 0,
878 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200879 if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
880 on = 0;
Lydia Wang1564b282009-10-10 19:07:52 +0800881 }
882 *ucontrol->value.integer.value = on;
883 return 0;
884}
885
886static int via_smart51_put(struct snd_kcontrol *kcontrol,
887 struct snd_ctl_elem_value *ucontrol)
888{
889 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
890 struct via_spec *spec = codec->spec;
891 int out_in = *ucontrol->value.integer.value
892 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800893 int i;
894
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200895 for (i = 0; i < spec->smart51_nums; i++) {
896 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200897 unsigned int parm;
898
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200899 parm = snd_hda_codec_read(codec, nid, 0,
900 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
901 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
902 parm |= out_in;
903 snd_hda_codec_write(codec, nid, 0,
904 AC_VERB_SET_PIN_WIDGET_CONTROL,
905 parm);
906 if (out_in == AC_PINCTL_OUT_EN) {
907 mute_aa_path(codec, 1);
908 notify_aa_path_ctls(codec);
909 }
Lydia Wang1564b282009-10-10 19:07:52 +0800910 }
911 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800912 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800913 return 1;
914}
915
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200916static const struct snd_kcontrol_new via_smart51_mixer = {
917 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
918 .name = "Smart 5.1",
919 .count = 1,
920 .info = via_smart51_info,
921 .get = via_smart51_get,
922 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800923};
924
Takashi Iwaif4a78282011-06-17 18:46:48 +0200925static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100926{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200927 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100928
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200929 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800930 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200931 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100932 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100933 return 0;
934}
935
Takashi Iwaiada509e2011-06-20 15:40:19 +0200936/* check AA path's mute status */
937static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800938{
Lydia Wangf5271102009-10-10 19:07:35 +0800939 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200940 const struct hda_amp_list *p;
941 int i, ch, v;
942
943 for (i = 0; i < spec->num_loopbacks; i++) {
944 p = &spec->loopback_list[i];
945 for (ch = 0; ch < 2; ch++) {
946 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
947 p->idx);
948 if (!(v & HDA_AMP_MUTE) && v > 0)
949 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800950 }
951 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200952 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800953}
954
955/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200956static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800957{
958 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200959 bool enable;
960 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800961
Takashi Iwaiada509e2011-06-20 15:40:19 +0200962 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800963
964 /* decide low current mode's verb & parameter */
965 switch (spec->codec_type) {
966 case VT1708B_8CH:
967 case VT1708B_4CH:
968 verb = 0xf70;
969 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
970 break;
971 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800972 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800973 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800974 verb = 0xf73;
975 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
976 break;
977 case VT1702:
978 verb = 0xf73;
979 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
980 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800981 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800982 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800983 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800984 verb = 0xf93;
985 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
986 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800987 default:
988 return; /* other codecs are not supported */
989 }
990 /* send verb */
991 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
992}
993
Joseph Chanc577b8a2006-11-29 15:29:40 +0100994/*
995 * generic initialization of ADC, input mixers and output mixers
996 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200997static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800998 /* power down jack detect function */
999 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001000 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001001};
1002
Takashi Iwaiada509e2011-06-20 15:40:19 +02001003static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001004{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001005 struct via_spec *spec = codec->spec;
1006
1007 if (active)
1008 spec->num_active_streams++;
1009 else
1010 spec->num_active_streams--;
1011 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001012}
1013
Takashi Iwaiece8d042011-06-19 16:24:21 +02001014static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001015 struct hda_codec *codec,
1016 struct snd_pcm_substream *substream)
1017{
1018 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001019 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001020
1021 if (!spec->hp_independent_mode)
1022 spec->multiout.hp_nid = spec->hp_dac_nid;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001023 set_stream_active(codec, true);
1024 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1025 hinfo);
1026 if (err < 0) {
1027 spec->multiout.hp_nid = 0;
1028 set_stream_active(codec, false);
1029 return err;
1030 }
1031 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001032}
1033
Takashi Iwaiece8d042011-06-19 16:24:21 +02001034static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001035 struct hda_codec *codec,
1036 struct snd_pcm_substream *substream)
1037{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001038 struct via_spec *spec = codec->spec;
1039
1040 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001041 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001042 return 0;
1043}
1044
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001045static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1046 struct hda_codec *codec,
1047 struct snd_pcm_substream *substream)
1048{
1049 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001050
Takashi Iwaiece8d042011-06-19 16:24:21 +02001051 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001052 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001053 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1054 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001055 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001056 return 0;
1057}
1058
1059static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1060 struct hda_codec *codec,
1061 struct snd_pcm_substream *substream)
1062{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001063 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001064 return 0;
1065}
1066
1067static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1068 struct hda_codec *codec,
1069 unsigned int stream_tag,
1070 unsigned int format,
1071 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001072{
1073 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001074
Takashi Iwaiece8d042011-06-19 16:24:21 +02001075 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1076 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001077 vt1708_start_hp_work(spec);
1078 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001079}
1080
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001081static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1082 struct hda_codec *codec,
1083 unsigned int stream_tag,
1084 unsigned int format,
1085 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001086{
1087 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001088
Takashi Iwaiece8d042011-06-19 16:24:21 +02001089 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1090 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001091 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001092 return 0;
1093}
1094
1095static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1096 struct hda_codec *codec,
1097 struct snd_pcm_substream *substream)
1098{
1099 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001100
Takashi Iwaiece8d042011-06-19 16:24:21 +02001101 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001102 vt1708_stop_hp_work(spec);
1103 return 0;
1104}
1105
1106static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1107 struct hda_codec *codec,
1108 struct snd_pcm_substream *substream)
1109{
1110 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001111
Takashi Iwaiece8d042011-06-19 16:24:21 +02001112 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001113 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001114 return 0;
1115}
1116
Joseph Chanc577b8a2006-11-29 15:29:40 +01001117/*
1118 * Digital out
1119 */
1120static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1121 struct hda_codec *codec,
1122 struct snd_pcm_substream *substream)
1123{
1124 struct via_spec *spec = codec->spec;
1125 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1126}
1127
1128static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1129 struct hda_codec *codec,
1130 struct snd_pcm_substream *substream)
1131{
1132 struct via_spec *spec = codec->spec;
1133 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1134}
1135
Harald Welte5691ec72008-09-15 22:42:26 +08001136static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001137 struct hda_codec *codec,
1138 unsigned int stream_tag,
1139 unsigned int format,
1140 struct snd_pcm_substream *substream)
1141{
1142 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001143 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1144 stream_tag, format, substream);
1145}
Harald Welte5691ec72008-09-15 22:42:26 +08001146
Takashi Iwai9da29272009-05-07 16:31:14 +02001147static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1148 struct hda_codec *codec,
1149 struct snd_pcm_substream *substream)
1150{
1151 struct via_spec *spec = codec->spec;
1152 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001153 return 0;
1154}
1155
Joseph Chanc577b8a2006-11-29 15:29:40 +01001156/*
1157 * Analog capture
1158 */
1159static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1160 struct hda_codec *codec,
1161 unsigned int stream_tag,
1162 unsigned int format,
1163 struct snd_pcm_substream *substream)
1164{
1165 struct via_spec *spec = codec->spec;
1166
1167 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1168 stream_tag, 0, format);
1169 return 0;
1170}
1171
1172static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1173 struct hda_codec *codec,
1174 struct snd_pcm_substream *substream)
1175{
1176 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001177 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001178 return 0;
1179}
1180
Takashi Iwai9af74212011-06-18 16:17:45 +02001181static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001182 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001183 .channels_min = 2,
1184 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001185 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001186 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001187 .open = via_playback_multi_pcm_open,
1188 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001189 .prepare = via_playback_multi_pcm_prepare,
1190 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001191 },
1192};
1193
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001194static const struct hda_pcm_stream via_pcm_hp_playback = {
1195 .substreams = 1,
1196 .channels_min = 2,
1197 .channels_max = 2,
1198 /* NID is set in via_build_pcms */
1199 .ops = {
1200 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001201 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001202 .prepare = via_playback_hp_pcm_prepare,
1203 .cleanup = via_playback_hp_pcm_cleanup
1204 },
1205};
1206
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001207static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001208 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001209 .channels_min = 2,
1210 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001211 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001212 /* We got noisy outputs on the right channel on VT1708 when
1213 * 24bit samples are used. Until any workaround is found,
1214 * disable the 24bit format, so far.
1215 */
1216 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1217 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001218 .open = via_playback_multi_pcm_open,
1219 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001220 .prepare = via_playback_multi_pcm_prepare,
1221 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001222 },
1223};
1224
Takashi Iwai9af74212011-06-18 16:17:45 +02001225static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001226 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001227 .channels_min = 2,
1228 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001229 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001230 .ops = {
1231 .prepare = via_capture_pcm_prepare,
1232 .cleanup = via_capture_pcm_cleanup
1233 },
1234};
1235
Takashi Iwai9af74212011-06-18 16:17:45 +02001236static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001237 .substreams = 1,
1238 .channels_min = 2,
1239 .channels_max = 2,
1240 /* NID is set in via_build_pcms */
1241 .ops = {
1242 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001243 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001244 .prepare = via_dig_playback_pcm_prepare,
1245 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001246 },
1247};
1248
Takashi Iwai9af74212011-06-18 16:17:45 +02001249static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001250 .substreams = 1,
1251 .channels_min = 2,
1252 .channels_max = 2,
1253};
1254
Takashi Iwai370bafb2011-06-20 12:47:45 +02001255/*
1256 * slave controls for virtual master
1257 */
1258static const char * const via_slave_vols[] = {
1259 "Front Playback Volume",
1260 "Surround Playback Volume",
1261 "Center Playback Volume",
1262 "LFE Playback Volume",
1263 "Side Playback Volume",
1264 "Headphone Playback Volume",
1265 "Speaker Playback Volume",
1266 NULL,
1267};
1268
1269static const char * const via_slave_sws[] = {
1270 "Front Playback Switch",
1271 "Surround Playback Switch",
1272 "Center Playback Switch",
1273 "LFE Playback Switch",
1274 "Side Playback Switch",
1275 "Headphone Playback Switch",
1276 "Speaker Playback Switch",
1277 NULL,
1278};
1279
Joseph Chanc577b8a2006-11-29 15:29:40 +01001280static int via_build_controls(struct hda_codec *codec)
1281{
1282 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001283 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001284 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001285
Takashi Iwai24088a52011-06-17 16:59:21 +02001286 if (spec->set_widgets_power_state)
1287 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1288 return -ENOMEM;
1289
Joseph Chanc577b8a2006-11-29 15:29:40 +01001290 for (i = 0; i < spec->num_mixers; i++) {
1291 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1292 if (err < 0)
1293 return err;
1294 }
1295
1296 if (spec->multiout.dig_out_nid) {
1297 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001298 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001299 spec->multiout.dig_out_nid);
1300 if (err < 0)
1301 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001302 err = snd_hda_create_spdif_share_sw(codec,
1303 &spec->multiout);
1304 if (err < 0)
1305 return err;
1306 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001307 }
1308 if (spec->dig_in_nid) {
1309 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1310 if (err < 0)
1311 return err;
1312 }
Lydia Wang17314372009-10-10 19:07:37 +08001313
Takashi Iwai370bafb2011-06-20 12:47:45 +02001314 /* if we have no master control, let's create it */
1315 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1316 unsigned int vmaster_tlv[4];
1317 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1318 HDA_OUTPUT, vmaster_tlv);
1319 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1320 vmaster_tlv, via_slave_vols);
1321 if (err < 0)
1322 return err;
1323 }
1324 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1325 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1326 NULL, via_slave_sws);
1327 if (err < 0)
1328 return err;
1329 }
1330
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001331 /* assign Capture Source enums to NID */
1332 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1333 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001334 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001335 if (err < 0)
1336 return err;
1337 }
1338
Lydia Wang17314372009-10-10 19:07:37 +08001339 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001340 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001341 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001342
Takashi Iwai603c4012008-07-30 15:01:44 +02001343 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001344 return 0;
1345}
1346
1347static int via_build_pcms(struct hda_codec *codec)
1348{
1349 struct via_spec *spec = codec->spec;
1350 struct hda_pcm *info = spec->pcm_rec;
1351
1352 codec->num_pcms = 1;
1353 codec->pcm_info = info;
1354
Takashi Iwai82673bc2011-06-17 16:24:21 +02001355 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1356 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001357 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001358
1359 if (!spec->stream_analog_playback)
1360 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001361 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001362 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001363 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1364 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001365 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1366 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001367
1368 if (!spec->stream_analog_capture)
1369 spec->stream_analog_capture = &via_pcm_analog_capture;
1370 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1371 *spec->stream_analog_capture;
1372 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1373 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1374 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001375
1376 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1377 codec->num_pcms++;
1378 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001379 snprintf(spec->stream_name_digital,
1380 sizeof(spec->stream_name_digital),
1381 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001382 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001383 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001384 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001385 if (!spec->stream_digital_playback)
1386 spec->stream_digital_playback =
1387 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001388 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001389 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001390 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1391 spec->multiout.dig_out_nid;
1392 }
1393 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001394 if (!spec->stream_digital_capture)
1395 spec->stream_digital_capture =
1396 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001397 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001398 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001399 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1400 spec->dig_in_nid;
1401 }
1402 }
1403
Takashi Iwaiece8d042011-06-19 16:24:21 +02001404 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001405 codec->num_pcms++;
1406 info++;
1407 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1408 "%s HP", codec->chip_name);
1409 info->name = spec->stream_name_hp;
1410 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1411 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001412 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001413 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001414 return 0;
1415}
1416
1417static void via_free(struct hda_codec *codec)
1418{
1419 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001420
1421 if (!spec)
1422 return;
1423
Takashi Iwai603c4012008-07-30 15:01:44 +02001424 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001425 vt1708_stop_hp_work(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001426 kfree(codec->spec);
1427}
1428
Takashi Iwai64be2852011-06-17 16:51:39 +02001429/* mute/unmute outputs */
1430static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1431 hda_nid_t *pins, bool mute)
1432{
1433 int i;
1434 for (i = 0; i < num_pins; i++)
1435 snd_hda_codec_write(codec, pins[i], 0,
1436 AC_VERB_SET_PIN_WIDGET_CONTROL,
1437 mute ? 0 : PIN_OUT);
1438}
1439
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001440/* mute internal speaker if line-out is plugged */
1441static void via_line_automute(struct hda_codec *codec, int present)
1442{
1443 struct via_spec *spec = codec->spec;
1444
1445 if (!spec->autocfg.speaker_outs)
1446 return;
1447 if (!present)
1448 present = snd_hda_jack_detect(codec,
1449 spec->autocfg.line_out_pins[0]);
1450 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1451 spec->autocfg.speaker_pins,
1452 present);
1453}
1454
Harald Welte69e52a82008-09-09 15:57:32 +08001455/* mute internal speaker if HP is plugged */
1456static void via_hp_automute(struct hda_codec *codec)
1457{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001458 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001459 struct via_spec *spec = codec->spec;
1460
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001461 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
1462 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai64be2852011-06-17 16:51:39 +02001463 toggle_output_mutes(codec, spec->autocfg.line_outs,
1464 spec->autocfg.line_out_pins,
1465 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001466 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001467 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001468}
1469
Harald Welte69e52a82008-09-09 15:57:32 +08001470static void via_gpio_control(struct hda_codec *codec)
1471{
1472 unsigned int gpio_data;
1473 unsigned int vol_counter;
1474 unsigned int vol;
1475 unsigned int master_vol;
1476
1477 struct via_spec *spec = codec->spec;
1478
1479 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1480 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1481
1482 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1483 0xF84, 0) & 0x3F0000) >> 16;
1484
1485 vol = vol_counter & 0x1F;
1486 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1487 AC_VERB_GET_AMP_GAIN_MUTE,
1488 AC_AMP_GET_INPUT);
1489
1490 if (gpio_data == 0x02) {
1491 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001492 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1493 AC_VERB_SET_PIN_WIDGET_CONTROL,
1494 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001495 if (vol_counter & 0x20) {
1496 /* decrease volume */
1497 if (vol > master_vol)
1498 vol = master_vol;
1499 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1500 0, HDA_AMP_VOLMASK,
1501 master_vol-vol);
1502 } else {
1503 /* increase volume */
1504 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1505 HDA_AMP_VOLMASK,
1506 ((master_vol+vol) > 0x2A) ? 0x2A :
1507 (master_vol+vol));
1508 }
1509 } else if (!(gpio_data & 0x02)) {
1510 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001511 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1512 AC_VERB_SET_PIN_WIDGET_CONTROL,
1513 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001514 }
1515}
1516
1517/* unsolicited event for jack sensing */
1518static void via_unsol_event(struct hda_codec *codec,
1519 unsigned int res)
1520{
1521 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001522
Lydia Wanga34df192009-10-10 19:08:01 +08001523 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001524 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001525
1526 res &= ~VIA_JACK_EVENT;
1527
1528 if (res == VIA_HP_EVENT)
1529 via_hp_automute(codec);
1530 else if (res == VIA_GPIO_EVENT)
1531 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001532 else if (res == VIA_LINE_EVENT)
1533 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001534}
1535
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001536#ifdef SND_HDA_NEEDS_RESUME
1537static int via_suspend(struct hda_codec *codec, pm_message_t state)
1538{
1539 struct via_spec *spec = codec->spec;
1540 vt1708_stop_hp_work(spec);
1541 return 0;
1542}
1543#endif
1544
Takashi Iwaicb53c622007-08-10 17:21:45 +02001545#ifdef CONFIG_SND_HDA_POWER_SAVE
1546static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1547{
1548 struct via_spec *spec = codec->spec;
1549 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1550}
1551#endif
1552
Joseph Chanc577b8a2006-11-29 15:29:40 +01001553/*
1554 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001555
1556static int via_init(struct hda_codec *codec);
1557
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001558static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001559 .build_controls = via_build_controls,
1560 .build_pcms = via_build_pcms,
1561 .init = via_init,
1562 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001563 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001564#ifdef SND_HDA_NEEDS_RESUME
1565 .suspend = via_suspend,
1566#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001567#ifdef CONFIG_SND_HDA_POWER_SAVE
1568 .check_power_status = via_check_power_status,
1569#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001570};
1571
Takashi Iwai4a796162011-06-17 17:53:38 +02001572static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001573{
Takashi Iwai4a796162011-06-17 17:53:38 +02001574 struct via_spec *spec = codec->spec;
1575 int i;
1576
1577 for (i = 0; i < spec->multiout.num_dacs; i++) {
1578 if (spec->multiout.dac_nids[i] == dac)
1579 return false;
1580 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001581 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001582 return false;
1583 return true;
1584}
1585
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001586static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001587 hda_nid_t target_dac, struct nid_path *path,
1588 int depth, int wid_type)
1589{
1590 hda_nid_t conn[8];
1591 int i, nums;
1592
1593 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1594 for (i = 0; i < nums; i++) {
1595 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1596 continue;
1597 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001598 path->path[0] = conn[i];
1599 path->idx[0] = i;
1600 path->depth = 1;
Takashi Iwai4a796162011-06-17 17:53:38 +02001601 return true;
1602 }
1603 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001604 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001605 return false;
1606 for (i = 0; i < nums; i++) {
1607 unsigned int type;
1608 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1609 if (type == AC_WID_AUD_OUT ||
1610 (wid_type != -1 && type != wid_type))
1611 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001612 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai4a796162011-06-17 17:53:38 +02001613 path, depth + 1, AC_WID_AUD_SEL)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001614 path->path[path->depth] = conn[i];
1615 path->idx[path->depth] = i;
1616 path->depth++;
Takashi Iwai4a796162011-06-17 17:53:38 +02001617 return true;
1618 }
1619 }
1620 return false;
1621}
1622
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001623static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1624 hda_nid_t target_dac, struct nid_path *path)
1625{
1626 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1627 path->path[path->depth] = nid;
1628 path->depth++;
1629 return true;
1630 }
1631 return false;
1632}
1633
Takashi Iwai4a796162011-06-17 17:53:38 +02001634static int via_auto_fill_dac_nids(struct hda_codec *codec)
1635{
1636 struct via_spec *spec = codec->spec;
1637 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001638 int i;
1639 hda_nid_t nid;
1640
Joseph Chanc577b8a2006-11-29 15:29:40 +01001641 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001642 spec->multiout.num_dacs = cfg->line_outs;
1643 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001644 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001645 if (!nid)
1646 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001647 if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
1648 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001649 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001650 return 0;
1651}
1652
Takashi Iwai4a796162011-06-17 17:53:38 +02001653static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
1654 hda_nid_t pin, hda_nid_t dac, int chs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001655{
Takashi Iwai4a796162011-06-17 17:53:38 +02001656 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001657 char name[32];
Takashi Iwai4a796162011-06-17 17:53:38 +02001658 hda_nid_t nid;
1659 int err;
1660
Takashi Iwai8df2a312011-06-21 11:48:29 +02001661 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001662 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001663 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001664 nid = pin;
1665 else
1666 nid = 0;
1667 if (nid) {
1668 sprintf(name, "%s Playback Volume", pfx);
1669 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001670 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001671 if (err < 0)
1672 return err;
1673 }
1674
Takashi Iwai8df2a312011-06-21 11:48:29 +02001675 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001676 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001677 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001678 nid = pin;
1679 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;
1687 }
1688 return 0;
1689}
1690
Takashi Iwaif4a78282011-06-17 18:46:48 +02001691static void mangle_smart51(struct hda_codec *codec)
1692{
1693 struct via_spec *spec = codec->spec;
1694 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001695 int i, nums = 0;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001696
1697 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001698 if (is_smart51_candidate(codec, cfg->inputs[i].pin))
1699 nums++;
1700 }
1701 if (cfg->line_outs + nums < 3)
1702 return;
1703 for (i = 0; i < cfg->num_inputs; i++) {
1704 if (!is_smart51_candidate(codec, cfg->inputs[i].pin))
Takashi Iwaif4a78282011-06-17 18:46:48 +02001705 continue;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001706 spec->smart51_pins[spec->smart51_nums++] = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001707 cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin;
1708 if (cfg->line_outs == 3)
1709 break;
1710 }
1711}
1712
Takashi Iwai4a796162011-06-17 17:53:38 +02001713/* add playback controls from the parsed DAC table */
1714static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1715{
1716 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001717 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001718 static const char * const chname[4] = {
1719 "Front", "Surround", "C/LFE", "Side"
1720 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001721 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001722 int old_line_outs;
1723
1724 /* check smart51 */
1725 old_line_outs = cfg->line_outs;
1726 if (cfg->line_outs == 1)
1727 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001728
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001729 err = via_auto_fill_dac_nids(codec);
1730 if (err < 0)
1731 return err;
1732
Takashi Iwai4a796162011-06-17 17:53:38 +02001733 for (i = 0; i < cfg->line_outs; i++) {
1734 hda_nid_t pin, dac;
1735 pin = cfg->line_out_pins[i];
1736 dac = spec->multiout.dac_nids[i];
1737 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001738 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001739 if (i == HDA_CLFE) {
Takashi Iwai4a796162011-06-17 17:53:38 +02001740 err = create_ch_ctls(codec, "Center", pin, dac, 1);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001741 if (err < 0)
1742 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02001743 err = create_ch_ctls(codec, "LFE", pin, dac, 2);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001744 if (err < 0)
1745 return err;
1746 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001747 const char *pfx = chname[i];
1748 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1749 cfg->line_outs == 1)
1750 pfx = "Speaker";
1751 err = create_ch_ctls(codec, pfx, pin, dac, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001752 if (err < 0)
1753 return err;
1754 }
1755 }
1756
Takashi Iwai4a796162011-06-17 17:53:38 +02001757 idx = get_connection_index(codec, spec->aa_mix_nid,
1758 spec->multiout.dac_nids[0]);
1759 if (idx >= 0) {
1760 /* add control to mixer */
1761 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1762 "PCM Playback Volume",
1763 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1764 idx, HDA_INPUT));
1765 if (err < 0)
1766 return err;
1767 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1768 "PCM Playback Switch",
1769 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1770 idx, HDA_INPUT));
1771 if (err < 0)
1772 return err;
1773 }
1774
Takashi Iwaif4a78282011-06-17 18:46:48 +02001775 cfg->line_outs = old_line_outs;
1776
Joseph Chanc577b8a2006-11-29 15:29:40 +01001777 return 0;
1778}
1779
Takashi Iwai4a796162011-06-17 17:53:38 +02001780static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001781{
Takashi Iwai4a796162011-06-17 17:53:38 +02001782 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001783 int err;
1784
1785 if (!pin)
1786 return 0;
1787
Takashi Iwai8df2a312011-06-21 11:48:29 +02001788 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001789 spec->hp_dac_nid = spec->hp_path.path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001790
Takashi Iwaiece8d042011-06-19 16:24:21 +02001791 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001792 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001793 !spec->hp_dac_nid)
1794 return 0;
1795
Takashi Iwaiece8d042011-06-19 16:24:21 +02001796 err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001797 if (err < 0)
1798 return err;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001799
Joseph Chanc577b8a2006-11-29 15:29:40 +01001800 return 0;
1801}
1802
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001803static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1804{
1805 struct via_spec *spec = codec->spec;
1806 hda_nid_t pin, dac;
1807
1808 pin = spec->autocfg.speaker_pins[0];
1809 if (!spec->autocfg.speaker_outs || !pin)
1810 return 0;
1811
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001812 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1813 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001814 spec->multiout.extra_out_nid[0] = dac;
1815 return create_ch_ctls(codec, "Speaker", pin, dac, 3);
1816 }
1817 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001818 &spec->speaker_path))
1819 return create_ch_ctls(codec, "Speaker", pin, 0, 3);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001820
1821 return 0;
1822}
1823
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001824/* look for ADCs */
1825static int via_fill_adcs(struct hda_codec *codec)
1826{
1827 struct via_spec *spec = codec->spec;
1828 hda_nid_t nid = codec->start_nid;
1829 int i;
1830
1831 for (i = 0; i < codec->num_nodes; i++, nid++) {
1832 unsigned int wcaps = get_wcaps(codec, nid);
1833 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1834 continue;
1835 if (wcaps & AC_WCAP_DIGITAL)
1836 continue;
1837 if (!(wcaps & AC_WCAP_CONN_LIST))
1838 continue;
1839 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1840 return -ENOMEM;
1841 spec->adc_nids[spec->num_adc_nids++] = nid;
1842 }
1843 return 0;
1844}
1845
1846static int get_mux_nids(struct hda_codec *codec);
1847
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001848static const struct snd_kcontrol_new via_input_src_ctl = {
1849 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1850 /* The multiple "Capture Source" controls confuse alsamixer
1851 * So call somewhat different..
1852 */
1853 /* .name = "Capture Source", */
1854 .name = "Input Source",
1855 .info = via_mux_enum_info,
1856 .get = via_mux_enum_get,
1857 .put = via_mux_enum_put,
1858};
1859
Takashi Iwai13af8e72011-06-20 14:05:46 +02001860static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
1861{
1862 struct hda_amp_list *list;
1863
1864 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
1865 return;
1866 list = spec->loopback_list + spec->num_loopbacks;
1867 list->nid = mix;
1868 list->dir = HDA_INPUT;
1869 list->idx = idx;
1870 spec->num_loopbacks++;
1871 spec->loopback.amplist = spec->loopback_list;
1872}
Takashi Iwai13af8e72011-06-20 14:05:46 +02001873
Joseph Chanc577b8a2006-11-29 15:29:40 +01001874/* create playback/capture controls for input pins */
Takashi Iwai620e2b22011-06-17 17:19:19 +02001875static int via_auto_create_analog_input_ctls(struct hda_codec *codec,
1876 const struct auto_pin_cfg *cfg)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001877{
Takashi Iwai10a20af2010-09-09 16:28:02 +02001878 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001879 struct hda_input_mux *imux = &spec->private_imux[0];
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001880 int i, j, err, idx, idx2, type, type_idx = 0;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001881 hda_nid_t cap_nid;
1882 hda_nid_t pin_idxs[8];
1883 int num_idxs;
1884
1885 err = via_fill_adcs(codec);
1886 if (err < 0)
1887 return err;
1888 err = get_mux_nids(codec);
1889 if (err < 0)
1890 return err;
1891 cap_nid = spec->mux_nids[0];
1892
1893 num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
1894 ARRAY_SIZE(pin_idxs));
1895 if (num_idxs <= 0)
1896 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001897
1898 /* for internal loopback recording select */
Takashi Iwaif3268512010-08-30 11:00:19 +02001899 for (idx = 0; idx < num_idxs; idx++) {
Takashi Iwai620e2b22011-06-17 17:19:19 +02001900 if (pin_idxs[idx] == spec->aa_mix_nid) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001901 snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
Takashi Iwaif3268512010-08-30 11:00:19 +02001902 break;
1903 }
1904 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001905
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001906 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001907 const char *label;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001908 type = cfg->inputs[i].type;
Takashi Iwaif3268512010-08-30 11:00:19 +02001909 for (idx = 0; idx < num_idxs; idx++)
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001910 if (pin_idxs[idx] == cfg->inputs[i].pin)
Takashi Iwaif3268512010-08-30 11:00:19 +02001911 break;
1912 if (idx >= num_idxs)
1913 continue;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001914 if (i > 0 && type == cfg->inputs[i - 1].type)
1915 type_idx++;
1916 else
1917 type_idx = 0;
Takashi Iwai10a20af2010-09-09 16:28:02 +02001918 label = hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwai620e2b22011-06-17 17:19:19 +02001919 idx2 = get_connection_index(codec, spec->aa_mix_nid,
1920 pin_idxs[idx]);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001921 if (idx2 >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08001922 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwai620e2b22011-06-17 17:19:19 +02001923 idx2, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001924 if (err < 0)
1925 return err;
1926 add_loopback_list(spec, spec->aa_mix_nid, idx2);
1927 }
Takashi Iwai10a20af2010-09-09 16:28:02 +02001928 snd_hda_add_imux_item(imux, label, idx, NULL);
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001929
1930 /* remember the label for smart51 control */
1931 for (j = 0; j < spec->smart51_nums; j++) {
1932 if (spec->smart51_pins[j] == cfg->inputs[i].pin) {
1933 spec->smart51_idxs[j] = idx;
1934 spec->smart51_labels[j] = label;
1935 break;
1936 }
1937 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001938 }
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001939
1940 /* create capture mixer elements */
1941 for (i = 0; i < spec->num_adc_nids; i++) {
1942 hda_nid_t adc = spec->adc_nids[i];
1943 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
1944 "Capture Volume", i,
1945 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1946 HDA_INPUT));
1947 if (err < 0)
1948 return err;
1949 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1950 "Capture Switch", i,
1951 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1952 HDA_INPUT));
1953 if (err < 0)
1954 return err;
1955 }
1956
1957 /* input-source control */
1958 for (i = 0; i < spec->num_adc_nids; i++)
1959 if (!spec->mux_nids[i])
1960 break;
1961 if (i) {
1962 struct snd_kcontrol_new *knew;
1963 knew = via_clone_control(spec, &via_input_src_ctl);
1964 if (!knew)
1965 return -ENOMEM;
1966 knew->count = i;
1967 }
1968
1969 /* mic-boosts */
1970 for (i = 0; i < cfg->num_inputs; i++) {
1971 hda_nid_t pin = cfg->inputs[i].pin;
1972 unsigned int caps;
1973 const char *label;
1974 char name[32];
1975
1976 if (cfg->inputs[i].type != AUTO_PIN_MIC)
1977 continue;
1978 caps = query_amp_caps(codec, pin, HDA_INPUT);
1979 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
1980 continue;
1981 label = hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwai30f7c5d2011-06-21 08:37:41 +02001982 snprintf(name, sizeof(name), "%s Boost Volume", label);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001983 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1984 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
1985 if (err < 0)
1986 return err;
1987 }
1988
Joseph Chanc577b8a2006-11-29 15:29:40 +01001989 return 0;
1990}
1991
Harald Welte76d9b0d2008-09-09 15:50:37 +08001992static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
1993{
1994 unsigned int def_conf;
1995 unsigned char seqassoc;
1996
Takashi Iwai2f334f92009-02-20 14:37:42 +01001997 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08001998 seqassoc = (unsigned char) get_defcfg_association(def_conf);
1999 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002000 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2001 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2002 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2003 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002004 }
2005
2006 return;
2007}
2008
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002009static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002010 struct snd_ctl_elem_value *ucontrol)
2011{
2012 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2013 struct via_spec *spec = codec->spec;
2014
2015 if (spec->codec_type != VT1708)
2016 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002017 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002018 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002019 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002020 return 0;
2021}
2022
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002023static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002024 struct snd_ctl_elem_value *ucontrol)
2025{
2026 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2027 struct via_spec *spec = codec->spec;
2028 int change;
2029
2030 if (spec->codec_type != VT1708)
2031 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002032 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002033 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002034 == !spec->vt1708_jack_detect;
2035 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002036 mute_aa_path(codec, 1);
2037 notify_aa_path_ctls(codec);
2038 }
2039 return change;
2040}
2041
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002042static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2043 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2044 .name = "Jack Detect",
2045 .count = 1,
2046 .info = snd_ctl_boolean_mono_info,
2047 .get = vt1708_jack_detect_get,
2048 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002049};
2050
Takashi Iwai12daef62011-06-18 17:45:49 +02002051static void fill_dig_outs(struct hda_codec *codec);
2052static void fill_dig_in(struct hda_codec *codec);
2053
2054static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002055{
2056 struct via_spec *spec = codec->spec;
2057 int err;
2058
2059 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2060 if (err < 0)
2061 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002062 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002063 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002064
Takashi Iwai4a796162011-06-17 17:53:38 +02002065 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002066 if (err < 0)
2067 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002068 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002069 if (err < 0)
2070 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002071 err = via_auto_create_speaker_ctls(codec);
2072 if (err < 0)
2073 return err;
Takashi Iwai620e2b22011-06-17 17:19:19 +02002074 err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002075 if (err < 0)
2076 return err;
2077
2078 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2079
Takashi Iwai12daef62011-06-18 17:45:49 +02002080 fill_dig_outs(codec);
2081 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002082
Takashi Iwai603c4012008-07-30 15:01:44 +02002083 if (spec->kctls.list)
2084 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002085
Takashi Iwai096a8852011-06-20 12:09:02 +02002086 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002087
Harald Welte0aa62ae2008-09-09 15:58:27 +08002088 spec->input_mux = &spec->private_imux[0];
2089
Takashi Iwai8df2a312011-06-21 11:48:29 +02002090 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002091 err = via_hp_build(codec);
2092 if (err < 0)
2093 return err;
2094 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002095
Takashi Iwaif4a78282011-06-17 18:46:48 +02002096 err = via_smart51_build(codec);
2097 if (err < 0)
2098 return err;
2099
Takashi Iwai5d417622011-06-20 11:32:27 +02002100 /* assign slave outs */
2101 if (spec->slave_dig_outs[0])
2102 codec->slave_dig_outs = spec->slave_dig_outs;
2103
Joseph Chanc577b8a2006-11-29 15:29:40 +01002104 return 1;
2105}
2106
Takashi Iwai5d417622011-06-20 11:32:27 +02002107static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002108{
Lydia Wang25eaba22009-10-10 19:08:43 +08002109 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002110 if (spec->multiout.dig_out_nid)
2111 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2112 if (spec->slave_dig_outs[0])
2113 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2114}
Lydia Wang25eaba22009-10-10 19:08:43 +08002115
Takashi Iwai5d417622011-06-20 11:32:27 +02002116static void via_auto_init_dig_in(struct hda_codec *codec)
2117{
2118 struct via_spec *spec = codec->spec;
2119 if (!spec->dig_in_nid)
2120 return;
2121 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2122 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2123}
2124
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002125/* initialize the unsolicited events */
2126static void via_auto_init_unsol_event(struct hda_codec *codec)
2127{
2128 struct via_spec *spec = codec->spec;
2129 struct auto_pin_cfg *cfg = &spec->autocfg;
2130 unsigned int ev;
2131 int i;
2132
2133 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2134 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2135 AC_VERB_SET_UNSOLICITED_ENABLE,
2136 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2137
2138 if (cfg->speaker_pins[0])
2139 ev = VIA_LINE_EVENT;
2140 else
2141 ev = 0;
2142 for (i = 0; i < cfg->line_outs; i++) {
2143 if (cfg->line_out_pins[i] &&
2144 is_jack_detectable(codec, cfg->line_out_pins[i]))
2145 snd_hda_codec_write(codec, cfg->line_out_pins[0], 0,
2146 AC_VERB_SET_UNSOLICITED_ENABLE,
2147 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2148 }
2149
2150 for (i = 0; i < cfg->num_inputs; i++) {
2151 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2152 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2153 AC_VERB_SET_UNSOLICITED_ENABLE,
2154 AC_USRSP_EN | VIA_JACK_EVENT);
2155 }
2156}
2157
Takashi Iwai5d417622011-06-20 11:32:27 +02002158static int via_init(struct hda_codec *codec)
2159{
2160 struct via_spec *spec = codec->spec;
2161 int i;
2162
2163 for (i = 0; i < spec->num_iverbs; i++)
2164 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2165
Joseph Chanc577b8a2006-11-29 15:29:40 +01002166 via_auto_init_multi_out(codec);
2167 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002168 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002169 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002170 via_auto_init_dig_outs(codec);
2171 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002172
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002173 via_auto_init_unsol_event(codec);
2174
2175 via_hp_automute(codec);
2176 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002177
Joseph Chanc577b8a2006-11-29 15:29:40 +01002178 return 0;
2179}
2180
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002181static void vt1708_update_hp_jack_state(struct work_struct *work)
2182{
2183 struct via_spec *spec = container_of(work, struct via_spec,
2184 vt1708_hp_work.work);
2185 if (spec->codec_type != VT1708)
2186 return;
2187 /* if jack state toggled */
2188 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002189 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002190 spec->vt1708_hp_present ^= 1;
2191 via_hp_automute(spec->codec);
2192 }
2193 vt1708_start_hp_work(spec);
2194}
2195
Takashi Iwai337b9d02009-07-07 18:18:59 +02002196static int get_mux_nids(struct hda_codec *codec)
2197{
2198 struct via_spec *spec = codec->spec;
2199 hda_nid_t nid, conn[8];
2200 unsigned int type;
2201 int i, n;
2202
2203 for (i = 0; i < spec->num_adc_nids; i++) {
2204 nid = spec->adc_nids[i];
2205 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002206 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002207 if (type == AC_WID_PIN)
2208 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002209 n = snd_hda_get_connections(codec, nid, conn,
2210 ARRAY_SIZE(conn));
2211 if (n <= 0)
2212 break;
2213 if (n > 1) {
2214 spec->mux_nids[i] = nid;
2215 break;
2216 }
2217 nid = conn[0];
2218 }
2219 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002220 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002221}
2222
Joseph Chanc577b8a2006-11-29 15:29:40 +01002223static int patch_vt1708(struct hda_codec *codec)
2224{
2225 struct via_spec *spec;
2226 int err;
2227
2228 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002229 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002230 if (spec == NULL)
2231 return -ENOMEM;
2232
Takashi Iwai620e2b22011-06-17 17:19:19 +02002233 spec->aa_mix_nid = 0x17;
2234
Takashi Iwai12daef62011-06-18 17:45:49 +02002235 /* Add HP and CD pin config connect bit re-config action */
2236 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2237 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2238
Joseph Chanc577b8a2006-11-29 15:29:40 +01002239 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002240 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002241 if (err < 0) {
2242 via_free(codec);
2243 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002244 }
2245
Takashi Iwai12daef62011-06-18 17:45:49 +02002246 /* add jack detect on/off control */
2247 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2248 return -ENOMEM;
2249
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002250 /* disable 32bit format on VT1708 */
2251 if (codec->vendor_id == 0x11061708)
2252 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002253
Joseph Chanc577b8a2006-11-29 15:29:40 +01002254 codec->patch_ops = via_patch_ops;
2255
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002256 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002257 return 0;
2258}
2259
Joseph Chanc577b8a2006-11-29 15:29:40 +01002260static int patch_vt1709_10ch(struct hda_codec *codec)
2261{
2262 struct via_spec *spec;
2263 int err;
2264
2265 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002266 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002267 if (spec == NULL)
2268 return -ENOMEM;
2269
Takashi Iwai620e2b22011-06-17 17:19:19 +02002270 spec->aa_mix_nid = 0x18;
2271
Takashi Iwai12daef62011-06-18 17:45:49 +02002272 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002273 if (err < 0) {
2274 via_free(codec);
2275 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002276 }
2277
Joseph Chanc577b8a2006-11-29 15:29:40 +01002278 codec->patch_ops = via_patch_ops;
2279
Joseph Chanc577b8a2006-11-29 15:29:40 +01002280 return 0;
2281}
2282/*
2283 * generic initialization of ADC, input mixers and output mixers
2284 */
Joseph Chanc577b8a2006-11-29 15:29:40 +01002285static int patch_vt1709_6ch(struct hda_codec *codec)
2286{
2287 struct via_spec *spec;
2288 int err;
2289
2290 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002291 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002292 if (spec == NULL)
2293 return -ENOMEM;
2294
Takashi Iwai620e2b22011-06-17 17:19:19 +02002295 spec->aa_mix_nid = 0x18;
2296
Takashi Iwai12daef62011-06-18 17:45:49 +02002297 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002298 if (err < 0) {
2299 via_free(codec);
2300 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002301 }
2302
Joseph Chanc577b8a2006-11-29 15:29:40 +01002303 codec->patch_ops = via_patch_ops;
2304
Josepch Chanf7278fd2007-12-13 16:40:40 +01002305 return 0;
2306}
2307
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002308static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2309{
2310 struct via_spec *spec = codec->spec;
2311 int imux_is_smixer;
2312 unsigned int parm;
2313 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002314 if ((spec->codec_type != VT1708B_4CH) &&
2315 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002316 is_8ch = 1;
2317
2318 /* SW0 (17h) = stereo mixer */
2319 imux_is_smixer =
2320 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2321 == ((spec->codec_type == VT1708S) ? 5 : 0));
2322 /* inputs */
2323 /* PW 1/2/5 (1ah/1bh/1eh) */
2324 parm = AC_PWRST_D3;
2325 set_pin_power_state(codec, 0x1a, &parm);
2326 set_pin_power_state(codec, 0x1b, &parm);
2327 set_pin_power_state(codec, 0x1e, &parm);
2328 if (imux_is_smixer)
2329 parm = AC_PWRST_D0;
2330 /* SW0 (17h), AIW 0/1 (13h/14h) */
2331 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2332 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2333 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2334
2335 /* outputs */
2336 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2337 parm = AC_PWRST_D3;
2338 set_pin_power_state(codec, 0x19, &parm);
2339 if (spec->smart51_enabled)
2340 set_pin_power_state(codec, 0x1b, &parm);
2341 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2342 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2343
2344 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2345 if (is_8ch) {
2346 parm = AC_PWRST_D3;
2347 set_pin_power_state(codec, 0x22, &parm);
2348 if (spec->smart51_enabled)
2349 set_pin_power_state(codec, 0x1a, &parm);
2350 snd_hda_codec_write(codec, 0x26, 0,
2351 AC_VERB_SET_POWER_STATE, parm);
2352 snd_hda_codec_write(codec, 0x24, 0,
2353 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002354 } else if (codec->vendor_id == 0x11064397) {
2355 /* PW7(23h), SW2(27h), AOW2(25h) */
2356 parm = AC_PWRST_D3;
2357 set_pin_power_state(codec, 0x23, &parm);
2358 if (spec->smart51_enabled)
2359 set_pin_power_state(codec, 0x1a, &parm);
2360 snd_hda_codec_write(codec, 0x27, 0,
2361 AC_VERB_SET_POWER_STATE, parm);
2362 snd_hda_codec_write(codec, 0x25, 0,
2363 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002364 }
2365
2366 /* PW 3/4/7 (1ch/1dh/23h) */
2367 parm = AC_PWRST_D3;
2368 /* force to D0 for internal Speaker */
2369 set_pin_power_state(codec, 0x1c, &parm);
2370 set_pin_power_state(codec, 0x1d, &parm);
2371 if (is_8ch)
2372 set_pin_power_state(codec, 0x23, &parm);
2373
2374 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2375 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2376 imux_is_smixer ? AC_PWRST_D0 : parm);
2377 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2378 if (is_8ch) {
2379 snd_hda_codec_write(codec, 0x25, 0,
2380 AC_VERB_SET_POWER_STATE, parm);
2381 snd_hda_codec_write(codec, 0x27, 0,
2382 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002383 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2384 snd_hda_codec_write(codec, 0x25, 0,
2385 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002386}
2387
Lydia Wang518bf3b2009-10-10 19:07:29 +08002388static int patch_vt1708S(struct hda_codec *codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002389static int patch_vt1708B_8ch(struct hda_codec *codec)
2390{
2391 struct via_spec *spec;
2392 int err;
2393
Lydia Wang518bf3b2009-10-10 19:07:29 +08002394 if (get_codec_type(codec) == VT1708BCE)
2395 return patch_vt1708S(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002396 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002397 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002398 if (spec == NULL)
2399 return -ENOMEM;
2400
Takashi Iwai620e2b22011-06-17 17:19:19 +02002401 spec->aa_mix_nid = 0x16;
2402
Josepch Chanf7278fd2007-12-13 16:40:40 +01002403 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002404 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002405 if (err < 0) {
2406 via_free(codec);
2407 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002408 }
2409
Josepch Chanf7278fd2007-12-13 16:40:40 +01002410 codec->patch_ops = via_patch_ops;
2411
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002412 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2413
Josepch Chanf7278fd2007-12-13 16:40:40 +01002414 return 0;
2415}
2416
2417static int patch_vt1708B_4ch(struct hda_codec *codec)
2418{
2419 struct via_spec *spec;
2420 int err;
2421
2422 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002423 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002424 if (spec == NULL)
2425 return -ENOMEM;
2426
Josepch Chanf7278fd2007-12-13 16:40:40 +01002427 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002428 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002429 if (err < 0) {
2430 via_free(codec);
2431 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002432 }
2433
Josepch Chanf7278fd2007-12-13 16:40:40 +01002434 codec->patch_ops = via_patch_ops;
2435
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002436 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2437
Joseph Chanc577b8a2006-11-29 15:29:40 +01002438 return 0;
2439}
2440
Harald Welted949cac2008-09-09 15:56:01 +08002441/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002442static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002443 /* Enable Mic Boost Volume backdoor */
2444 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002445 /* don't bybass mixer */
2446 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002447 { }
2448};
2449
Takashi Iwai9da29272009-05-07 16:31:14 +02002450/* fill out digital output widgets; one for master and one for slave outputs */
2451static void fill_dig_outs(struct hda_codec *codec)
2452{
2453 struct via_spec *spec = codec->spec;
2454 int i;
2455
2456 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2457 hda_nid_t nid;
2458 int conn;
2459
2460 nid = spec->autocfg.dig_out_pins[i];
2461 if (!nid)
2462 continue;
2463 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2464 if (conn < 1)
2465 continue;
2466 if (!spec->multiout.dig_out_nid)
2467 spec->multiout.dig_out_nid = nid;
2468 else {
2469 spec->slave_dig_outs[0] = nid;
2470 break; /* at most two dig outs */
2471 }
2472 }
2473}
2474
Takashi Iwai12daef62011-06-18 17:45:49 +02002475static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002476{
2477 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002478 hda_nid_t dig_nid;
2479 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002480
Takashi Iwai12daef62011-06-18 17:45:49 +02002481 if (!spec->autocfg.dig_in_pin)
2482 return;
Harald Welted949cac2008-09-09 15:56:01 +08002483
Takashi Iwai12daef62011-06-18 17:45:49 +02002484 dig_nid = codec->start_nid;
2485 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2486 unsigned int wcaps = get_wcaps(codec, dig_nid);
2487 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2488 continue;
2489 if (!(wcaps & AC_WCAP_DIGITAL))
2490 continue;
2491 if (!(wcaps & AC_WCAP_CONN_LIST))
2492 continue;
2493 err = get_connection_index(codec, dig_nid,
2494 spec->autocfg.dig_in_pin);
2495 if (err >= 0) {
2496 spec->dig_in_nid = dig_nid;
2497 break;
2498 }
2499 }
Harald Welted949cac2008-09-09 15:56:01 +08002500}
2501
Lydia Wang6369bcf2009-10-10 19:08:31 +08002502static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2503 int offset, int num_steps, int step_size)
2504{
2505 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2506 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2507 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2508 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2509 (0 << AC_AMPCAP_MUTE_SHIFT));
2510}
2511
Harald Welted949cac2008-09-09 15:56:01 +08002512static int patch_vt1708S(struct hda_codec *codec)
2513{
2514 struct via_spec *spec;
2515 int err;
2516
2517 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002518 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002519 if (spec == NULL)
2520 return -ENOMEM;
2521
Takashi Iwai620e2b22011-06-17 17:19:19 +02002522 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002523 override_mic_boost(codec, 0x1a, 0, 3, 40);
2524 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002525
Harald Welted949cac2008-09-09 15:56:01 +08002526 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002527 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002528 if (err < 0) {
2529 via_free(codec);
2530 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002531 }
2532
Takashi Iwai096a8852011-06-20 12:09:02 +02002533 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002534
Harald Welted949cac2008-09-09 15:56:01 +08002535 codec->patch_ops = via_patch_ops;
2536
Lydia Wang518bf3b2009-10-10 19:07:29 +08002537 /* correct names for VT1708BCE */
2538 if (get_codec_type(codec) == VT1708BCE) {
2539 kfree(codec->chip_name);
2540 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2541 snprintf(codec->bus->card->mixername,
2542 sizeof(codec->bus->card->mixername),
2543 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002544 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002545 /* correct names for VT1705 */
2546 if (codec->vendor_id == 0x11064397) {
2547 kfree(codec->chip_name);
2548 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2549 snprintf(codec->bus->card->mixername,
2550 sizeof(codec->bus->card->mixername),
2551 "%s %s", codec->vendor_name, codec->chip_name);
2552 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002553 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002554 return 0;
2555}
2556
2557/* Patch for VT1702 */
2558
Takashi Iwai096a8852011-06-20 12:09:02 +02002559static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002560 /* mixer enable */
2561 {0x1, 0xF88, 0x3},
2562 /* GPIO 0~2 */
2563 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002564 { }
2565};
2566
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002567static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2568{
2569 int imux_is_smixer =
2570 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2571 unsigned int parm;
2572 /* inputs */
2573 /* PW 1/2/5 (14h/15h/18h) */
2574 parm = AC_PWRST_D3;
2575 set_pin_power_state(codec, 0x14, &parm);
2576 set_pin_power_state(codec, 0x15, &parm);
2577 set_pin_power_state(codec, 0x18, &parm);
2578 if (imux_is_smixer)
2579 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2580 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2581 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2582 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2583 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2584 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2585
2586 /* outputs */
2587 /* PW 3/4 (16h/17h) */
2588 parm = AC_PWRST_D3;
2589 set_pin_power_state(codec, 0x17, &parm);
2590 set_pin_power_state(codec, 0x16, &parm);
2591 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2592 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2593 imux_is_smixer ? AC_PWRST_D0 : parm);
2594 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2595 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2596}
2597
Harald Welted949cac2008-09-09 15:56:01 +08002598static int patch_vt1702(struct hda_codec *codec)
2599{
2600 struct via_spec *spec;
2601 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002602
2603 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002604 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002605 if (spec == NULL)
2606 return -ENOMEM;
2607
Takashi Iwai620e2b22011-06-17 17:19:19 +02002608 spec->aa_mix_nid = 0x1a;
2609
Takashi Iwai12daef62011-06-18 17:45:49 +02002610 /* limit AA path volume to 0 dB */
2611 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2612 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2613 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2614 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2615 (1 << AC_AMPCAP_MUTE_SHIFT));
2616
Harald Welted949cac2008-09-09 15:56:01 +08002617 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002618 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002619 if (err < 0) {
2620 via_free(codec);
2621 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002622 }
2623
Takashi Iwai096a8852011-06-20 12:09:02 +02002624 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002625
Harald Welted949cac2008-09-09 15:56:01 +08002626 codec->patch_ops = via_patch_ops;
2627
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002628 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002629 return 0;
2630}
2631
Lydia Wangeb7188c2009-10-10 19:08:34 +08002632/* Patch for VT1718S */
2633
Takashi Iwai096a8852011-06-20 12:09:02 +02002634static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002635 /* Enable MW0 adjust Gain 5 */
2636 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002637 /* Enable Boost Volume backdoor */
2638 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002639
Lydia Wangeb7188c2009-10-10 19:08:34 +08002640 { }
2641};
2642
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002643static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2644{
2645 struct via_spec *spec = codec->spec;
2646 int imux_is_smixer;
2647 unsigned int parm;
2648 /* MUX6 (1eh) = stereo mixer */
2649 imux_is_smixer =
2650 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2651 /* inputs */
2652 /* PW 5/6/7 (29h/2ah/2bh) */
2653 parm = AC_PWRST_D3;
2654 set_pin_power_state(codec, 0x29, &parm);
2655 set_pin_power_state(codec, 0x2a, &parm);
2656 set_pin_power_state(codec, 0x2b, &parm);
2657 if (imux_is_smixer)
2658 parm = AC_PWRST_D0;
2659 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2660 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2661 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2662 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2663 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2664
2665 /* outputs */
2666 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2667 parm = AC_PWRST_D3;
2668 set_pin_power_state(codec, 0x27, &parm);
2669 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2670 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2671
2672 /* PW2 (26h), AOW2 (ah) */
2673 parm = AC_PWRST_D3;
2674 set_pin_power_state(codec, 0x26, &parm);
2675 if (spec->smart51_enabled)
2676 set_pin_power_state(codec, 0x2b, &parm);
2677 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2678
2679 /* PW0 (24h), AOW0 (8h) */
2680 parm = AC_PWRST_D3;
2681 set_pin_power_state(codec, 0x24, &parm);
2682 if (!spec->hp_independent_mode) /* check for redirected HP */
2683 set_pin_power_state(codec, 0x28, &parm);
2684 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2685 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2686 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2687 imux_is_smixer ? AC_PWRST_D0 : parm);
2688
2689 /* PW1 (25h), AOW1 (9h) */
2690 parm = AC_PWRST_D3;
2691 set_pin_power_state(codec, 0x25, &parm);
2692 if (spec->smart51_enabled)
2693 set_pin_power_state(codec, 0x2a, &parm);
2694 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2695
2696 if (spec->hp_independent_mode) {
2697 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2698 parm = AC_PWRST_D3;
2699 set_pin_power_state(codec, 0x28, &parm);
2700 snd_hda_codec_write(codec, 0x1b, 0,
2701 AC_VERB_SET_POWER_STATE, parm);
2702 snd_hda_codec_write(codec, 0x34, 0,
2703 AC_VERB_SET_POWER_STATE, parm);
2704 snd_hda_codec_write(codec, 0xc, 0,
2705 AC_VERB_SET_POWER_STATE, parm);
2706 }
2707}
2708
Lydia Wangeb7188c2009-10-10 19:08:34 +08002709static int patch_vt1718S(struct hda_codec *codec)
2710{
2711 struct via_spec *spec;
2712 int err;
2713
2714 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002715 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002716 if (spec == NULL)
2717 return -ENOMEM;
2718
Takashi Iwai620e2b22011-06-17 17:19:19 +02002719 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002720 override_mic_boost(codec, 0x2b, 0, 3, 40);
2721 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002722
Lydia Wangeb7188c2009-10-10 19:08:34 +08002723 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002724 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002725 if (err < 0) {
2726 via_free(codec);
2727 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002728 }
2729
Takashi Iwai096a8852011-06-20 12:09:02 +02002730 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002731
Lydia Wangeb7188c2009-10-10 19:08:34 +08002732 codec->patch_ops = via_patch_ops;
2733
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002734 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2735
Lydia Wangeb7188c2009-10-10 19:08:34 +08002736 return 0;
2737}
Lydia Wangf3db4232009-10-10 19:08:41 +08002738
2739/* Patch for VT1716S */
2740
2741static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2742 struct snd_ctl_elem_info *uinfo)
2743{
2744 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2745 uinfo->count = 1;
2746 uinfo->value.integer.min = 0;
2747 uinfo->value.integer.max = 1;
2748 return 0;
2749}
2750
2751static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2752 struct snd_ctl_elem_value *ucontrol)
2753{
2754 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2755 int index = 0;
2756
2757 index = snd_hda_codec_read(codec, 0x26, 0,
2758 AC_VERB_GET_CONNECT_SEL, 0);
2759 if (index != -1)
2760 *ucontrol->value.integer.value = index;
2761
2762 return 0;
2763}
2764
2765static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2766 struct snd_ctl_elem_value *ucontrol)
2767{
2768 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2769 struct via_spec *spec = codec->spec;
2770 int index = *ucontrol->value.integer.value;
2771
2772 snd_hda_codec_write(codec, 0x26, 0,
2773 AC_VERB_SET_CONNECT_SEL, index);
2774 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002775 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002776 return 1;
2777}
2778
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002779static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002780 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2781 {
2782 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2783 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002784 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08002785 .count = 1,
2786 .info = vt1716s_dmic_info,
2787 .get = vt1716s_dmic_get,
2788 .put = vt1716s_dmic_put,
2789 },
2790 {} /* end */
2791};
2792
2793
2794/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002795static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002796 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
2797 { } /* end */
2798};
2799
Takashi Iwai096a8852011-06-20 12:09:02 +02002800static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002801 /* Enable Boost Volume backdoor */
2802 {0x1, 0xf8a, 0x80},
2803 /* don't bybass mixer */
2804 {0x1, 0xf88, 0xc0},
2805 /* Enable mono output */
2806 {0x1, 0xf90, 0x08},
2807 { }
2808};
2809
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002810static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
2811{
2812 struct via_spec *spec = codec->spec;
2813 int imux_is_smixer;
2814 unsigned int parm;
2815 unsigned int mono_out, present;
2816 /* SW0 (17h) = stereo mixer */
2817 imux_is_smixer =
2818 (snd_hda_codec_read(codec, 0x17, 0,
2819 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
2820 /* inputs */
2821 /* PW 1/2/5 (1ah/1bh/1eh) */
2822 parm = AC_PWRST_D3;
2823 set_pin_power_state(codec, 0x1a, &parm);
2824 set_pin_power_state(codec, 0x1b, &parm);
2825 set_pin_power_state(codec, 0x1e, &parm);
2826 if (imux_is_smixer)
2827 parm = AC_PWRST_D0;
2828 /* SW0 (17h), AIW0(13h) */
2829 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2830 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2831
2832 parm = AC_PWRST_D3;
2833 set_pin_power_state(codec, 0x1e, &parm);
2834 /* PW11 (22h) */
2835 if (spec->dmic_enabled)
2836 set_pin_power_state(codec, 0x22, &parm);
2837 else
2838 snd_hda_codec_write(codec, 0x22, 0,
2839 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
2840
2841 /* SW2(26h), AIW1(14h) */
2842 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
2843 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2844
2845 /* outputs */
2846 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2847 parm = AC_PWRST_D3;
2848 set_pin_power_state(codec, 0x19, &parm);
2849 /* Smart 5.1 PW2(1bh) */
2850 if (spec->smart51_enabled)
2851 set_pin_power_state(codec, 0x1b, &parm);
2852 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2853 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2854
2855 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
2856 parm = AC_PWRST_D3;
2857 set_pin_power_state(codec, 0x23, &parm);
2858 /* Smart 5.1 PW1(1ah) */
2859 if (spec->smart51_enabled)
2860 set_pin_power_state(codec, 0x1a, &parm);
2861 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
2862
2863 /* Smart 5.1 PW5(1eh) */
2864 if (spec->smart51_enabled)
2865 set_pin_power_state(codec, 0x1e, &parm);
2866 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
2867
2868 /* Mono out */
2869 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
2870 present = snd_hda_jack_detect(codec, 0x1c);
2871
2872 if (present)
2873 mono_out = 0;
2874 else {
2875 present = snd_hda_jack_detect(codec, 0x1d);
2876 if (!spec->hp_independent_mode && present)
2877 mono_out = 0;
2878 else
2879 mono_out = 1;
2880 }
2881 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
2882 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
2883 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
2884 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
2885
2886 /* PW 3/4 (1ch/1dh) */
2887 parm = AC_PWRST_D3;
2888 set_pin_power_state(codec, 0x1c, &parm);
2889 set_pin_power_state(codec, 0x1d, &parm);
2890 /* HP Independent Mode, power on AOW3 */
2891 if (spec->hp_independent_mode)
2892 snd_hda_codec_write(codec, 0x25, 0,
2893 AC_VERB_SET_POWER_STATE, parm);
2894
2895 /* force to D0 for internal Speaker */
2896 /* MW0 (16h), AOW0 (10h) */
2897 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2898 imux_is_smixer ? AC_PWRST_D0 : parm);
2899 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
2900 mono_out ? AC_PWRST_D0 : parm);
2901}
2902
Lydia Wangf3db4232009-10-10 19:08:41 +08002903static int patch_vt1716S(struct hda_codec *codec)
2904{
2905 struct via_spec *spec;
2906 int err;
2907
2908 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002909 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002910 if (spec == NULL)
2911 return -ENOMEM;
2912
Takashi Iwai620e2b22011-06-17 17:19:19 +02002913 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002914 override_mic_boost(codec, 0x1a, 0, 3, 40);
2915 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002916
Lydia Wangf3db4232009-10-10 19:08:41 +08002917 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002918 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002919 if (err < 0) {
2920 via_free(codec);
2921 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08002922 }
2923
Takashi Iwai096a8852011-06-20 12:09:02 +02002924 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08002925
Lydia Wangf3db4232009-10-10 19:08:41 +08002926 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
2927 spec->num_mixers++;
2928
2929 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
2930
2931 codec->patch_ops = via_patch_ops;
2932
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002933 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08002934 return 0;
2935}
Lydia Wang25eaba22009-10-10 19:08:43 +08002936
2937/* for vt2002P */
2938
Takashi Iwai096a8852011-06-20 12:09:02 +02002939static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08002940 /* Class-D speaker related verbs */
2941 {0x1, 0xfe0, 0x4},
2942 {0x1, 0xfe9, 0x80},
2943 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08002944 /* Enable Boost Volume backdoor */
2945 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08002946 /* Enable AOW0 to MW9 */
2947 {0x1, 0xfb8, 0x88},
2948 { }
2949};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002950
Takashi Iwai096a8852011-06-20 12:09:02 +02002951static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08002952 /* Enable Boost Volume backdoor */
2953 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08002954 /* Enable AOW0 to MW9 */
2955 {0x1, 0xfb8, 0x88},
2956 { }
2957};
Lydia Wang25eaba22009-10-10 19:08:43 +08002958
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002959static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
2960{
2961 struct via_spec *spec = codec->spec;
2962 int imux_is_smixer;
2963 unsigned int parm;
2964 unsigned int present;
2965 /* MUX9 (1eh) = stereo mixer */
2966 imux_is_smixer =
2967 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2968 /* inputs */
2969 /* PW 5/6/7 (29h/2ah/2bh) */
2970 parm = AC_PWRST_D3;
2971 set_pin_power_state(codec, 0x29, &parm);
2972 set_pin_power_state(codec, 0x2a, &parm);
2973 set_pin_power_state(codec, 0x2b, &parm);
2974 parm = AC_PWRST_D0;
2975 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
2976 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2977 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2978 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2979 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2980
2981 /* outputs */
2982 /* AOW0 (8h)*/
2983 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2984
Lydia Wang118909562011-03-23 17:57:34 +08002985 if (spec->codec_type == VT1802) {
2986 /* PW4 (28h), MW4 (18h), MUX4(38h) */
2987 parm = AC_PWRST_D3;
2988 set_pin_power_state(codec, 0x28, &parm);
2989 snd_hda_codec_write(codec, 0x18, 0,
2990 AC_VERB_SET_POWER_STATE, parm);
2991 snd_hda_codec_write(codec, 0x38, 0,
2992 AC_VERB_SET_POWER_STATE, parm);
2993 } else {
2994 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
2995 parm = AC_PWRST_D3;
2996 set_pin_power_state(codec, 0x26, &parm);
2997 snd_hda_codec_write(codec, 0x1c, 0,
2998 AC_VERB_SET_POWER_STATE, parm);
2999 snd_hda_codec_write(codec, 0x37, 0,
3000 AC_VERB_SET_POWER_STATE, parm);
3001 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003002
Lydia Wang118909562011-03-23 17:57:34 +08003003 if (spec->codec_type == VT1802) {
3004 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3005 parm = AC_PWRST_D3;
3006 set_pin_power_state(codec, 0x25, &parm);
3007 snd_hda_codec_write(codec, 0x15, 0,
3008 AC_VERB_SET_POWER_STATE, parm);
3009 snd_hda_codec_write(codec, 0x35, 0,
3010 AC_VERB_SET_POWER_STATE, parm);
3011 } else {
3012 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3013 parm = AC_PWRST_D3;
3014 set_pin_power_state(codec, 0x25, &parm);
3015 snd_hda_codec_write(codec, 0x19, 0,
3016 AC_VERB_SET_POWER_STATE, parm);
3017 snd_hda_codec_write(codec, 0x35, 0,
3018 AC_VERB_SET_POWER_STATE, parm);
3019 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003020
3021 if (spec->hp_independent_mode)
3022 snd_hda_codec_write(codec, 0x9, 0,
3023 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3024
3025 /* Class-D */
3026 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3027 present = snd_hda_jack_detect(codec, 0x25);
3028
3029 parm = AC_PWRST_D3;
3030 set_pin_power_state(codec, 0x24, &parm);
3031 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003032 if (spec->codec_type == VT1802)
3033 snd_hda_codec_write(codec, 0x14, 0,
3034 AC_VERB_SET_POWER_STATE, parm);
3035 else
3036 snd_hda_codec_write(codec, 0x18, 0,
3037 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003038 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3039
3040 /* Mono Out */
3041 present = snd_hda_jack_detect(codec, 0x26);
3042
3043 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003044 if (spec->codec_type == VT1802) {
3045 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3046 snd_hda_codec_write(codec, 0x33, 0,
3047 AC_VERB_SET_POWER_STATE, parm);
3048 snd_hda_codec_write(codec, 0x1c, 0,
3049 AC_VERB_SET_POWER_STATE, parm);
3050 snd_hda_codec_write(codec, 0x3c, 0,
3051 AC_VERB_SET_POWER_STATE, parm);
3052 } else {
3053 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3054 snd_hda_codec_write(codec, 0x31, 0,
3055 AC_VERB_SET_POWER_STATE, parm);
3056 snd_hda_codec_write(codec, 0x17, 0,
3057 AC_VERB_SET_POWER_STATE, parm);
3058 snd_hda_codec_write(codec, 0x3b, 0,
3059 AC_VERB_SET_POWER_STATE, parm);
3060 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003061 /* MW9 (21h) */
3062 if (imux_is_smixer || !is_aa_path_mute(codec))
3063 snd_hda_codec_write(codec, 0x21, 0,
3064 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3065 else
3066 snd_hda_codec_write(codec, 0x21, 0,
3067 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3068}
Lydia Wang25eaba22009-10-10 19:08:43 +08003069
3070/* patch for vt2002P */
3071static int patch_vt2002P(struct hda_codec *codec)
3072{
3073 struct via_spec *spec;
3074 int err;
3075
3076 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003077 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003078 if (spec == NULL)
3079 return -ENOMEM;
3080
Takashi Iwai620e2b22011-06-17 17:19:19 +02003081 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003082 override_mic_boost(codec, 0x2b, 0, 3, 40);
3083 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003084
Lydia Wang25eaba22009-10-10 19:08:43 +08003085 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003086 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003087 if (err < 0) {
3088 via_free(codec);
3089 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003090 }
3091
Lydia Wang118909562011-03-23 17:57:34 +08003092 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003093 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003094 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003095 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003096
Lydia Wang25eaba22009-10-10 19:08:43 +08003097 codec->patch_ops = via_patch_ops;
3098
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003099 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003100 return 0;
3101}
Lydia Wangab6734e2009-10-10 19:08:46 +08003102
3103/* for vt1812 */
3104
Takashi Iwai096a8852011-06-20 12:09:02 +02003105static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003106 /* Enable Boost Volume backdoor */
3107 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003108 /* Enable AOW0 to MW9 */
3109 {0x1, 0xfb8, 0xa8},
3110 { }
3111};
3112
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003113static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3114{
3115 struct via_spec *spec = codec->spec;
3116 int imux_is_smixer =
3117 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3118 unsigned int parm;
3119 unsigned int present;
3120 /* MUX10 (1eh) = stereo mixer */
3121 imux_is_smixer =
3122 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3123 /* inputs */
3124 /* PW 5/6/7 (29h/2ah/2bh) */
3125 parm = AC_PWRST_D3;
3126 set_pin_power_state(codec, 0x29, &parm);
3127 set_pin_power_state(codec, 0x2a, &parm);
3128 set_pin_power_state(codec, 0x2b, &parm);
3129 parm = AC_PWRST_D0;
3130 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3131 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3132 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3133 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3134 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3135
3136 /* outputs */
3137 /* AOW0 (8h)*/
3138 snd_hda_codec_write(codec, 0x8, 0,
3139 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3140
3141 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3142 parm = AC_PWRST_D3;
3143 set_pin_power_state(codec, 0x28, &parm);
3144 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3145 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3146
3147 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3148 parm = AC_PWRST_D3;
3149 set_pin_power_state(codec, 0x25, &parm);
3150 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3151 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3152 if (spec->hp_independent_mode)
3153 snd_hda_codec_write(codec, 0x9, 0,
3154 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3155
3156 /* Internal Speaker */
3157 /* PW0 (24h), MW0(14h), MUX0(34h) */
3158 present = snd_hda_jack_detect(codec, 0x25);
3159
3160 parm = AC_PWRST_D3;
3161 set_pin_power_state(codec, 0x24, &parm);
3162 if (present) {
3163 snd_hda_codec_write(codec, 0x14, 0,
3164 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3165 snd_hda_codec_write(codec, 0x34, 0,
3166 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3167 } else {
3168 snd_hda_codec_write(codec, 0x14, 0,
3169 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3170 snd_hda_codec_write(codec, 0x34, 0,
3171 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3172 }
3173
3174
3175 /* Mono Out */
3176 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3177 present = snd_hda_jack_detect(codec, 0x28);
3178
3179 parm = AC_PWRST_D3;
3180 set_pin_power_state(codec, 0x31, &parm);
3181 if (present) {
3182 snd_hda_codec_write(codec, 0x1c, 0,
3183 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3184 snd_hda_codec_write(codec, 0x3c, 0,
3185 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3186 snd_hda_codec_write(codec, 0x3e, 0,
3187 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3188 } else {
3189 snd_hda_codec_write(codec, 0x1c, 0,
3190 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3191 snd_hda_codec_write(codec, 0x3c, 0,
3192 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3193 snd_hda_codec_write(codec, 0x3e, 0,
3194 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3195 }
3196
3197 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3198 parm = AC_PWRST_D3;
3199 set_pin_power_state(codec, 0x33, &parm);
3200 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3201 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3202
3203}
Lydia Wangab6734e2009-10-10 19:08:46 +08003204
3205/* patch for vt1812 */
3206static int patch_vt1812(struct hda_codec *codec)
3207{
3208 struct via_spec *spec;
3209 int err;
3210
3211 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003212 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003213 if (spec == NULL)
3214 return -ENOMEM;
3215
Takashi Iwai620e2b22011-06-17 17:19:19 +02003216 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003217 override_mic_boost(codec, 0x2b, 0, 3, 40);
3218 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003219
Lydia Wangab6734e2009-10-10 19:08:46 +08003220 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003221 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003222 if (err < 0) {
3223 via_free(codec);
3224 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003225 }
3226
Takashi Iwai096a8852011-06-20 12:09:02 +02003227 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003228
Lydia Wangab6734e2009-10-10 19:08:46 +08003229 codec->patch_ops = via_patch_ops;
3230
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003231 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003232 return 0;
3233}
3234
Joseph Chanc577b8a2006-11-29 15:29:40 +01003235/*
3236 * patch entries
3237 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003238static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003239 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3240 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3241 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3242 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3243 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003244 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003245 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003246 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003247 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003248 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003249 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003250 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003251 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003252 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003253 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003254 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003255 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003256 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003257 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003258 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003259 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003260 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003261 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003262 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003263 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003264 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003265 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003266 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003267 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003268 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003269 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003270 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003271 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003272 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003273 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003274 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003275 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003276 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003277 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003278 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003279 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003280 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003281 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003282 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003283 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003284 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003285 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003286 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003287 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003288 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003289 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003290 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003291 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003292 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003293 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003294 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003295 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003296 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003297 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003298 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003299 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003300 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003301 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003302 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003303 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003304 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003305 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003306 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003307 { .id = 0x11060428, .name = "VT1718S",
3308 .patch = patch_vt1718S},
3309 { .id = 0x11064428, .name = "VT1718S",
3310 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003311 { .id = 0x11060441, .name = "VT2020",
3312 .patch = patch_vt1718S},
3313 { .id = 0x11064441, .name = "VT1828S",
3314 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003315 { .id = 0x11060433, .name = "VT1716S",
3316 .patch = patch_vt1716S},
3317 { .id = 0x1106a721, .name = "VT1716S",
3318 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003319 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3320 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003321 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003322 { .id = 0x11060440, .name = "VT1818S",
3323 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003324 { .id = 0x11060446, .name = "VT1802",
3325 .patch = patch_vt2002P},
3326 { .id = 0x11068446, .name = "VT1802",
3327 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003328 {} /* terminator */
3329};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003330
3331MODULE_ALIAS("snd-hda-codec-id:1106*");
3332
3333static struct hda_codec_preset_list via_list = {
3334 .preset = snd_hda_preset_via,
3335 .owner = THIS_MODULE,
3336};
3337
3338MODULE_LICENSE("GPL");
3339MODULE_DESCRIPTION("VIA HD-audio codec");
3340
3341static int __init patch_via_init(void)
3342{
3343 return snd_hda_add_codec_preset(&via_list);
3344}
3345
3346static void __exit patch_via_exit(void)
3347{
3348 snd_hda_delete_codec_preset(&via_list);
3349}
3350
3351module_init(patch_via_init)
3352module_exit(patch_via_exit)