blob: b67a5768a9dece7ca22b5a9b6725d2146e239fd0 [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 Wangbb3c6bf2009-10-10 19:08:39 +0800217 else if (dev_id == 0x0441 || dev_id == 0x4441)
218 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800219 else if (dev_id == 0x0438 || dev_id == 0x4438)
220 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800221 else if (dev_id == 0x0448)
222 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800223 else if (dev_id == 0x0440)
224 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800225 else if ((dev_id & 0xfff) == 0x446)
226 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800227 else
228 codec_type = UNKNOWN;
229 return codec_type;
230};
231
Lydia Wangec7e7e42011-03-24 12:43:44 +0800232#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800233#define VIA_HP_EVENT 0x01
234#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200235#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800236
Joseph Chanc577b8a2006-11-29 15:29:40 +0100237enum {
238 VIA_CTL_WIDGET_VOL,
239 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800240 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100241};
242
Takashi Iwaiada509e2011-06-20 15:40:19 +0200243static void analog_low_current_mode(struct hda_codec *codec);
244static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800245
246static void vt1708_start_hp_work(struct via_spec *spec)
247{
248 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
249 return;
250 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200251 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800252 if (!delayed_work_pending(&spec->vt1708_hp_work))
253 schedule_delayed_work(&spec->vt1708_hp_work,
254 msecs_to_jiffies(100));
255}
256
257static void vt1708_stop_hp_work(struct via_spec *spec)
258{
259 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
260 return;
261 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
262 && !is_aa_path_mute(spec->codec))
263 return;
264 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200265 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100266 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800267}
Lydia Wangf5271102009-10-10 19:07:35 +0800268
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800269static void set_widgets_power_state(struct hda_codec *codec)
270{
271 struct via_spec *spec = codec->spec;
272 if (spec->set_widgets_power_state)
273 spec->set_widgets_power_state(codec);
274}
Lydia Wang25eaba22009-10-10 19:08:43 +0800275
Lydia Wangf5271102009-10-10 19:07:35 +0800276static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
277 struct snd_ctl_elem_value *ucontrol)
278{
279 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
280 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
281
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800282 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200283 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800284 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
285 if (is_aa_path_mute(codec))
286 vt1708_start_hp_work(codec->spec);
287 else
288 vt1708_stop_hp_work(codec->spec);
289 }
Lydia Wangf5271102009-10-10 19:07:35 +0800290 return change;
291}
292
293/* modify .put = snd_hda_mixer_amp_switch_put */
294#define ANALOG_INPUT_MUTE \
295 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
296 .name = NULL, \
297 .index = 0, \
298 .info = snd_hda_mixer_amp_switch_info, \
299 .get = snd_hda_mixer_amp_switch_get, \
300 .put = analog_input_switch_put, \
301 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
302
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200303static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100304 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
305 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800306 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100307};
308
Lydia Wangab6734e2009-10-10 19:08:46 +0800309
Joseph Chanc577b8a2006-11-29 15:29:40 +0100310/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200311static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
312 const struct snd_kcontrol_new *tmpl,
313 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100314{
315 struct snd_kcontrol_new *knew;
316
Takashi Iwai603c4012008-07-30 15:01:44 +0200317 snd_array_init(&spec->kctls, sizeof(*knew), 32);
318 knew = snd_array_new(&spec->kctls);
319 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200320 return NULL;
321 *knew = *tmpl;
322 if (!name)
323 name = tmpl->name;
324 if (name) {
325 knew->name = kstrdup(name, GFP_KERNEL);
326 if (!knew->name)
327 return NULL;
328 }
329 return knew;
330}
331
332static int __via_add_control(struct via_spec *spec, int type, const char *name,
333 int idx, unsigned long val)
334{
335 struct snd_kcontrol_new *knew;
336
337 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
338 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100339 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200340 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100341 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100342 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100343 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100344 return 0;
345}
346
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200347#define via_add_control(spec, type, name, val) \
348 __via_add_control(spec, type, name, 0, val)
349
Takashi Iwai291c9e32011-06-17 16:15:26 +0200350#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100351
Takashi Iwai603c4012008-07-30 15:01:44 +0200352static void via_free_kctls(struct hda_codec *codec)
353{
354 struct via_spec *spec = codec->spec;
355
356 if (spec->kctls.list) {
357 struct snd_kcontrol_new *kctl = spec->kctls.list;
358 int i;
359 for (i = 0; i < spec->kctls.used; i++)
360 kfree(kctl[i].name);
361 }
362 snd_array_free(&spec->kctls);
363}
364
Joseph Chanc577b8a2006-11-29 15:29:40 +0100365/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800366static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200367 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100368{
369 char name[32];
370 int err;
371
372 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200373 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100374 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
375 if (err < 0)
376 return err;
377 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200378 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100379 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
380 if (err < 0)
381 return err;
382 return 0;
383}
384
Takashi Iwai5d417622011-06-20 11:32:27 +0200385/* return the index of the given widget nid as the source of mux;
386 * return -1 if not found;
387 * if num_conns is non-NULL, set the total number of connections
388 */
389static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux,
390 hda_nid_t nid, int *num_conns)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100391{
Takashi Iwai5d417622011-06-20 11:32:27 +0200392 hda_nid_t conn[HDA_MAX_NUM_INPUTS];
393 int i, nums;
394
395 nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
396 if (num_conns)
397 *num_conns = nums;
398 for (i = 0; i < nums; i++)
399 if (conn[i] == nid)
400 return i;
401 return -1;
402}
403
404#define get_connection_index(codec, mux, nid) \
405 __get_connection_index(codec, mux, nid, NULL)
406
Takashi Iwai8df2a312011-06-21 11:48:29 +0200407static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
408 unsigned int mask)
409{
410 unsigned int caps = get_wcaps(codec, nid);
411 if (dir == HDA_INPUT)
412 caps &= AC_WCAP_IN_AMP;
413 else
414 caps &= AC_WCAP_OUT_AMP;
415 if (!caps)
416 return false;
417 if (query_amp_caps(codec, nid, dir) & mask)
418 return true;
419 return false;
420}
421
422#define have_vol_or_mute(codec, nid, dir) \
423 check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)
424
Takashi Iwai5d417622011-06-20 11:32:27 +0200425/* unmute input amp and select the specificed source */
426static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid,
427 hda_nid_t src, hda_nid_t mix)
428{
429 int idx, num_conns;
430
431 idx = __get_connection_index(codec, nid, src, &num_conns);
432 if (idx < 0)
433 return;
434
435 /* select the route explicitly when multiple connections exist */
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200436 if (num_conns > 1 &&
437 get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
Lydia Wang377ff312009-10-10 19:08:55 +0800438 snd_hda_codec_write(codec, nid, 0,
Takashi Iwai5d417622011-06-20 11:32:27 +0200439 AC_VERB_SET_CONNECT_SEL, idx);
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200440
Takashi Iwai5d417622011-06-20 11:32:27 +0200441 /* unmute if the input amp is present */
Takashi Iwai8df2a312011-06-21 11:48:29 +0200442 if (have_vol_or_mute(codec, nid, HDA_INPUT))
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200443 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
444 AMP_IN_UNMUTE(idx));
445
446 /* unmute the src output */
Takashi Iwai8df2a312011-06-21 11:48:29 +0200447 if (have_vol_or_mute(codec, src, HDA_OUTPUT))
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200448 snd_hda_codec_write(codec, src, 0, AC_VERB_SET_AMP_GAIN_MUTE,
449 AMP_OUT_UNMUTE);
Takashi Iwai5d417622011-06-20 11:32:27 +0200450
451 /* unmute AA-path if present */
Takashi Iwai8df2a312011-06-21 11:48:29 +0200452 if (!mix || mix == src)
Takashi Iwai5d417622011-06-20 11:32:27 +0200453 return;
454 idx = __get_connection_index(codec, nid, mix, NULL);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200455 if (idx >= 0 && have_vol_or_mute(codec, nid, HDA_INPUT))
Takashi Iwai5d417622011-06-20 11:32:27 +0200456 snd_hda_codec_write(codec, nid, 0,
457 AC_VERB_SET_AMP_GAIN_MUTE,
458 AMP_IN_UNMUTE(idx));
459}
460
461/* set the given pin as output */
462static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
463 int pin_type)
464{
465 if (!pin)
466 return;
467 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
468 pin_type);
469 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
470 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200471 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100472}
473
Takashi Iwai5d417622011-06-20 11:32:27 +0200474static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin,
475 int pin_type, struct nid_path *path)
476{
477 struct via_spec *spec = codec->spec;
478 unsigned int caps;
479 hda_nid_t nid;
480 int i;
481
482 if (!pin)
483 return;
484
485 init_output_pin(codec, pin, pin_type);
486 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
487 if (caps & AC_AMPCAP_MUTE) {
488 unsigned int val;
489 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
490 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
491 AMP_OUT_MUTE | val);
492 }
493
494 /* initialize the output path */
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200495 for (i = path->depth - 1; i > 0; i--) {
496 nid = path->path[i - 1];
497 unmute_and_select(codec, path->path[i], nid, spec->aa_mix_nid);
Takashi Iwai5d417622011-06-20 11:32:27 +0200498 }
499}
500
Joseph Chanc577b8a2006-11-29 15:29:40 +0100501
502static void via_auto_init_multi_out(struct hda_codec *codec)
503{
504 struct via_spec *spec = codec->spec;
505 int i;
506
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200507 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwai5d417622011-06-20 11:32:27 +0200508 via_auto_init_output(codec, spec->autocfg.line_out_pins[i],
509 PIN_OUT, &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100510}
511
512static void via_auto_init_hp_out(struct hda_codec *codec)
513{
514 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100515
Takashi Iwai5d417622011-06-20 11:32:27 +0200516 if (spec->hp_dac_nid)
517 via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
518 &spec->hp_path);
519 else
520 via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
521 &spec->hp_dep_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100522}
523
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200524static void via_auto_init_speaker_out(struct hda_codec *codec)
525{
526 struct via_spec *spec = codec->spec;
527
528 if (spec->autocfg.speaker_outs)
529 via_auto_init_output(codec, spec->autocfg.speaker_pins[0],
530 PIN_OUT, &spec->speaker_path);
531}
532
Takashi Iwaif4a78282011-06-17 18:46:48 +0200533static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200534
Joseph Chanc577b8a2006-11-29 15:29:40 +0100535static void via_auto_init_analog_input(struct hda_codec *codec)
536{
537 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200538 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200539 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200540 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200541 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100542
Takashi Iwai096a8852011-06-20 12:09:02 +0200543 /* init ADCs */
544 for (i = 0; i < spec->num_adc_nids; i++) {
545 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
546 AC_VERB_SET_AMP_GAIN_MUTE,
547 AMP_IN_UNMUTE(0));
548 }
549
550 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200551 for (i = 0; i < cfg->num_inputs; i++) {
552 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200553 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200554 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100555 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200556 ctl = PIN_VREF50;
557 else
558 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100559 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200560 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100561 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200562
563 /* init input-src */
564 for (i = 0; i < spec->num_adc_nids; i++) {
565 const struct hda_input_mux *imux = spec->input_mux;
566 if (!imux || !spec->mux_nids[i])
567 continue;
568 snd_hda_codec_write(codec, spec->mux_nids[i], 0,
569 AC_VERB_SET_CONNECT_SEL,
570 imux->items[spec->cur_mux[i]].index);
571 }
572
573 /* init aa-mixer */
574 if (!spec->aa_mix_nid)
575 return;
576 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
577 ARRAY_SIZE(conn));
578 for (i = 0; i < num_conns; i++) {
579 unsigned int caps = get_wcaps(codec, conn[i]);
580 if (get_wcaps_type(caps) == AC_WID_PIN)
581 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
582 AC_VERB_SET_AMP_GAIN_MUTE,
583 AMP_IN_MUTE(i));
584 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100585}
Lydia Wangf5271102009-10-10 19:07:35 +0800586
587static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
588 unsigned int *affected_parm)
589{
590 unsigned parm;
591 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
592 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
593 >> AC_DEFCFG_MISC_SHIFT
594 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800595 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200596 unsigned present = 0;
597
598 no_presence |= spec->no_pin_power_ctl;
599 if (!no_presence)
600 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200601 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800602 || ((no_presence || present)
603 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800604 *affected_parm = AC_PWRST_D0; /* if it's connected */
605 parm = AC_PWRST_D0;
606 } else
607 parm = AC_PWRST_D3;
608
609 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
610}
611
Takashi Iwai24088a52011-06-17 16:59:21 +0200612static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
613 struct snd_ctl_elem_info *uinfo)
614{
615 static const char * const texts[] = {
616 "Disabled", "Enabled"
617 };
618
619 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
620 uinfo->count = 1;
621 uinfo->value.enumerated.items = 2;
622 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
623 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
624 strcpy(uinfo->value.enumerated.name,
625 texts[uinfo->value.enumerated.item]);
626 return 0;
627}
628
629static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
630 struct snd_ctl_elem_value *ucontrol)
631{
632 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
633 struct via_spec *spec = codec->spec;
634 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
635 return 0;
636}
637
638static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
639 struct snd_ctl_elem_value *ucontrol)
640{
641 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
642 struct via_spec *spec = codec->spec;
643 unsigned int val = !ucontrol->value.enumerated.item[0];
644
645 if (val == spec->no_pin_power_ctl)
646 return 0;
647 spec->no_pin_power_ctl = val;
648 set_widgets_power_state(codec);
649 return 1;
650}
651
652static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
653 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
654 .name = "Dynamic Power-Control",
655 .info = via_pin_power_ctl_info,
656 .get = via_pin_power_ctl_get,
657 .put = via_pin_power_ctl_put,
658};
659
660
Joseph Chanc577b8a2006-11-29 15:29:40 +0100661/*
662 * input MUX handling
663 */
664static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
665 struct snd_ctl_elem_info *uinfo)
666{
667 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
668 struct via_spec *spec = codec->spec;
669 return snd_hda_input_mux_info(spec->input_mux, uinfo);
670}
671
672static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
673 struct snd_ctl_elem_value *ucontrol)
674{
675 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
676 struct via_spec *spec = codec->spec;
677 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
678
679 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
680 return 0;
681}
682
683static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
684 struct snd_ctl_elem_value *ucontrol)
685{
686 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
687 struct via_spec *spec = codec->spec;
688 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800689 int ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100690
Takashi Iwai337b9d02009-07-07 18:18:59 +0200691 if (!spec->mux_nids[adc_idx])
692 return -EINVAL;
Lydia Wanga80e6e32009-10-10 19:07:55 +0800693 /* switch to D0 beofre change index */
694 if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
695 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
696 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
697 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800698
699 ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
700 spec->mux_nids[adc_idx],
701 &spec->cur_mux[adc_idx]);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800702 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800703 set_widgets_power_state(codec);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800704
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800705 return ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100706}
707
Harald Welte0aa62ae2008-09-09 15:58:27 +0800708static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
709 struct snd_ctl_elem_info *uinfo)
710{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200711 static const char * const texts[] = { "OFF", "ON" };
712
713 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
714 uinfo->count = 1;
715 uinfo->value.enumerated.items = 2;
716 if (uinfo->value.enumerated.item >= 2)
717 uinfo->value.enumerated.item = 1;
718 strcpy(uinfo->value.enumerated.name,
719 texts[uinfo->value.enumerated.item]);
720 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800721}
722
723static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
724 struct snd_ctl_elem_value *ucontrol)
725{
726 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800727 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800728
Takashi Iwaiece8d042011-06-19 16:24:21 +0200729 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800730 return 0;
731}
732
Harald Welte0aa62ae2008-09-09 15:58:27 +0800733static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
734 struct snd_ctl_elem_value *ucontrol)
735{
736 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
737 struct via_spec *spec = codec->spec;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200738 hda_nid_t nid, src;
739 int i, idx, num_conns;
740 struct nid_path *path;
741
742 spec->hp_independent_mode = !!ucontrol->value.enumerated.item[0];
743 if (spec->hp_independent_mode)
744 path = &spec->hp_path;
745 else
746 path = &spec->hp_dep_path;
747
748 /* re-route the output path */
749 for (i = path->depth - 1; i > 0; i--) {
750 nid = path->path[i];
751 src = path->path[i - 1];
752 idx = __get_connection_index(codec, nid, src, &num_conns);
753 if (idx < 0)
754 continue;
755 if (num_conns > 1 &&
756 get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
757 snd_hda_codec_write(codec, nid, 0,
758 AC_VERB_SET_CONNECT_SEL, idx);
759 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800760
Lydia Wangce0e5a92011-03-22 16:22:37 +0800761 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800762 set_widgets_power_state(codec);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800763 return 0;
764}
765
Takashi Iwaiece8d042011-06-19 16:24:21 +0200766static const struct snd_kcontrol_new via_hp_mixer = {
767 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
768 .name = "Independent HP",
769 .info = via_independent_hp_info,
770 .get = via_independent_hp_get,
771 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800772};
773
Takashi Iwai3d83e572010-04-14 14:36:23 +0200774static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100775{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200776 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100777 struct snd_kcontrol_new *knew;
778 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100779
Takashi Iwaiece8d042011-06-19 16:24:21 +0200780 nid = spec->autocfg.hp_pins[0];
781 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200782 if (knew == NULL)
783 return -ENOMEM;
784
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100785 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100786
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100787 return 0;
788}
789
Lydia Wang1564b282009-10-10 19:07:52 +0800790static void notify_aa_path_ctls(struct hda_codec *codec)
791{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200792 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800793 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800794
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200795 for (i = 0; i < spec->smart51_nums; i++) {
796 struct snd_kcontrol *ctl;
797 struct snd_ctl_elem_id id;
798 memset(&id, 0, sizeof(id));
799 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
800 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800801 ctl = snd_hda_find_mixer_ctl(codec, id.name);
802 if (ctl)
803 snd_ctl_notify(codec->bus->card,
804 SNDRV_CTL_EVENT_MASK_VALUE,
805 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800806 }
807}
808
809static void mute_aa_path(struct hda_codec *codec, int mute)
810{
811 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200812 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800813 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200814
Lydia Wang1564b282009-10-10 19:07:52 +0800815 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200816 for (i = 0; i < spec->smart51_nums; i++) {
817 if (spec->smart51_idxs[i] < 0)
818 continue;
819 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
820 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800821 HDA_AMP_MUTE, val);
822 }
823}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200824
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200825static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
826{
827 struct via_spec *spec = codec->spec;
828 int i;
829
830 for (i = 0; i < spec->smart51_nums; i++)
831 if (spec->smart51_pins[i] == pin)
832 return true;
833 return false;
834}
835
Lydia Wang1564b282009-10-10 19:07:52 +0800836static int via_smart51_info(struct snd_kcontrol *kcontrol,
837 struct snd_ctl_elem_info *uinfo)
838{
839 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
840 uinfo->count = 1;
841 uinfo->value.integer.min = 0;
842 uinfo->value.integer.max = 1;
843 return 0;
844}
845
846static int via_smart51_get(struct snd_kcontrol *kcontrol,
847 struct snd_ctl_elem_value *ucontrol)
848{
849 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
850 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800851 int on = 1;
852 int i;
853
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200854 for (i = 0; i < spec->smart51_nums; i++) {
855 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwaif4a78282011-06-17 18:46:48 +0200856 unsigned int ctl;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200857 ctl = snd_hda_codec_read(codec, nid, 0,
858 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200859 if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
860 on = 0;
Lydia Wang1564b282009-10-10 19:07:52 +0800861 }
862 *ucontrol->value.integer.value = on;
863 return 0;
864}
865
866static int via_smart51_put(struct snd_kcontrol *kcontrol,
867 struct snd_ctl_elem_value *ucontrol)
868{
869 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
870 struct via_spec *spec = codec->spec;
871 int out_in = *ucontrol->value.integer.value
872 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800873 int i;
874
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200875 for (i = 0; i < spec->smart51_nums; i++) {
876 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200877 unsigned int parm;
878
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200879 parm = snd_hda_codec_read(codec, nid, 0,
880 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
881 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
882 parm |= out_in;
883 snd_hda_codec_write(codec, nid, 0,
884 AC_VERB_SET_PIN_WIDGET_CONTROL,
885 parm);
886 if (out_in == AC_PINCTL_OUT_EN) {
887 mute_aa_path(codec, 1);
888 notify_aa_path_ctls(codec);
889 }
Lydia Wang1564b282009-10-10 19:07:52 +0800890 }
891 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800892 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800893 return 1;
894}
895
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200896static const struct snd_kcontrol_new via_smart51_mixer = {
897 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
898 .name = "Smart 5.1",
899 .count = 1,
900 .info = via_smart51_info,
901 .get = via_smart51_get,
902 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800903};
904
Takashi Iwaif4a78282011-06-17 18:46:48 +0200905static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100906{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200907 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100908
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200909 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800910 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200911 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100912 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100913 return 0;
914}
915
Takashi Iwaiada509e2011-06-20 15:40:19 +0200916/* check AA path's mute status */
917static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800918{
Lydia Wangf5271102009-10-10 19:07:35 +0800919 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200920 const struct hda_amp_list *p;
921 int i, ch, v;
922
923 for (i = 0; i < spec->num_loopbacks; i++) {
924 p = &spec->loopback_list[i];
925 for (ch = 0; ch < 2; ch++) {
926 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
927 p->idx);
928 if (!(v & HDA_AMP_MUTE) && v > 0)
929 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800930 }
931 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200932 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800933}
934
935/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200936static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800937{
938 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200939 bool enable;
940 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800941
Takashi Iwaiada509e2011-06-20 15:40:19 +0200942 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800943
944 /* decide low current mode's verb & parameter */
945 switch (spec->codec_type) {
946 case VT1708B_8CH:
947 case VT1708B_4CH:
948 verb = 0xf70;
949 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
950 break;
951 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800952 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800953 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800954 verb = 0xf73;
955 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
956 break;
957 case VT1702:
958 verb = 0xf73;
959 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
960 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800961 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800962 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800963 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800964 verb = 0xf93;
965 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
966 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800967 default:
968 return; /* other codecs are not supported */
969 }
970 /* send verb */
971 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
972}
973
Joseph Chanc577b8a2006-11-29 15:29:40 +0100974/*
975 * generic initialization of ADC, input mixers and output mixers
976 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200977static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800978 /* power down jack detect function */
979 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +0100980 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100981};
982
Takashi Iwaiada509e2011-06-20 15:40:19 +0200983static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200984{
Takashi Iwaiada509e2011-06-20 15:40:19 +0200985 struct via_spec *spec = codec->spec;
986
987 if (active)
988 spec->num_active_streams++;
989 else
990 spec->num_active_streams--;
991 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200992}
993
Takashi Iwaiece8d042011-06-19 16:24:21 +0200994static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100995 struct hda_codec *codec,
996 struct snd_pcm_substream *substream)
997{
998 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200999 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001000
1001 if (!spec->hp_independent_mode)
1002 spec->multiout.hp_nid = spec->hp_dac_nid;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001003 set_stream_active(codec, true);
1004 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1005 hinfo);
1006 if (err < 0) {
1007 spec->multiout.hp_nid = 0;
1008 set_stream_active(codec, false);
1009 return err;
1010 }
1011 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001012}
1013
Takashi Iwaiece8d042011-06-19 16:24:21 +02001014static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001015 struct hda_codec *codec,
1016 struct snd_pcm_substream *substream)
1017{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001018 struct via_spec *spec = codec->spec;
1019
1020 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001021 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001022 return 0;
1023}
1024
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001025static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1026 struct hda_codec *codec,
1027 struct snd_pcm_substream *substream)
1028{
1029 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001030
Takashi Iwaiece8d042011-06-19 16:24:21 +02001031 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001032 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001033 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1034 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001035 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001036 return 0;
1037}
1038
1039static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1040 struct hda_codec *codec,
1041 struct snd_pcm_substream *substream)
1042{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001043 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001044 return 0;
1045}
1046
1047static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1048 struct hda_codec *codec,
1049 unsigned int stream_tag,
1050 unsigned int format,
1051 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001052{
1053 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001054
Takashi Iwaiece8d042011-06-19 16:24:21 +02001055 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1056 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001057 vt1708_start_hp_work(spec);
1058 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001059}
1060
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001061static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1062 struct hda_codec *codec,
1063 unsigned int stream_tag,
1064 unsigned int format,
1065 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001066{
1067 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001068
Takashi Iwaiece8d042011-06-19 16:24:21 +02001069 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1070 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001071 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001072 return 0;
1073}
1074
1075static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1076 struct hda_codec *codec,
1077 struct snd_pcm_substream *substream)
1078{
1079 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001080
Takashi Iwaiece8d042011-06-19 16:24:21 +02001081 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001082 vt1708_stop_hp_work(spec);
1083 return 0;
1084}
1085
1086static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1087 struct hda_codec *codec,
1088 struct snd_pcm_substream *substream)
1089{
1090 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001091
Takashi Iwaiece8d042011-06-19 16:24:21 +02001092 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001093 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001094 return 0;
1095}
1096
Joseph Chanc577b8a2006-11-29 15:29:40 +01001097/*
1098 * Digital out
1099 */
1100static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1101 struct hda_codec *codec,
1102 struct snd_pcm_substream *substream)
1103{
1104 struct via_spec *spec = codec->spec;
1105 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1106}
1107
1108static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1109 struct hda_codec *codec,
1110 struct snd_pcm_substream *substream)
1111{
1112 struct via_spec *spec = codec->spec;
1113 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1114}
1115
Harald Welte5691ec72008-09-15 22:42:26 +08001116static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001117 struct hda_codec *codec,
1118 unsigned int stream_tag,
1119 unsigned int format,
1120 struct snd_pcm_substream *substream)
1121{
1122 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001123 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1124 stream_tag, format, substream);
1125}
Harald Welte5691ec72008-09-15 22:42:26 +08001126
Takashi Iwai9da29272009-05-07 16:31:14 +02001127static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1128 struct hda_codec *codec,
1129 struct snd_pcm_substream *substream)
1130{
1131 struct via_spec *spec = codec->spec;
1132 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001133 return 0;
1134}
1135
Joseph Chanc577b8a2006-11-29 15:29:40 +01001136/*
1137 * Analog capture
1138 */
1139static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1140 struct hda_codec *codec,
1141 unsigned int stream_tag,
1142 unsigned int format,
1143 struct snd_pcm_substream *substream)
1144{
1145 struct via_spec *spec = codec->spec;
1146
1147 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1148 stream_tag, 0, format);
1149 return 0;
1150}
1151
1152static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1153 struct hda_codec *codec,
1154 struct snd_pcm_substream *substream)
1155{
1156 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001157 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001158 return 0;
1159}
1160
Takashi Iwai9af74212011-06-18 16:17:45 +02001161static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001162 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001163 .channels_min = 2,
1164 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001165 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001166 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001167 .open = via_playback_multi_pcm_open,
1168 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001169 .prepare = via_playback_multi_pcm_prepare,
1170 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001171 },
1172};
1173
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001174static const struct hda_pcm_stream via_pcm_hp_playback = {
1175 .substreams = 1,
1176 .channels_min = 2,
1177 .channels_max = 2,
1178 /* NID is set in via_build_pcms */
1179 .ops = {
1180 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001181 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001182 .prepare = via_playback_hp_pcm_prepare,
1183 .cleanup = via_playback_hp_pcm_cleanup
1184 },
1185};
1186
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001187static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001188 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001189 .channels_min = 2,
1190 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001191 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001192 /* We got noisy outputs on the right channel on VT1708 when
1193 * 24bit samples are used. Until any workaround is found,
1194 * disable the 24bit format, so far.
1195 */
1196 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1197 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001198 .open = via_playback_multi_pcm_open,
1199 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001200 .prepare = via_playback_multi_pcm_prepare,
1201 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001202 },
1203};
1204
Takashi Iwai9af74212011-06-18 16:17:45 +02001205static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001206 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001207 .channels_min = 2,
1208 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001209 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001210 .ops = {
1211 .prepare = via_capture_pcm_prepare,
1212 .cleanup = via_capture_pcm_cleanup
1213 },
1214};
1215
Takashi Iwai9af74212011-06-18 16:17:45 +02001216static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001217 .substreams = 1,
1218 .channels_min = 2,
1219 .channels_max = 2,
1220 /* NID is set in via_build_pcms */
1221 .ops = {
1222 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001223 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001224 .prepare = via_dig_playback_pcm_prepare,
1225 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001226 },
1227};
1228
Takashi Iwai9af74212011-06-18 16:17:45 +02001229static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001230 .substreams = 1,
1231 .channels_min = 2,
1232 .channels_max = 2,
1233};
1234
Takashi Iwai370bafb2011-06-20 12:47:45 +02001235/*
1236 * slave controls for virtual master
1237 */
1238static const char * const via_slave_vols[] = {
1239 "Front Playback Volume",
1240 "Surround Playback Volume",
1241 "Center Playback Volume",
1242 "LFE Playback Volume",
1243 "Side Playback Volume",
1244 "Headphone Playback Volume",
1245 "Speaker Playback Volume",
1246 NULL,
1247};
1248
1249static const char * const via_slave_sws[] = {
1250 "Front Playback Switch",
1251 "Surround Playback Switch",
1252 "Center Playback Switch",
1253 "LFE Playback Switch",
1254 "Side Playback Switch",
1255 "Headphone Playback Switch",
1256 "Speaker Playback Switch",
1257 NULL,
1258};
1259
Joseph Chanc577b8a2006-11-29 15:29:40 +01001260static int via_build_controls(struct hda_codec *codec)
1261{
1262 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001263 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001264 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001265
Takashi Iwai24088a52011-06-17 16:59:21 +02001266 if (spec->set_widgets_power_state)
1267 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1268 return -ENOMEM;
1269
Joseph Chanc577b8a2006-11-29 15:29:40 +01001270 for (i = 0; i < spec->num_mixers; i++) {
1271 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1272 if (err < 0)
1273 return err;
1274 }
1275
1276 if (spec->multiout.dig_out_nid) {
1277 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001278 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001279 spec->multiout.dig_out_nid);
1280 if (err < 0)
1281 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001282 err = snd_hda_create_spdif_share_sw(codec,
1283 &spec->multiout);
1284 if (err < 0)
1285 return err;
1286 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001287 }
1288 if (spec->dig_in_nid) {
1289 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1290 if (err < 0)
1291 return err;
1292 }
Lydia Wang17314372009-10-10 19:07:37 +08001293
Takashi Iwai370bafb2011-06-20 12:47:45 +02001294 /* if we have no master control, let's create it */
1295 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1296 unsigned int vmaster_tlv[4];
1297 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1298 HDA_OUTPUT, vmaster_tlv);
1299 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1300 vmaster_tlv, via_slave_vols);
1301 if (err < 0)
1302 return err;
1303 }
1304 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1305 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1306 NULL, via_slave_sws);
1307 if (err < 0)
1308 return err;
1309 }
1310
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001311 /* assign Capture Source enums to NID */
1312 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1313 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001314 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001315 if (err < 0)
1316 return err;
1317 }
1318
Lydia Wang17314372009-10-10 19:07:37 +08001319 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001320 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001321 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001322
Takashi Iwai603c4012008-07-30 15:01:44 +02001323 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001324 return 0;
1325}
1326
1327static int via_build_pcms(struct hda_codec *codec)
1328{
1329 struct via_spec *spec = codec->spec;
1330 struct hda_pcm *info = spec->pcm_rec;
1331
1332 codec->num_pcms = 1;
1333 codec->pcm_info = info;
1334
Takashi Iwai82673bc2011-06-17 16:24:21 +02001335 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1336 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001337 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001338
1339 if (!spec->stream_analog_playback)
1340 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001341 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001342 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001343 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1344 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001345 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1346 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001347
1348 if (!spec->stream_analog_capture)
1349 spec->stream_analog_capture = &via_pcm_analog_capture;
1350 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1351 *spec->stream_analog_capture;
1352 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1353 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1354 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001355
1356 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1357 codec->num_pcms++;
1358 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001359 snprintf(spec->stream_name_digital,
1360 sizeof(spec->stream_name_digital),
1361 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001362 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001363 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001364 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001365 if (!spec->stream_digital_playback)
1366 spec->stream_digital_playback =
1367 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001368 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001369 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001370 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1371 spec->multiout.dig_out_nid;
1372 }
1373 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001374 if (!spec->stream_digital_capture)
1375 spec->stream_digital_capture =
1376 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001377 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001378 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001379 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1380 spec->dig_in_nid;
1381 }
1382 }
1383
Takashi Iwaiece8d042011-06-19 16:24:21 +02001384 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001385 codec->num_pcms++;
1386 info++;
1387 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1388 "%s HP", codec->chip_name);
1389 info->name = spec->stream_name_hp;
1390 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1391 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001392 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001393 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001394 return 0;
1395}
1396
1397static void via_free(struct hda_codec *codec)
1398{
1399 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001400
1401 if (!spec)
1402 return;
1403
Takashi Iwai603c4012008-07-30 15:01:44 +02001404 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001405 vt1708_stop_hp_work(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001406 kfree(codec->spec);
1407}
1408
Takashi Iwai64be2852011-06-17 16:51:39 +02001409/* mute/unmute outputs */
1410static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1411 hda_nid_t *pins, bool mute)
1412{
1413 int i;
1414 for (i = 0; i < num_pins; i++)
1415 snd_hda_codec_write(codec, pins[i], 0,
1416 AC_VERB_SET_PIN_WIDGET_CONTROL,
1417 mute ? 0 : PIN_OUT);
1418}
1419
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001420/* mute internal speaker if line-out is plugged */
1421static void via_line_automute(struct hda_codec *codec, int present)
1422{
1423 struct via_spec *spec = codec->spec;
1424
1425 if (!spec->autocfg.speaker_outs)
1426 return;
1427 if (!present)
1428 present = snd_hda_jack_detect(codec,
1429 spec->autocfg.line_out_pins[0]);
1430 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1431 spec->autocfg.speaker_pins,
1432 present);
1433}
1434
Harald Welte69e52a82008-09-09 15:57:32 +08001435/* mute internal speaker if HP is plugged */
1436static void via_hp_automute(struct hda_codec *codec)
1437{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001438 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001439 struct via_spec *spec = codec->spec;
1440
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001441 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
1442 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai64be2852011-06-17 16:51:39 +02001443 toggle_output_mutes(codec, spec->autocfg.line_outs,
1444 spec->autocfg.line_out_pins,
1445 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001446 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001447 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001448}
1449
Harald Welte69e52a82008-09-09 15:57:32 +08001450static void via_gpio_control(struct hda_codec *codec)
1451{
1452 unsigned int gpio_data;
1453 unsigned int vol_counter;
1454 unsigned int vol;
1455 unsigned int master_vol;
1456
1457 struct via_spec *spec = codec->spec;
1458
1459 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1460 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1461
1462 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1463 0xF84, 0) & 0x3F0000) >> 16;
1464
1465 vol = vol_counter & 0x1F;
1466 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1467 AC_VERB_GET_AMP_GAIN_MUTE,
1468 AC_AMP_GET_INPUT);
1469
1470 if (gpio_data == 0x02) {
1471 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001472 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1473 AC_VERB_SET_PIN_WIDGET_CONTROL,
1474 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001475 if (vol_counter & 0x20) {
1476 /* decrease volume */
1477 if (vol > master_vol)
1478 vol = master_vol;
1479 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1480 0, HDA_AMP_VOLMASK,
1481 master_vol-vol);
1482 } else {
1483 /* increase volume */
1484 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1485 HDA_AMP_VOLMASK,
1486 ((master_vol+vol) > 0x2A) ? 0x2A :
1487 (master_vol+vol));
1488 }
1489 } else if (!(gpio_data & 0x02)) {
1490 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001491 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1492 AC_VERB_SET_PIN_WIDGET_CONTROL,
1493 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001494 }
1495}
1496
1497/* unsolicited event for jack sensing */
1498static void via_unsol_event(struct hda_codec *codec,
1499 unsigned int res)
1500{
1501 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001502
Lydia Wanga34df192009-10-10 19:08:01 +08001503 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001504 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001505
1506 res &= ~VIA_JACK_EVENT;
1507
1508 if (res == VIA_HP_EVENT)
1509 via_hp_automute(codec);
1510 else if (res == VIA_GPIO_EVENT)
1511 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001512 else if (res == VIA_LINE_EVENT)
1513 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001514}
1515
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001516#ifdef SND_HDA_NEEDS_RESUME
1517static int via_suspend(struct hda_codec *codec, pm_message_t state)
1518{
1519 struct via_spec *spec = codec->spec;
1520 vt1708_stop_hp_work(spec);
1521 return 0;
1522}
1523#endif
1524
Takashi Iwaicb53c622007-08-10 17:21:45 +02001525#ifdef CONFIG_SND_HDA_POWER_SAVE
1526static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1527{
1528 struct via_spec *spec = codec->spec;
1529 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1530}
1531#endif
1532
Joseph Chanc577b8a2006-11-29 15:29:40 +01001533/*
1534 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001535
1536static int via_init(struct hda_codec *codec);
1537
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001538static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001539 .build_controls = via_build_controls,
1540 .build_pcms = via_build_pcms,
1541 .init = via_init,
1542 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001543 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001544#ifdef SND_HDA_NEEDS_RESUME
1545 .suspend = via_suspend,
1546#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001547#ifdef CONFIG_SND_HDA_POWER_SAVE
1548 .check_power_status = via_check_power_status,
1549#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001550};
1551
Takashi Iwai4a796162011-06-17 17:53:38 +02001552static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001553{
Takashi Iwai4a796162011-06-17 17:53:38 +02001554 struct via_spec *spec = codec->spec;
1555 int i;
1556
1557 for (i = 0; i < spec->multiout.num_dacs; i++) {
1558 if (spec->multiout.dac_nids[i] == dac)
1559 return false;
1560 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001561 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001562 return false;
1563 return true;
1564}
1565
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001566static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001567 hda_nid_t target_dac, struct nid_path *path,
1568 int depth, int wid_type)
1569{
1570 hda_nid_t conn[8];
1571 int i, nums;
1572
1573 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1574 for (i = 0; i < nums; i++) {
1575 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1576 continue;
1577 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001578 path->path[0] = conn[i];
1579 path->idx[0] = i;
1580 path->depth = 1;
Takashi Iwai4a796162011-06-17 17:53:38 +02001581 return true;
1582 }
1583 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001584 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001585 return false;
1586 for (i = 0; i < nums; i++) {
1587 unsigned int type;
1588 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1589 if (type == AC_WID_AUD_OUT ||
1590 (wid_type != -1 && type != wid_type))
1591 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001592 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai4a796162011-06-17 17:53:38 +02001593 path, depth + 1, AC_WID_AUD_SEL)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001594 path->path[path->depth] = conn[i];
1595 path->idx[path->depth] = i;
1596 path->depth++;
Takashi Iwai4a796162011-06-17 17:53:38 +02001597 return true;
1598 }
1599 }
1600 return false;
1601}
1602
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001603static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1604 hda_nid_t target_dac, struct nid_path *path)
1605{
1606 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1607 path->path[path->depth] = nid;
1608 path->depth++;
1609 return true;
1610 }
1611 return false;
1612}
1613
Takashi Iwai4a796162011-06-17 17:53:38 +02001614static int via_auto_fill_dac_nids(struct hda_codec *codec)
1615{
1616 struct via_spec *spec = codec->spec;
1617 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001618 int i;
1619 hda_nid_t nid;
1620
Joseph Chanc577b8a2006-11-29 15:29:40 +01001621 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001622 spec->multiout.num_dacs = cfg->line_outs;
1623 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001624 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001625 if (!nid)
1626 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001627 if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
1628 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001629 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001630 return 0;
1631}
1632
Takashi Iwai4a796162011-06-17 17:53:38 +02001633static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
1634 hda_nid_t pin, hda_nid_t dac, int chs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001635{
Takashi Iwai4a796162011-06-17 17:53:38 +02001636 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001637 char name[32];
Takashi Iwai4a796162011-06-17 17:53:38 +02001638 hda_nid_t nid;
1639 int err;
1640
Takashi Iwai8df2a312011-06-21 11:48:29 +02001641 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001642 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001643 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001644 nid = pin;
1645 else
1646 nid = 0;
1647 if (nid) {
1648 sprintf(name, "%s Playback Volume", pfx);
1649 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001650 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001651 if (err < 0)
1652 return err;
1653 }
1654
Takashi Iwai8df2a312011-06-21 11:48:29 +02001655 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001656 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001657 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001658 nid = pin;
1659 else
1660 nid = 0;
1661 if (nid) {
1662 sprintf(name, "%s Playback Switch", pfx);
1663 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1664 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1665 if (err < 0)
1666 return err;
1667 }
1668 return 0;
1669}
1670
Takashi Iwaif4a78282011-06-17 18:46:48 +02001671static void mangle_smart51(struct hda_codec *codec)
1672{
1673 struct via_spec *spec = codec->spec;
1674 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001675 struct auto_pin_cfg_item *ins = cfg->inputs;
1676 int i, j, nums, attr;
1677 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001678
Takashi Iwai0f98c242011-06-21 12:51:33 +02001679 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1680 nums = 0;
1681 for (i = 0; i < cfg->num_inputs; i++) {
1682 unsigned int def;
1683 if (ins[i].type > AUTO_PIN_LINE_IN)
1684 continue;
1685 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1686 if (snd_hda_get_input_pin_attr(def) != attr)
1687 continue;
1688 for (j = 0; j < nums; j++)
1689 if (ins[pins[j]].type < ins[i].type) {
1690 memmove(pins + j + 1, pins + j,
1691 (nums - j - 1) * sizeof(int));
1692 break;
1693 }
1694 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001695 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001696 }
1697 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001698 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001699 for (i = 0; i < nums; i++) {
1700 hda_nid_t pin = ins[pins[i]].pin;
1701 spec->smart51_pins[spec->smart51_nums++] = pin;
1702 cfg->line_out_pins[cfg->line_outs++] = pin;
1703 if (cfg->line_outs == 3)
1704 break;
1705 }
1706 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001707 }
1708}
1709
Takashi Iwai4a796162011-06-17 17:53:38 +02001710/* add playback controls from the parsed DAC table */
1711static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1712{
1713 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001714 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001715 static const char * const chname[4] = {
1716 "Front", "Surround", "C/LFE", "Side"
1717 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001718 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001719 int old_line_outs;
1720
1721 /* check smart51 */
1722 old_line_outs = cfg->line_outs;
1723 if (cfg->line_outs == 1)
1724 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001725
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001726 err = via_auto_fill_dac_nids(codec);
1727 if (err < 0)
1728 return err;
1729
Takashi Iwai4a796162011-06-17 17:53:38 +02001730 for (i = 0; i < cfg->line_outs; i++) {
1731 hda_nid_t pin, dac;
1732 pin = cfg->line_out_pins[i];
1733 dac = spec->multiout.dac_nids[i];
1734 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001735 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001736 if (i == HDA_CLFE) {
Takashi Iwai4a796162011-06-17 17:53:38 +02001737 err = create_ch_ctls(codec, "Center", pin, dac, 1);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001738 if (err < 0)
1739 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02001740 err = create_ch_ctls(codec, "LFE", pin, dac, 2);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001741 if (err < 0)
1742 return err;
1743 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001744 const char *pfx = chname[i];
1745 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1746 cfg->line_outs == 1)
1747 pfx = "Speaker";
1748 err = create_ch_ctls(codec, pfx, pin, dac, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001749 if (err < 0)
1750 return err;
1751 }
1752 }
1753
Takashi Iwai4a796162011-06-17 17:53:38 +02001754 idx = get_connection_index(codec, spec->aa_mix_nid,
1755 spec->multiout.dac_nids[0]);
1756 if (idx >= 0) {
1757 /* add control to mixer */
1758 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1759 "PCM Playback Volume",
1760 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1761 idx, HDA_INPUT));
1762 if (err < 0)
1763 return err;
1764 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1765 "PCM Playback Switch",
1766 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1767 idx, HDA_INPUT));
1768 if (err < 0)
1769 return err;
1770 }
1771
Takashi Iwaif4a78282011-06-17 18:46:48 +02001772 cfg->line_outs = old_line_outs;
1773
Joseph Chanc577b8a2006-11-29 15:29:40 +01001774 return 0;
1775}
1776
Takashi Iwai4a796162011-06-17 17:53:38 +02001777static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001778{
Takashi Iwai4a796162011-06-17 17:53:38 +02001779 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001780 int err;
1781
1782 if (!pin)
1783 return 0;
1784
Takashi Iwai8df2a312011-06-21 11:48:29 +02001785 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001786 spec->hp_dac_nid = spec->hp_path.path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001787
Takashi Iwaiece8d042011-06-19 16:24:21 +02001788 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001789 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001790 !spec->hp_dac_nid)
1791 return 0;
1792
Takashi Iwaiece8d042011-06-19 16:24:21 +02001793 err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001794 if (err < 0)
1795 return err;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001796
Joseph Chanc577b8a2006-11-29 15:29:40 +01001797 return 0;
1798}
1799
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001800static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1801{
1802 struct via_spec *spec = codec->spec;
1803 hda_nid_t pin, dac;
1804
1805 pin = spec->autocfg.speaker_pins[0];
1806 if (!spec->autocfg.speaker_outs || !pin)
1807 return 0;
1808
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001809 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1810 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001811 spec->multiout.extra_out_nid[0] = dac;
1812 return create_ch_ctls(codec, "Speaker", pin, dac, 3);
1813 }
1814 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001815 &spec->speaker_path))
1816 return create_ch_ctls(codec, "Speaker", pin, 0, 3);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001817
1818 return 0;
1819}
1820
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001821/* look for ADCs */
1822static int via_fill_adcs(struct hda_codec *codec)
1823{
1824 struct via_spec *spec = codec->spec;
1825 hda_nid_t nid = codec->start_nid;
1826 int i;
1827
1828 for (i = 0; i < codec->num_nodes; i++, nid++) {
1829 unsigned int wcaps = get_wcaps(codec, nid);
1830 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1831 continue;
1832 if (wcaps & AC_WCAP_DIGITAL)
1833 continue;
1834 if (!(wcaps & AC_WCAP_CONN_LIST))
1835 continue;
1836 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1837 return -ENOMEM;
1838 spec->adc_nids[spec->num_adc_nids++] = nid;
1839 }
1840 return 0;
1841}
1842
1843static int get_mux_nids(struct hda_codec *codec);
1844
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001845static const struct snd_kcontrol_new via_input_src_ctl = {
1846 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1847 /* The multiple "Capture Source" controls confuse alsamixer
1848 * So call somewhat different..
1849 */
1850 /* .name = "Capture Source", */
1851 .name = "Input Source",
1852 .info = via_mux_enum_info,
1853 .get = via_mux_enum_get,
1854 .put = via_mux_enum_put,
1855};
1856
Takashi Iwai13af8e72011-06-20 14:05:46 +02001857static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
1858{
1859 struct hda_amp_list *list;
1860
1861 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
1862 return;
1863 list = spec->loopback_list + spec->num_loopbacks;
1864 list->nid = mix;
1865 list->dir = HDA_INPUT;
1866 list->idx = idx;
1867 spec->num_loopbacks++;
1868 spec->loopback.amplist = spec->loopback_list;
1869}
Takashi Iwai13af8e72011-06-20 14:05:46 +02001870
Joseph Chanc577b8a2006-11-29 15:29:40 +01001871/* create playback/capture controls for input pins */
Takashi Iwai620e2b22011-06-17 17:19:19 +02001872static int via_auto_create_analog_input_ctls(struct hda_codec *codec,
1873 const struct auto_pin_cfg *cfg)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001874{
Takashi Iwai10a20af2010-09-09 16:28:02 +02001875 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001876 struct hda_input_mux *imux = &spec->private_imux[0];
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001877 int i, j, err, idx, idx2, type, type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02001878 const char *prev_label = NULL;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001879 hda_nid_t cap_nid;
1880 hda_nid_t pin_idxs[8];
1881 int num_idxs;
1882
1883 err = via_fill_adcs(codec);
1884 if (err < 0)
1885 return err;
1886 err = get_mux_nids(codec);
1887 if (err < 0)
1888 return err;
1889 cap_nid = spec->mux_nids[0];
1890
1891 num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
1892 ARRAY_SIZE(pin_idxs));
1893 if (num_idxs <= 0)
1894 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001895
1896 /* for internal loopback recording select */
Takashi Iwaif3268512010-08-30 11:00:19 +02001897 for (idx = 0; idx < num_idxs; idx++) {
Takashi Iwai620e2b22011-06-17 17:19:19 +02001898 if (pin_idxs[idx] == spec->aa_mix_nid) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001899 snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
Takashi Iwaif3268512010-08-30 11:00:19 +02001900 break;
1901 }
1902 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001903
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001904 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001905 const char *label;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001906 type = cfg->inputs[i].type;
Takashi Iwaif3268512010-08-30 11:00:19 +02001907 for (idx = 0; idx < num_idxs; idx++)
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001908 if (pin_idxs[idx] == cfg->inputs[i].pin)
Takashi Iwaif3268512010-08-30 11:00:19 +02001909 break;
1910 if (idx >= num_idxs)
1911 continue;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02001912 label = hda_get_autocfg_input_label(codec, cfg, i);
1913 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001914 type_idx++;
1915 else
1916 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02001917 prev_label = label;
Takashi Iwai620e2b22011-06-17 17:19:19 +02001918 idx2 = get_connection_index(codec, spec->aa_mix_nid,
1919 pin_idxs[idx]);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001920 if (idx2 >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08001921 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwai620e2b22011-06-17 17:19:19 +02001922 idx2, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001923 if (err < 0)
1924 return err;
1925 add_loopback_list(spec, spec->aa_mix_nid, idx2);
1926 }
Takashi Iwai10a20af2010-09-09 16:28:02 +02001927 snd_hda_add_imux_item(imux, label, idx, NULL);
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001928
1929 /* remember the label for smart51 control */
1930 for (j = 0; j < spec->smart51_nums; j++) {
1931 if (spec->smart51_pins[j] == cfg->inputs[i].pin) {
1932 spec->smart51_idxs[j] = idx;
1933 spec->smart51_labels[j] = label;
1934 break;
1935 }
1936 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001937 }
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001938
1939 /* create capture mixer elements */
1940 for (i = 0; i < spec->num_adc_nids; i++) {
1941 hda_nid_t adc = spec->adc_nids[i];
1942 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
1943 "Capture Volume", i,
1944 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1945 HDA_INPUT));
1946 if (err < 0)
1947 return err;
1948 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1949 "Capture Switch", i,
1950 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1951 HDA_INPUT));
1952 if (err < 0)
1953 return err;
1954 }
1955
1956 /* input-source control */
1957 for (i = 0; i < spec->num_adc_nids; i++)
1958 if (!spec->mux_nids[i])
1959 break;
1960 if (i) {
1961 struct snd_kcontrol_new *knew;
1962 knew = via_clone_control(spec, &via_input_src_ctl);
1963 if (!knew)
1964 return -ENOMEM;
1965 knew->count = i;
1966 }
1967
1968 /* mic-boosts */
1969 for (i = 0; i < cfg->num_inputs; i++) {
1970 hda_nid_t pin = cfg->inputs[i].pin;
1971 unsigned int caps;
1972 const char *label;
1973 char name[32];
1974
1975 if (cfg->inputs[i].type != AUTO_PIN_MIC)
1976 continue;
1977 caps = query_amp_caps(codec, pin, HDA_INPUT);
1978 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
1979 continue;
1980 label = hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwai30f7c5d2011-06-21 08:37:41 +02001981 snprintf(name, sizeof(name), "%s Boost Volume", label);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001982 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1983 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
1984 if (err < 0)
1985 return err;
1986 }
1987
Joseph Chanc577b8a2006-11-29 15:29:40 +01001988 return 0;
1989}
1990
Harald Welte76d9b0d2008-09-09 15:50:37 +08001991static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
1992{
1993 unsigned int def_conf;
1994 unsigned char seqassoc;
1995
Takashi Iwai2f334f92009-02-20 14:37:42 +01001996 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08001997 seqassoc = (unsigned char) get_defcfg_association(def_conf);
1998 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08001999 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2000 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2001 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2002 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002003 }
2004
2005 return;
2006}
2007
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002008static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002009 struct snd_ctl_elem_value *ucontrol)
2010{
2011 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2012 struct via_spec *spec = codec->spec;
2013
2014 if (spec->codec_type != VT1708)
2015 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002016 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002017 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002018 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002019 return 0;
2020}
2021
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002022static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002023 struct snd_ctl_elem_value *ucontrol)
2024{
2025 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2026 struct via_spec *spec = codec->spec;
2027 int change;
2028
2029 if (spec->codec_type != VT1708)
2030 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002031 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002032 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002033 == !spec->vt1708_jack_detect;
2034 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002035 mute_aa_path(codec, 1);
2036 notify_aa_path_ctls(codec);
2037 }
2038 return change;
2039}
2040
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002041static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2042 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2043 .name = "Jack Detect",
2044 .count = 1,
2045 .info = snd_ctl_boolean_mono_info,
2046 .get = vt1708_jack_detect_get,
2047 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002048};
2049
Takashi Iwai12daef62011-06-18 17:45:49 +02002050static void fill_dig_outs(struct hda_codec *codec);
2051static void fill_dig_in(struct hda_codec *codec);
2052
2053static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002054{
2055 struct via_spec *spec = codec->spec;
2056 int err;
2057
2058 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2059 if (err < 0)
2060 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002061 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002062 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002063
Takashi Iwai4a796162011-06-17 17:53:38 +02002064 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002065 if (err < 0)
2066 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002067 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002068 if (err < 0)
2069 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002070 err = via_auto_create_speaker_ctls(codec);
2071 if (err < 0)
2072 return err;
Takashi Iwai620e2b22011-06-17 17:19:19 +02002073 err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002074 if (err < 0)
2075 return err;
2076
2077 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2078
Takashi Iwai12daef62011-06-18 17:45:49 +02002079 fill_dig_outs(codec);
2080 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002081
Takashi Iwai603c4012008-07-30 15:01:44 +02002082 if (spec->kctls.list)
2083 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002084
Takashi Iwai096a8852011-06-20 12:09:02 +02002085 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002086
Harald Welte0aa62ae2008-09-09 15:58:27 +08002087 spec->input_mux = &spec->private_imux[0];
2088
Takashi Iwai8df2a312011-06-21 11:48:29 +02002089 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002090 err = via_hp_build(codec);
2091 if (err < 0)
2092 return err;
2093 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002094
Takashi Iwaif4a78282011-06-17 18:46:48 +02002095 err = via_smart51_build(codec);
2096 if (err < 0)
2097 return err;
2098
Takashi Iwai5d417622011-06-20 11:32:27 +02002099 /* assign slave outs */
2100 if (spec->slave_dig_outs[0])
2101 codec->slave_dig_outs = spec->slave_dig_outs;
2102
Joseph Chanc577b8a2006-11-29 15:29:40 +01002103 return 1;
2104}
2105
Takashi Iwai5d417622011-06-20 11:32:27 +02002106static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002107{
Lydia Wang25eaba22009-10-10 19:08:43 +08002108 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002109 if (spec->multiout.dig_out_nid)
2110 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2111 if (spec->slave_dig_outs[0])
2112 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2113}
Lydia Wang25eaba22009-10-10 19:08:43 +08002114
Takashi Iwai5d417622011-06-20 11:32:27 +02002115static void via_auto_init_dig_in(struct hda_codec *codec)
2116{
2117 struct via_spec *spec = codec->spec;
2118 if (!spec->dig_in_nid)
2119 return;
2120 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2121 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2122}
2123
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002124/* initialize the unsolicited events */
2125static void via_auto_init_unsol_event(struct hda_codec *codec)
2126{
2127 struct via_spec *spec = codec->spec;
2128 struct auto_pin_cfg *cfg = &spec->autocfg;
2129 unsigned int ev;
2130 int i;
2131
2132 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2133 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2134 AC_VERB_SET_UNSOLICITED_ENABLE,
2135 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2136
2137 if (cfg->speaker_pins[0])
2138 ev = VIA_LINE_EVENT;
2139 else
2140 ev = 0;
2141 for (i = 0; i < cfg->line_outs; i++) {
2142 if (cfg->line_out_pins[i] &&
2143 is_jack_detectable(codec, cfg->line_out_pins[i]))
2144 snd_hda_codec_write(codec, cfg->line_out_pins[0], 0,
2145 AC_VERB_SET_UNSOLICITED_ENABLE,
2146 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2147 }
2148
2149 for (i = 0; i < cfg->num_inputs; i++) {
2150 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2151 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2152 AC_VERB_SET_UNSOLICITED_ENABLE,
2153 AC_USRSP_EN | VIA_JACK_EVENT);
2154 }
2155}
2156
Takashi Iwai5d417622011-06-20 11:32:27 +02002157static int via_init(struct hda_codec *codec)
2158{
2159 struct via_spec *spec = codec->spec;
2160 int i;
2161
2162 for (i = 0; i < spec->num_iverbs; i++)
2163 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2164
Joseph Chanc577b8a2006-11-29 15:29:40 +01002165 via_auto_init_multi_out(codec);
2166 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002167 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002168 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002169 via_auto_init_dig_outs(codec);
2170 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002171
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002172 via_auto_init_unsol_event(codec);
2173
2174 via_hp_automute(codec);
2175 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002176
Joseph Chanc577b8a2006-11-29 15:29:40 +01002177 return 0;
2178}
2179
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002180static void vt1708_update_hp_jack_state(struct work_struct *work)
2181{
2182 struct via_spec *spec = container_of(work, struct via_spec,
2183 vt1708_hp_work.work);
2184 if (spec->codec_type != VT1708)
2185 return;
2186 /* if jack state toggled */
2187 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002188 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002189 spec->vt1708_hp_present ^= 1;
2190 via_hp_automute(spec->codec);
2191 }
2192 vt1708_start_hp_work(spec);
2193}
2194
Takashi Iwai337b9d02009-07-07 18:18:59 +02002195static int get_mux_nids(struct hda_codec *codec)
2196{
2197 struct via_spec *spec = codec->spec;
2198 hda_nid_t nid, conn[8];
2199 unsigned int type;
2200 int i, n;
2201
2202 for (i = 0; i < spec->num_adc_nids; i++) {
2203 nid = spec->adc_nids[i];
2204 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002205 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002206 if (type == AC_WID_PIN)
2207 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002208 n = snd_hda_get_connections(codec, nid, conn,
2209 ARRAY_SIZE(conn));
2210 if (n <= 0)
2211 break;
2212 if (n > 1) {
2213 spec->mux_nids[i] = nid;
2214 break;
2215 }
2216 nid = conn[0];
2217 }
2218 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002219 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002220}
2221
Joseph Chanc577b8a2006-11-29 15:29:40 +01002222static int patch_vt1708(struct hda_codec *codec)
2223{
2224 struct via_spec *spec;
2225 int err;
2226
2227 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002228 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002229 if (spec == NULL)
2230 return -ENOMEM;
2231
Takashi Iwai620e2b22011-06-17 17:19:19 +02002232 spec->aa_mix_nid = 0x17;
2233
Takashi Iwai12daef62011-06-18 17:45:49 +02002234 /* Add HP and CD pin config connect bit re-config action */
2235 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2236 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2237
Joseph Chanc577b8a2006-11-29 15:29:40 +01002238 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002239 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002240 if (err < 0) {
2241 via_free(codec);
2242 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002243 }
2244
Takashi Iwai12daef62011-06-18 17:45:49 +02002245 /* add jack detect on/off control */
2246 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2247 return -ENOMEM;
2248
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002249 /* disable 32bit format on VT1708 */
2250 if (codec->vendor_id == 0x11061708)
2251 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002252
Joseph Chanc577b8a2006-11-29 15:29:40 +01002253 codec->patch_ops = via_patch_ops;
2254
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002255 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002256 return 0;
2257}
2258
Joseph Chanc577b8a2006-11-29 15:29:40 +01002259static int patch_vt1709_10ch(struct hda_codec *codec)
2260{
2261 struct via_spec *spec;
2262 int err;
2263
2264 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002265 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002266 if (spec == NULL)
2267 return -ENOMEM;
2268
Takashi Iwai620e2b22011-06-17 17:19:19 +02002269 spec->aa_mix_nid = 0x18;
2270
Takashi Iwai12daef62011-06-18 17:45:49 +02002271 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002272 if (err < 0) {
2273 via_free(codec);
2274 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002275 }
2276
Joseph Chanc577b8a2006-11-29 15:29:40 +01002277 codec->patch_ops = via_patch_ops;
2278
Joseph Chanc577b8a2006-11-29 15:29:40 +01002279 return 0;
2280}
2281/*
2282 * generic initialization of ADC, input mixers and output mixers
2283 */
Joseph Chanc577b8a2006-11-29 15:29:40 +01002284static int patch_vt1709_6ch(struct hda_codec *codec)
2285{
2286 struct via_spec *spec;
2287 int err;
2288
2289 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002290 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002291 if (spec == NULL)
2292 return -ENOMEM;
2293
Takashi Iwai620e2b22011-06-17 17:19:19 +02002294 spec->aa_mix_nid = 0x18;
2295
Takashi Iwai12daef62011-06-18 17:45:49 +02002296 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002297 if (err < 0) {
2298 via_free(codec);
2299 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002300 }
2301
Joseph Chanc577b8a2006-11-29 15:29:40 +01002302 codec->patch_ops = via_patch_ops;
2303
Josepch Chanf7278fd2007-12-13 16:40:40 +01002304 return 0;
2305}
2306
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002307static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2308{
2309 struct via_spec *spec = codec->spec;
2310 int imux_is_smixer;
2311 unsigned int parm;
2312 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002313 if ((spec->codec_type != VT1708B_4CH) &&
2314 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002315 is_8ch = 1;
2316
2317 /* SW0 (17h) = stereo mixer */
2318 imux_is_smixer =
2319 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2320 == ((spec->codec_type == VT1708S) ? 5 : 0));
2321 /* inputs */
2322 /* PW 1/2/5 (1ah/1bh/1eh) */
2323 parm = AC_PWRST_D3;
2324 set_pin_power_state(codec, 0x1a, &parm);
2325 set_pin_power_state(codec, 0x1b, &parm);
2326 set_pin_power_state(codec, 0x1e, &parm);
2327 if (imux_is_smixer)
2328 parm = AC_PWRST_D0;
2329 /* SW0 (17h), AIW 0/1 (13h/14h) */
2330 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2331 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2332 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2333
2334 /* outputs */
2335 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2336 parm = AC_PWRST_D3;
2337 set_pin_power_state(codec, 0x19, &parm);
2338 if (spec->smart51_enabled)
2339 set_pin_power_state(codec, 0x1b, &parm);
2340 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2341 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2342
2343 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2344 if (is_8ch) {
2345 parm = AC_PWRST_D3;
2346 set_pin_power_state(codec, 0x22, &parm);
2347 if (spec->smart51_enabled)
2348 set_pin_power_state(codec, 0x1a, &parm);
2349 snd_hda_codec_write(codec, 0x26, 0,
2350 AC_VERB_SET_POWER_STATE, parm);
2351 snd_hda_codec_write(codec, 0x24, 0,
2352 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002353 } else if (codec->vendor_id == 0x11064397) {
2354 /* PW7(23h), SW2(27h), AOW2(25h) */
2355 parm = AC_PWRST_D3;
2356 set_pin_power_state(codec, 0x23, &parm);
2357 if (spec->smart51_enabled)
2358 set_pin_power_state(codec, 0x1a, &parm);
2359 snd_hda_codec_write(codec, 0x27, 0,
2360 AC_VERB_SET_POWER_STATE, parm);
2361 snd_hda_codec_write(codec, 0x25, 0,
2362 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002363 }
2364
2365 /* PW 3/4/7 (1ch/1dh/23h) */
2366 parm = AC_PWRST_D3;
2367 /* force to D0 for internal Speaker */
2368 set_pin_power_state(codec, 0x1c, &parm);
2369 set_pin_power_state(codec, 0x1d, &parm);
2370 if (is_8ch)
2371 set_pin_power_state(codec, 0x23, &parm);
2372
2373 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2374 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2375 imux_is_smixer ? AC_PWRST_D0 : parm);
2376 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2377 if (is_8ch) {
2378 snd_hda_codec_write(codec, 0x25, 0,
2379 AC_VERB_SET_POWER_STATE, parm);
2380 snd_hda_codec_write(codec, 0x27, 0,
2381 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002382 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2383 snd_hda_codec_write(codec, 0x25, 0,
2384 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002385}
2386
Lydia Wang518bf3b2009-10-10 19:07:29 +08002387static int patch_vt1708S(struct hda_codec *codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002388static int patch_vt1708B_8ch(struct hda_codec *codec)
2389{
2390 struct via_spec *spec;
2391 int err;
2392
Lydia Wang518bf3b2009-10-10 19:07:29 +08002393 if (get_codec_type(codec) == VT1708BCE)
2394 return patch_vt1708S(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002395 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002396 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002397 if (spec == NULL)
2398 return -ENOMEM;
2399
Takashi Iwai620e2b22011-06-17 17:19:19 +02002400 spec->aa_mix_nid = 0x16;
2401
Josepch Chanf7278fd2007-12-13 16:40:40 +01002402 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002403 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002404 if (err < 0) {
2405 via_free(codec);
2406 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002407 }
2408
Josepch Chanf7278fd2007-12-13 16:40:40 +01002409 codec->patch_ops = via_patch_ops;
2410
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002411 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2412
Josepch Chanf7278fd2007-12-13 16:40:40 +01002413 return 0;
2414}
2415
2416static int patch_vt1708B_4ch(struct hda_codec *codec)
2417{
2418 struct via_spec *spec;
2419 int err;
2420
2421 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002422 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002423 if (spec == NULL)
2424 return -ENOMEM;
2425
Josepch Chanf7278fd2007-12-13 16:40:40 +01002426 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002427 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002428 if (err < 0) {
2429 via_free(codec);
2430 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002431 }
2432
Josepch Chanf7278fd2007-12-13 16:40:40 +01002433 codec->patch_ops = via_patch_ops;
2434
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002435 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2436
Joseph Chanc577b8a2006-11-29 15:29:40 +01002437 return 0;
2438}
2439
Harald Welted949cac2008-09-09 15:56:01 +08002440/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002441static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002442 /* Enable Mic Boost Volume backdoor */
2443 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002444 /* don't bybass mixer */
2445 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002446 { }
2447};
2448
Takashi Iwai9da29272009-05-07 16:31:14 +02002449/* fill out digital output widgets; one for master and one for slave outputs */
2450static void fill_dig_outs(struct hda_codec *codec)
2451{
2452 struct via_spec *spec = codec->spec;
2453 int i;
2454
2455 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2456 hda_nid_t nid;
2457 int conn;
2458
2459 nid = spec->autocfg.dig_out_pins[i];
2460 if (!nid)
2461 continue;
2462 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2463 if (conn < 1)
2464 continue;
2465 if (!spec->multiout.dig_out_nid)
2466 spec->multiout.dig_out_nid = nid;
2467 else {
2468 spec->slave_dig_outs[0] = nid;
2469 break; /* at most two dig outs */
2470 }
2471 }
2472}
2473
Takashi Iwai12daef62011-06-18 17:45:49 +02002474static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002475{
2476 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002477 hda_nid_t dig_nid;
2478 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002479
Takashi Iwai12daef62011-06-18 17:45:49 +02002480 if (!spec->autocfg.dig_in_pin)
2481 return;
Harald Welted949cac2008-09-09 15:56:01 +08002482
Takashi Iwai12daef62011-06-18 17:45:49 +02002483 dig_nid = codec->start_nid;
2484 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2485 unsigned int wcaps = get_wcaps(codec, dig_nid);
2486 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2487 continue;
2488 if (!(wcaps & AC_WCAP_DIGITAL))
2489 continue;
2490 if (!(wcaps & AC_WCAP_CONN_LIST))
2491 continue;
2492 err = get_connection_index(codec, dig_nid,
2493 spec->autocfg.dig_in_pin);
2494 if (err >= 0) {
2495 spec->dig_in_nid = dig_nid;
2496 break;
2497 }
2498 }
Harald Welted949cac2008-09-09 15:56:01 +08002499}
2500
Lydia Wang6369bcf2009-10-10 19:08:31 +08002501static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2502 int offset, int num_steps, int step_size)
2503{
2504 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2505 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2506 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2507 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2508 (0 << AC_AMPCAP_MUTE_SHIFT));
2509}
2510
Harald Welted949cac2008-09-09 15:56:01 +08002511static int patch_vt1708S(struct hda_codec *codec)
2512{
2513 struct via_spec *spec;
2514 int err;
2515
2516 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002517 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002518 if (spec == NULL)
2519 return -ENOMEM;
2520
Takashi Iwai620e2b22011-06-17 17:19:19 +02002521 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002522 override_mic_boost(codec, 0x1a, 0, 3, 40);
2523 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002524
Harald Welted949cac2008-09-09 15:56:01 +08002525 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002526 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002527 if (err < 0) {
2528 via_free(codec);
2529 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002530 }
2531
Takashi Iwai096a8852011-06-20 12:09:02 +02002532 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002533
Harald Welted949cac2008-09-09 15:56:01 +08002534 codec->patch_ops = via_patch_ops;
2535
Lydia Wang518bf3b2009-10-10 19:07:29 +08002536 /* correct names for VT1708BCE */
2537 if (get_codec_type(codec) == VT1708BCE) {
2538 kfree(codec->chip_name);
2539 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2540 snprintf(codec->bus->card->mixername,
2541 sizeof(codec->bus->card->mixername),
2542 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002543 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002544 /* correct names for VT1705 */
2545 if (codec->vendor_id == 0x11064397) {
2546 kfree(codec->chip_name);
2547 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2548 snprintf(codec->bus->card->mixername,
2549 sizeof(codec->bus->card->mixername),
2550 "%s %s", codec->vendor_name, codec->chip_name);
2551 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002552 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002553 return 0;
2554}
2555
2556/* Patch for VT1702 */
2557
Takashi Iwai096a8852011-06-20 12:09:02 +02002558static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002559 /* mixer enable */
2560 {0x1, 0xF88, 0x3},
2561 /* GPIO 0~2 */
2562 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002563 { }
2564};
2565
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002566static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2567{
2568 int imux_is_smixer =
2569 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2570 unsigned int parm;
2571 /* inputs */
2572 /* PW 1/2/5 (14h/15h/18h) */
2573 parm = AC_PWRST_D3;
2574 set_pin_power_state(codec, 0x14, &parm);
2575 set_pin_power_state(codec, 0x15, &parm);
2576 set_pin_power_state(codec, 0x18, &parm);
2577 if (imux_is_smixer)
2578 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2579 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2580 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2581 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2582 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2583 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2584
2585 /* outputs */
2586 /* PW 3/4 (16h/17h) */
2587 parm = AC_PWRST_D3;
2588 set_pin_power_state(codec, 0x17, &parm);
2589 set_pin_power_state(codec, 0x16, &parm);
2590 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2591 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2592 imux_is_smixer ? AC_PWRST_D0 : parm);
2593 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2594 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2595}
2596
Harald Welted949cac2008-09-09 15:56:01 +08002597static int patch_vt1702(struct hda_codec *codec)
2598{
2599 struct via_spec *spec;
2600 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002601
2602 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002603 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002604 if (spec == NULL)
2605 return -ENOMEM;
2606
Takashi Iwai620e2b22011-06-17 17:19:19 +02002607 spec->aa_mix_nid = 0x1a;
2608
Takashi Iwai12daef62011-06-18 17:45:49 +02002609 /* limit AA path volume to 0 dB */
2610 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2611 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2612 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2613 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2614 (1 << AC_AMPCAP_MUTE_SHIFT));
2615
Harald Welted949cac2008-09-09 15:56:01 +08002616 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002617 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002618 if (err < 0) {
2619 via_free(codec);
2620 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002621 }
2622
Takashi Iwai096a8852011-06-20 12:09:02 +02002623 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002624
Harald Welted949cac2008-09-09 15:56:01 +08002625 codec->patch_ops = via_patch_ops;
2626
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002627 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002628 return 0;
2629}
2630
Lydia Wangeb7188c2009-10-10 19:08:34 +08002631/* Patch for VT1718S */
2632
Takashi Iwai096a8852011-06-20 12:09:02 +02002633static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002634 /* Enable MW0 adjust Gain 5 */
2635 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002636 /* Enable Boost Volume backdoor */
2637 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002638
Lydia Wangeb7188c2009-10-10 19:08:34 +08002639 { }
2640};
2641
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002642static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2643{
2644 struct via_spec *spec = codec->spec;
2645 int imux_is_smixer;
2646 unsigned int parm;
2647 /* MUX6 (1eh) = stereo mixer */
2648 imux_is_smixer =
2649 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2650 /* inputs */
2651 /* PW 5/6/7 (29h/2ah/2bh) */
2652 parm = AC_PWRST_D3;
2653 set_pin_power_state(codec, 0x29, &parm);
2654 set_pin_power_state(codec, 0x2a, &parm);
2655 set_pin_power_state(codec, 0x2b, &parm);
2656 if (imux_is_smixer)
2657 parm = AC_PWRST_D0;
2658 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2659 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2660 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2661 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2662 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2663
2664 /* outputs */
2665 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2666 parm = AC_PWRST_D3;
2667 set_pin_power_state(codec, 0x27, &parm);
2668 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2669 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2670
2671 /* PW2 (26h), AOW2 (ah) */
2672 parm = AC_PWRST_D3;
2673 set_pin_power_state(codec, 0x26, &parm);
2674 if (spec->smart51_enabled)
2675 set_pin_power_state(codec, 0x2b, &parm);
2676 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2677
2678 /* PW0 (24h), AOW0 (8h) */
2679 parm = AC_PWRST_D3;
2680 set_pin_power_state(codec, 0x24, &parm);
2681 if (!spec->hp_independent_mode) /* check for redirected HP */
2682 set_pin_power_state(codec, 0x28, &parm);
2683 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2684 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2685 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2686 imux_is_smixer ? AC_PWRST_D0 : parm);
2687
2688 /* PW1 (25h), AOW1 (9h) */
2689 parm = AC_PWRST_D3;
2690 set_pin_power_state(codec, 0x25, &parm);
2691 if (spec->smart51_enabled)
2692 set_pin_power_state(codec, 0x2a, &parm);
2693 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2694
2695 if (spec->hp_independent_mode) {
2696 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2697 parm = AC_PWRST_D3;
2698 set_pin_power_state(codec, 0x28, &parm);
2699 snd_hda_codec_write(codec, 0x1b, 0,
2700 AC_VERB_SET_POWER_STATE, parm);
2701 snd_hda_codec_write(codec, 0x34, 0,
2702 AC_VERB_SET_POWER_STATE, parm);
2703 snd_hda_codec_write(codec, 0xc, 0,
2704 AC_VERB_SET_POWER_STATE, parm);
2705 }
2706}
2707
Lydia Wangeb7188c2009-10-10 19:08:34 +08002708static int patch_vt1718S(struct hda_codec *codec)
2709{
2710 struct via_spec *spec;
2711 int err;
2712
2713 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002714 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002715 if (spec == NULL)
2716 return -ENOMEM;
2717
Takashi Iwai620e2b22011-06-17 17:19:19 +02002718 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002719 override_mic_boost(codec, 0x2b, 0, 3, 40);
2720 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002721
Lydia Wangeb7188c2009-10-10 19:08:34 +08002722 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002723 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002724 if (err < 0) {
2725 via_free(codec);
2726 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002727 }
2728
Takashi Iwai096a8852011-06-20 12:09:02 +02002729 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002730
Lydia Wangeb7188c2009-10-10 19:08:34 +08002731 codec->patch_ops = via_patch_ops;
2732
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002733 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2734
Lydia Wangeb7188c2009-10-10 19:08:34 +08002735 return 0;
2736}
Lydia Wangf3db4232009-10-10 19:08:41 +08002737
2738/* Patch for VT1716S */
2739
2740static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2741 struct snd_ctl_elem_info *uinfo)
2742{
2743 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2744 uinfo->count = 1;
2745 uinfo->value.integer.min = 0;
2746 uinfo->value.integer.max = 1;
2747 return 0;
2748}
2749
2750static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2751 struct snd_ctl_elem_value *ucontrol)
2752{
2753 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2754 int index = 0;
2755
2756 index = snd_hda_codec_read(codec, 0x26, 0,
2757 AC_VERB_GET_CONNECT_SEL, 0);
2758 if (index != -1)
2759 *ucontrol->value.integer.value = index;
2760
2761 return 0;
2762}
2763
2764static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2765 struct snd_ctl_elem_value *ucontrol)
2766{
2767 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2768 struct via_spec *spec = codec->spec;
2769 int index = *ucontrol->value.integer.value;
2770
2771 snd_hda_codec_write(codec, 0x26, 0,
2772 AC_VERB_SET_CONNECT_SEL, index);
2773 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002774 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002775 return 1;
2776}
2777
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002778static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002779 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2780 {
2781 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2782 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002783 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08002784 .count = 1,
2785 .info = vt1716s_dmic_info,
2786 .get = vt1716s_dmic_get,
2787 .put = vt1716s_dmic_put,
2788 },
2789 {} /* end */
2790};
2791
2792
2793/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002794static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002795 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
2796 { } /* end */
2797};
2798
Takashi Iwai096a8852011-06-20 12:09:02 +02002799static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002800 /* Enable Boost Volume backdoor */
2801 {0x1, 0xf8a, 0x80},
2802 /* don't bybass mixer */
2803 {0x1, 0xf88, 0xc0},
2804 /* Enable mono output */
2805 {0x1, 0xf90, 0x08},
2806 { }
2807};
2808
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002809static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
2810{
2811 struct via_spec *spec = codec->spec;
2812 int imux_is_smixer;
2813 unsigned int parm;
2814 unsigned int mono_out, present;
2815 /* SW0 (17h) = stereo mixer */
2816 imux_is_smixer =
2817 (snd_hda_codec_read(codec, 0x17, 0,
2818 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
2819 /* inputs */
2820 /* PW 1/2/5 (1ah/1bh/1eh) */
2821 parm = AC_PWRST_D3;
2822 set_pin_power_state(codec, 0x1a, &parm);
2823 set_pin_power_state(codec, 0x1b, &parm);
2824 set_pin_power_state(codec, 0x1e, &parm);
2825 if (imux_is_smixer)
2826 parm = AC_PWRST_D0;
2827 /* SW0 (17h), AIW0(13h) */
2828 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2829 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2830
2831 parm = AC_PWRST_D3;
2832 set_pin_power_state(codec, 0x1e, &parm);
2833 /* PW11 (22h) */
2834 if (spec->dmic_enabled)
2835 set_pin_power_state(codec, 0x22, &parm);
2836 else
2837 snd_hda_codec_write(codec, 0x22, 0,
2838 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
2839
2840 /* SW2(26h), AIW1(14h) */
2841 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
2842 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2843
2844 /* outputs */
2845 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2846 parm = AC_PWRST_D3;
2847 set_pin_power_state(codec, 0x19, &parm);
2848 /* Smart 5.1 PW2(1bh) */
2849 if (spec->smart51_enabled)
2850 set_pin_power_state(codec, 0x1b, &parm);
2851 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2852 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2853
2854 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
2855 parm = AC_PWRST_D3;
2856 set_pin_power_state(codec, 0x23, &parm);
2857 /* Smart 5.1 PW1(1ah) */
2858 if (spec->smart51_enabled)
2859 set_pin_power_state(codec, 0x1a, &parm);
2860 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
2861
2862 /* Smart 5.1 PW5(1eh) */
2863 if (spec->smart51_enabled)
2864 set_pin_power_state(codec, 0x1e, &parm);
2865 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
2866
2867 /* Mono out */
2868 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
2869 present = snd_hda_jack_detect(codec, 0x1c);
2870
2871 if (present)
2872 mono_out = 0;
2873 else {
2874 present = snd_hda_jack_detect(codec, 0x1d);
2875 if (!spec->hp_independent_mode && present)
2876 mono_out = 0;
2877 else
2878 mono_out = 1;
2879 }
2880 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
2881 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
2882 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
2883 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
2884
2885 /* PW 3/4 (1ch/1dh) */
2886 parm = AC_PWRST_D3;
2887 set_pin_power_state(codec, 0x1c, &parm);
2888 set_pin_power_state(codec, 0x1d, &parm);
2889 /* HP Independent Mode, power on AOW3 */
2890 if (spec->hp_independent_mode)
2891 snd_hda_codec_write(codec, 0x25, 0,
2892 AC_VERB_SET_POWER_STATE, parm);
2893
2894 /* force to D0 for internal Speaker */
2895 /* MW0 (16h), AOW0 (10h) */
2896 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2897 imux_is_smixer ? AC_PWRST_D0 : parm);
2898 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
2899 mono_out ? AC_PWRST_D0 : parm);
2900}
2901
Lydia Wangf3db4232009-10-10 19:08:41 +08002902static int patch_vt1716S(struct hda_codec *codec)
2903{
2904 struct via_spec *spec;
2905 int err;
2906
2907 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002908 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002909 if (spec == NULL)
2910 return -ENOMEM;
2911
Takashi Iwai620e2b22011-06-17 17:19:19 +02002912 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002913 override_mic_boost(codec, 0x1a, 0, 3, 40);
2914 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002915
Lydia Wangf3db4232009-10-10 19:08:41 +08002916 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002917 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002918 if (err < 0) {
2919 via_free(codec);
2920 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08002921 }
2922
Takashi Iwai096a8852011-06-20 12:09:02 +02002923 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08002924
Lydia Wangf3db4232009-10-10 19:08:41 +08002925 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
2926 spec->num_mixers++;
2927
2928 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
2929
2930 codec->patch_ops = via_patch_ops;
2931
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002932 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08002933 return 0;
2934}
Lydia Wang25eaba22009-10-10 19:08:43 +08002935
2936/* for vt2002P */
2937
Takashi Iwai096a8852011-06-20 12:09:02 +02002938static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08002939 /* Class-D speaker related verbs */
2940 {0x1, 0xfe0, 0x4},
2941 {0x1, 0xfe9, 0x80},
2942 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08002943 /* Enable Boost Volume backdoor */
2944 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08002945 /* Enable AOW0 to MW9 */
2946 {0x1, 0xfb8, 0x88},
2947 { }
2948};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002949
Takashi Iwai096a8852011-06-20 12:09:02 +02002950static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08002951 /* Enable Boost Volume backdoor */
2952 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08002953 /* Enable AOW0 to MW9 */
2954 {0x1, 0xfb8, 0x88},
2955 { }
2956};
Lydia Wang25eaba22009-10-10 19:08:43 +08002957
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002958static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
2959{
2960 struct via_spec *spec = codec->spec;
2961 int imux_is_smixer;
2962 unsigned int parm;
2963 unsigned int present;
2964 /* MUX9 (1eh) = stereo mixer */
2965 imux_is_smixer =
2966 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2967 /* inputs */
2968 /* PW 5/6/7 (29h/2ah/2bh) */
2969 parm = AC_PWRST_D3;
2970 set_pin_power_state(codec, 0x29, &parm);
2971 set_pin_power_state(codec, 0x2a, &parm);
2972 set_pin_power_state(codec, 0x2b, &parm);
2973 parm = AC_PWRST_D0;
2974 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
2975 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2976 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2977 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2978 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2979
2980 /* outputs */
2981 /* AOW0 (8h)*/
2982 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2983
Lydia Wang118909562011-03-23 17:57:34 +08002984 if (spec->codec_type == VT1802) {
2985 /* PW4 (28h), MW4 (18h), MUX4(38h) */
2986 parm = AC_PWRST_D3;
2987 set_pin_power_state(codec, 0x28, &parm);
2988 snd_hda_codec_write(codec, 0x18, 0,
2989 AC_VERB_SET_POWER_STATE, parm);
2990 snd_hda_codec_write(codec, 0x38, 0,
2991 AC_VERB_SET_POWER_STATE, parm);
2992 } else {
2993 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
2994 parm = AC_PWRST_D3;
2995 set_pin_power_state(codec, 0x26, &parm);
2996 snd_hda_codec_write(codec, 0x1c, 0,
2997 AC_VERB_SET_POWER_STATE, parm);
2998 snd_hda_codec_write(codec, 0x37, 0,
2999 AC_VERB_SET_POWER_STATE, parm);
3000 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003001
Lydia Wang118909562011-03-23 17:57:34 +08003002 if (spec->codec_type == VT1802) {
3003 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3004 parm = AC_PWRST_D3;
3005 set_pin_power_state(codec, 0x25, &parm);
3006 snd_hda_codec_write(codec, 0x15, 0,
3007 AC_VERB_SET_POWER_STATE, parm);
3008 snd_hda_codec_write(codec, 0x35, 0,
3009 AC_VERB_SET_POWER_STATE, parm);
3010 } else {
3011 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3012 parm = AC_PWRST_D3;
3013 set_pin_power_state(codec, 0x25, &parm);
3014 snd_hda_codec_write(codec, 0x19, 0,
3015 AC_VERB_SET_POWER_STATE, parm);
3016 snd_hda_codec_write(codec, 0x35, 0,
3017 AC_VERB_SET_POWER_STATE, parm);
3018 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003019
3020 if (spec->hp_independent_mode)
3021 snd_hda_codec_write(codec, 0x9, 0,
3022 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3023
3024 /* Class-D */
3025 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3026 present = snd_hda_jack_detect(codec, 0x25);
3027
3028 parm = AC_PWRST_D3;
3029 set_pin_power_state(codec, 0x24, &parm);
3030 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003031 if (spec->codec_type == VT1802)
3032 snd_hda_codec_write(codec, 0x14, 0,
3033 AC_VERB_SET_POWER_STATE, parm);
3034 else
3035 snd_hda_codec_write(codec, 0x18, 0,
3036 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003037 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3038
3039 /* Mono Out */
3040 present = snd_hda_jack_detect(codec, 0x26);
3041
3042 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003043 if (spec->codec_type == VT1802) {
3044 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3045 snd_hda_codec_write(codec, 0x33, 0,
3046 AC_VERB_SET_POWER_STATE, parm);
3047 snd_hda_codec_write(codec, 0x1c, 0,
3048 AC_VERB_SET_POWER_STATE, parm);
3049 snd_hda_codec_write(codec, 0x3c, 0,
3050 AC_VERB_SET_POWER_STATE, parm);
3051 } else {
3052 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3053 snd_hda_codec_write(codec, 0x31, 0,
3054 AC_VERB_SET_POWER_STATE, parm);
3055 snd_hda_codec_write(codec, 0x17, 0,
3056 AC_VERB_SET_POWER_STATE, parm);
3057 snd_hda_codec_write(codec, 0x3b, 0,
3058 AC_VERB_SET_POWER_STATE, parm);
3059 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003060 /* MW9 (21h) */
3061 if (imux_is_smixer || !is_aa_path_mute(codec))
3062 snd_hda_codec_write(codec, 0x21, 0,
3063 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3064 else
3065 snd_hda_codec_write(codec, 0x21, 0,
3066 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3067}
Lydia Wang25eaba22009-10-10 19:08:43 +08003068
3069/* patch for vt2002P */
3070static int patch_vt2002P(struct hda_codec *codec)
3071{
3072 struct via_spec *spec;
3073 int err;
3074
3075 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003076 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003077 if (spec == NULL)
3078 return -ENOMEM;
3079
Takashi Iwai620e2b22011-06-17 17:19:19 +02003080 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003081 override_mic_boost(codec, 0x2b, 0, 3, 40);
3082 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003083
Lydia Wang25eaba22009-10-10 19:08:43 +08003084 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003085 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003086 if (err < 0) {
3087 via_free(codec);
3088 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003089 }
3090
Lydia Wang118909562011-03-23 17:57:34 +08003091 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003092 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003093 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003094 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003095
Lydia Wang25eaba22009-10-10 19:08:43 +08003096 codec->patch_ops = via_patch_ops;
3097
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003098 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003099 return 0;
3100}
Lydia Wangab6734e2009-10-10 19:08:46 +08003101
3102/* for vt1812 */
3103
Takashi Iwai096a8852011-06-20 12:09:02 +02003104static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003105 /* Enable Boost Volume backdoor */
3106 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003107 /* Enable AOW0 to MW9 */
3108 {0x1, 0xfb8, 0xa8},
3109 { }
3110};
3111
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003112static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3113{
3114 struct via_spec *spec = codec->spec;
3115 int imux_is_smixer =
3116 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3117 unsigned int parm;
3118 unsigned int present;
3119 /* MUX10 (1eh) = stereo mixer */
3120 imux_is_smixer =
3121 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3122 /* inputs */
3123 /* PW 5/6/7 (29h/2ah/2bh) */
3124 parm = AC_PWRST_D3;
3125 set_pin_power_state(codec, 0x29, &parm);
3126 set_pin_power_state(codec, 0x2a, &parm);
3127 set_pin_power_state(codec, 0x2b, &parm);
3128 parm = AC_PWRST_D0;
3129 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3130 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3131 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3132 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3133 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3134
3135 /* outputs */
3136 /* AOW0 (8h)*/
3137 snd_hda_codec_write(codec, 0x8, 0,
3138 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3139
3140 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3141 parm = AC_PWRST_D3;
3142 set_pin_power_state(codec, 0x28, &parm);
3143 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3144 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3145
3146 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3147 parm = AC_PWRST_D3;
3148 set_pin_power_state(codec, 0x25, &parm);
3149 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3150 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3151 if (spec->hp_independent_mode)
3152 snd_hda_codec_write(codec, 0x9, 0,
3153 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3154
3155 /* Internal Speaker */
3156 /* PW0 (24h), MW0(14h), MUX0(34h) */
3157 present = snd_hda_jack_detect(codec, 0x25);
3158
3159 parm = AC_PWRST_D3;
3160 set_pin_power_state(codec, 0x24, &parm);
3161 if (present) {
3162 snd_hda_codec_write(codec, 0x14, 0,
3163 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3164 snd_hda_codec_write(codec, 0x34, 0,
3165 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3166 } else {
3167 snd_hda_codec_write(codec, 0x14, 0,
3168 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3169 snd_hda_codec_write(codec, 0x34, 0,
3170 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3171 }
3172
3173
3174 /* Mono Out */
3175 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3176 present = snd_hda_jack_detect(codec, 0x28);
3177
3178 parm = AC_PWRST_D3;
3179 set_pin_power_state(codec, 0x31, &parm);
3180 if (present) {
3181 snd_hda_codec_write(codec, 0x1c, 0,
3182 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3183 snd_hda_codec_write(codec, 0x3c, 0,
3184 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3185 snd_hda_codec_write(codec, 0x3e, 0,
3186 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3187 } else {
3188 snd_hda_codec_write(codec, 0x1c, 0,
3189 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3190 snd_hda_codec_write(codec, 0x3c, 0,
3191 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3192 snd_hda_codec_write(codec, 0x3e, 0,
3193 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3194 }
3195
3196 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3197 parm = AC_PWRST_D3;
3198 set_pin_power_state(codec, 0x33, &parm);
3199 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3200 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3201
3202}
Lydia Wangab6734e2009-10-10 19:08:46 +08003203
3204/* patch for vt1812 */
3205static int patch_vt1812(struct hda_codec *codec)
3206{
3207 struct via_spec *spec;
3208 int err;
3209
3210 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003211 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003212 if (spec == NULL)
3213 return -ENOMEM;
3214
Takashi Iwai620e2b22011-06-17 17:19:19 +02003215 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003216 override_mic_boost(codec, 0x2b, 0, 3, 40);
3217 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003218
Lydia Wangab6734e2009-10-10 19:08:46 +08003219 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003220 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003221 if (err < 0) {
3222 via_free(codec);
3223 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003224 }
3225
Takashi Iwai096a8852011-06-20 12:09:02 +02003226 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003227
Lydia Wangab6734e2009-10-10 19:08:46 +08003228 codec->patch_ops = via_patch_ops;
3229
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003230 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003231 return 0;
3232}
3233
Joseph Chanc577b8a2006-11-29 15:29:40 +01003234/*
3235 * patch entries
3236 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003237static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003238 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3239 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3240 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3241 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3242 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003243 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003244 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003245 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003246 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003247 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003248 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003249 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003250 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003251 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003252 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003253 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003254 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003255 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003256 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003257 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003258 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003259 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003260 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003261 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003262 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003263 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003264 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003265 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003266 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003267 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003268 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003269 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003270 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003271 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003272 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003273 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003274 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003275 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003276 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003277 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003278 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003279 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003280 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003281 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003282 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003283 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003284 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003285 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003286 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003287 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003288 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003289 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003290 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003291 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003292 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003293 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003294 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003295 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003296 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003297 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003298 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003299 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003300 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003301 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003302 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003303 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003304 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003305 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003306 { .id = 0x11060428, .name = "VT1718S",
3307 .patch = patch_vt1718S},
3308 { .id = 0x11064428, .name = "VT1718S",
3309 .patch = patch_vt1718S},
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08003310 { .id = 0x11060441, .name = "VT2020",
3311 .patch = patch_vt1718S},
3312 { .id = 0x11064441, .name = "VT1828S",
3313 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003314 { .id = 0x11060433, .name = "VT1716S",
3315 .patch = patch_vt1716S},
3316 { .id = 0x1106a721, .name = "VT1716S",
3317 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003318 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3319 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003320 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003321 { .id = 0x11060440, .name = "VT1818S",
3322 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003323 { .id = 0x11060446, .name = "VT1802",
3324 .patch = patch_vt2002P},
3325 { .id = 0x11068446, .name = "VT1802",
3326 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003327 {} /* terminator */
3328};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003329
3330MODULE_ALIAS("snd-hda-codec-id:1106*");
3331
3332static struct hda_codec_preset_list via_list = {
3333 .preset = snd_hda_preset_via,
3334 .owner = THIS_MODULE,
3335};
3336
3337MODULE_LICENSE("GPL");
3338MODULE_DESCRIPTION("VIA HD-audio codec");
3339
3340static int __init patch_via_init(void)
3341{
3342 return snd_hda_add_codec_preset(&via_list);
3343}
3344
3345static void __exit patch_via_exit(void)
3346{
3347 snd_hda_delete_codec_preset(&via_list);
3348}
3349
3350module_init(patch_via_init)
3351module_exit(patch_via_exit)