blob: 5b907b356951941ed8907a977c89a67c5d15e8a6 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010052#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080053#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010054#include "hda_codec.h"
55#include "hda_local.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010056
Joseph Chanc577b8a2006-11-29 15:29:40 +010057/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080058#define VT1708_HP_PIN_NID 0x20
59#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010060
Harald Welted7426322008-09-15 22:43:23 +080061enum VIA_HDA_CODEC {
62 UNKNOWN = -1,
63 VT1708,
64 VT1709_10CH,
65 VT1709_6CH,
66 VT1708B_8CH,
67 VT1708B_4CH,
68 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080069 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080070 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080071 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080072 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080073 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080074 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080075 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080076 CODEC_TYPES,
77};
78
Lydia Wang118909562011-03-23 17:57:34 +080079#define VT2002P_COMPATIBLE(spec) \
80 ((spec)->codec_type == VT2002P ||\
81 (spec)->codec_type == VT1812 ||\
82 (spec)->codec_type == VT1802)
83
Takashi Iwai8e3679d2011-06-21 09:01:36 +020084#define MAX_NID_PATH_DEPTH 5
85
Takashi Iwai4a796162011-06-17 17:53:38 +020086struct nid_path {
87 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020088 hda_nid_t path[MAX_NID_PATH_DEPTH];
89 short idx[MAX_NID_PATH_DEPTH];
Takashi Iwai4a796162011-06-17 17:53:38 +020090};
91
Lydia Wang1f2e99f2009-10-10 19:08:17 +080092struct via_spec {
93 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +020094 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +080095 unsigned int num_mixers;
96
Takashi Iwai90dd48a2011-05-02 12:38:19 +020097 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +080098 unsigned int num_iverbs;
99
Takashi Iwai82673bc2011-06-17 16:24:21 +0200100 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200101 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200102 const struct hda_pcm_stream *stream_analog_playback;
103 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800104
Takashi Iwai82673bc2011-06-17 16:24:21 +0200105 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200106 const struct hda_pcm_stream *stream_digital_playback;
107 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800108
109 /* playback */
110 struct hda_multi_out multiout;
111 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200112 hda_nid_t hp_dac_nid;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200113 int num_active_streams;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800114
Takashi Iwai4a796162011-06-17 17:53:38 +0200115 struct nid_path out_path[4];
116 struct nid_path hp_path;
117 struct nid_path hp_dep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200118 struct nid_path speaker_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200119
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800120 /* capture */
121 unsigned int num_adc_nids;
Takashi Iwaia766d0d2011-06-17 09:01:29 +0200122 hda_nid_t adc_nids[3];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800123 hda_nid_t mux_nids[3];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200124 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800125 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800126
127 /* capture source */
128 const struct hda_input_mux *input_mux;
129 unsigned int cur_mux[3];
130
131 /* PCM information */
132 struct hda_pcm pcm_rec[3];
133
134 /* dynamic controls, init_verbs and input_mux */
135 struct auto_pin_cfg autocfg;
136 struct snd_array kctls;
137 struct hda_input_mux private_imux[2];
138 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
139
140 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800141 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800142 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200143 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800144 enum VIA_HDA_CODEC codec_type;
145
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200146 /* smart51 setup */
147 unsigned int smart51_nums;
148 hda_nid_t smart51_pins[2];
149 int smart51_idxs[2];
150 const char *smart51_labels[2];
151 unsigned int smart51_enabled;
152
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800153 /* work to check hp jack state */
154 struct hda_codec *codec;
155 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200156 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800157 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800158
159 void (*set_widgets_power_state)(struct hda_codec *codec);
160
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800161 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200162 int num_loopbacks;
163 struct hda_amp_list loopback_list[8];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800164};
165
Lydia Wang0341ccd2011-03-22 16:25:03 +0800166static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100167static struct via_spec * via_new_spec(struct hda_codec *codec)
168{
169 struct via_spec *spec;
170
171 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
172 if (spec == NULL)
173 return NULL;
174
175 codec->spec = spec;
176 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800177 spec->codec_type = get_codec_type(codec);
178 /* VT1708BCE & VT1708S are almost same */
179 if (spec->codec_type == VT1708BCE)
180 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100181 return spec;
182}
183
Lydia Wang744ff5f2009-10-10 19:07:26 +0800184static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800185{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800186 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800187 u16 ven_id = vendor_id >> 16;
188 u16 dev_id = vendor_id & 0xffff;
189 enum VIA_HDA_CODEC codec_type;
190
191 /* get codec type */
192 if (ven_id != 0x1106)
193 codec_type = UNKNOWN;
194 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
195 codec_type = VT1708;
196 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
197 codec_type = VT1709_10CH;
198 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
199 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800200 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800201 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800202 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
203 codec_type = VT1708BCE;
204 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800205 codec_type = VT1708B_4CH;
206 else if ((dev_id & 0xfff) == 0x397
207 && (dev_id >> 12) < 8)
208 codec_type = VT1708S;
209 else if ((dev_id & 0xfff) == 0x398
210 && (dev_id >> 12) < 8)
211 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800212 else if ((dev_id & 0xfff) == 0x428
213 && (dev_id >> 12) < 8)
214 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800215 else if (dev_id == 0x0433 || dev_id == 0xa721)
216 codec_type = VT1716S;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +0800217 else if (dev_id == 0x0441 || dev_id == 0x4441)
218 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800219 else if (dev_id == 0x0438 || dev_id == 0x4438)
220 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800221 else if (dev_id == 0x0448)
222 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800223 else if (dev_id == 0x0440)
224 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800225 else if ((dev_id & 0xfff) == 0x446)
226 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800227 else
228 codec_type = UNKNOWN;
229 return codec_type;
230};
231
Lydia Wangec7e7e42011-03-24 12:43:44 +0800232#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800233#define VIA_HP_EVENT 0x01
234#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200235#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800236
Joseph Chanc577b8a2006-11-29 15:29:40 +0100237enum {
238 VIA_CTL_WIDGET_VOL,
239 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800240 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100241};
242
Takashi Iwaiada509e2011-06-20 15:40:19 +0200243static void analog_low_current_mode(struct hda_codec *codec);
244static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800245
246static void vt1708_start_hp_work(struct via_spec *spec)
247{
248 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
249 return;
250 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200251 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800252 if (!delayed_work_pending(&spec->vt1708_hp_work))
253 schedule_delayed_work(&spec->vt1708_hp_work,
254 msecs_to_jiffies(100));
255}
256
257static void vt1708_stop_hp_work(struct via_spec *spec)
258{
259 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
260 return;
261 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
262 && !is_aa_path_mute(spec->codec))
263 return;
264 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200265 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100266 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800267}
Lydia Wangf5271102009-10-10 19:07:35 +0800268
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800269static void set_widgets_power_state(struct hda_codec *codec)
270{
271 struct via_spec *spec = codec->spec;
272 if (spec->set_widgets_power_state)
273 spec->set_widgets_power_state(codec);
274}
Lydia Wang25eaba22009-10-10 19:08:43 +0800275
Lydia Wangf5271102009-10-10 19:07:35 +0800276static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
277 struct snd_ctl_elem_value *ucontrol)
278{
279 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
280 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
281
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800282 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200283 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800284 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
285 if (is_aa_path_mute(codec))
286 vt1708_start_hp_work(codec->spec);
287 else
288 vt1708_stop_hp_work(codec->spec);
289 }
Lydia Wangf5271102009-10-10 19:07:35 +0800290 return change;
291}
292
293/* modify .put = snd_hda_mixer_amp_switch_put */
294#define ANALOG_INPUT_MUTE \
295 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
296 .name = NULL, \
297 .index = 0, \
298 .info = snd_hda_mixer_amp_switch_info, \
299 .get = snd_hda_mixer_amp_switch_get, \
300 .put = analog_input_switch_put, \
301 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
302
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200303static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100304 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
305 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800306 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100307};
308
Lydia Wangab6734e2009-10-10 19:08:46 +0800309
Joseph Chanc577b8a2006-11-29 15:29:40 +0100310/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200311static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
312 const struct snd_kcontrol_new *tmpl,
313 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100314{
315 struct snd_kcontrol_new *knew;
316
Takashi Iwai603c4012008-07-30 15:01:44 +0200317 snd_array_init(&spec->kctls, sizeof(*knew), 32);
318 knew = snd_array_new(&spec->kctls);
319 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200320 return NULL;
321 *knew = *tmpl;
322 if (!name)
323 name = tmpl->name;
324 if (name) {
325 knew->name = kstrdup(name, GFP_KERNEL);
326 if (!knew->name)
327 return NULL;
328 }
329 return knew;
330}
331
332static int __via_add_control(struct via_spec *spec, int type, const char *name,
333 int idx, unsigned long val)
334{
335 struct snd_kcontrol_new *knew;
336
337 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
338 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100339 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200340 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100341 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100342 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100343 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100344 return 0;
345}
346
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200347#define via_add_control(spec, type, name, val) \
348 __via_add_control(spec, type, name, 0, val)
349
Takashi Iwai291c9e32011-06-17 16:15:26 +0200350#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100351
Takashi Iwai603c4012008-07-30 15:01:44 +0200352static void via_free_kctls(struct hda_codec *codec)
353{
354 struct via_spec *spec = codec->spec;
355
356 if (spec->kctls.list) {
357 struct snd_kcontrol_new *kctl = spec->kctls.list;
358 int i;
359 for (i = 0; i < spec->kctls.used; i++)
360 kfree(kctl[i].name);
361 }
362 snd_array_free(&spec->kctls);
363}
364
Joseph Chanc577b8a2006-11-29 15:29:40 +0100365/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800366static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200367 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100368{
369 char name[32];
370 int err;
371
372 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200373 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100374 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
375 if (err < 0)
376 return err;
377 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200378 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100379 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
380 if (err < 0)
381 return err;
382 return 0;
383}
384
Takashi Iwai5d417622011-06-20 11:32:27 +0200385/* return the index of the given widget nid as the source of mux;
386 * return -1 if not found;
387 * if num_conns is non-NULL, set the total number of connections
388 */
389static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux,
390 hda_nid_t nid, int *num_conns)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100391{
Takashi Iwai5d417622011-06-20 11:32:27 +0200392 hda_nid_t conn[HDA_MAX_NUM_INPUTS];
393 int i, nums;
394
395 nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
396 if (num_conns)
397 *num_conns = nums;
398 for (i = 0; i < nums; i++)
399 if (conn[i] == nid)
400 return i;
401 return -1;
402}
403
404#define get_connection_index(codec, mux, nid) \
405 __get_connection_index(codec, mux, nid, NULL)
406
Takashi Iwai8df2a312011-06-21 11:48:29 +0200407static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
408 unsigned int mask)
409{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200410 unsigned int caps;
411 if (!nid)
412 return false;
413 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200414 if (dir == HDA_INPUT)
415 caps &= AC_WCAP_IN_AMP;
416 else
417 caps &= AC_WCAP_OUT_AMP;
418 if (!caps)
419 return false;
420 if (query_amp_caps(codec, nid, dir) & mask)
421 return true;
422 return false;
423}
424
425#define have_vol_or_mute(codec, nid, dir) \
426 check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)
427
Takashi Iwai5d417622011-06-20 11:32:27 +0200428/* unmute input amp and select the specificed source */
429static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid,
430 hda_nid_t src, hda_nid_t mix)
431{
432 int idx, num_conns;
433
434 idx = __get_connection_index(codec, nid, src, &num_conns);
435 if (idx < 0)
436 return;
437
438 /* select the route explicitly when multiple connections exist */
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200439 if (num_conns > 1 &&
440 get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
Lydia Wang377ff312009-10-10 19:08:55 +0800441 snd_hda_codec_write(codec, nid, 0,
Takashi Iwai5d417622011-06-20 11:32:27 +0200442 AC_VERB_SET_CONNECT_SEL, idx);
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200443
Takashi Iwai5d417622011-06-20 11:32:27 +0200444 /* unmute if the input amp is present */
Takashi Iwai8df2a312011-06-21 11:48:29 +0200445 if (have_vol_or_mute(codec, nid, HDA_INPUT))
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200446 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
447 AMP_IN_UNMUTE(idx));
448
449 /* unmute the src output */
Takashi Iwai8df2a312011-06-21 11:48:29 +0200450 if (have_vol_or_mute(codec, src, HDA_OUTPUT))
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200451 snd_hda_codec_write(codec, src, 0, AC_VERB_SET_AMP_GAIN_MUTE,
452 AMP_OUT_UNMUTE);
Takashi Iwai5d417622011-06-20 11:32:27 +0200453
454 /* unmute AA-path if present */
Takashi Iwai8df2a312011-06-21 11:48:29 +0200455 if (!mix || mix == src)
Takashi Iwai5d417622011-06-20 11:32:27 +0200456 return;
457 idx = __get_connection_index(codec, nid, mix, NULL);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200458 if (idx >= 0 && have_vol_or_mute(codec, nid, HDA_INPUT))
Takashi Iwai5d417622011-06-20 11:32:27 +0200459 snd_hda_codec_write(codec, nid, 0,
460 AC_VERB_SET_AMP_GAIN_MUTE,
461 AMP_IN_UNMUTE(idx));
462}
463
464/* set the given pin as output */
465static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
466 int pin_type)
467{
468 if (!pin)
469 return;
470 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
471 pin_type);
472 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
473 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200474 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100475}
476
Takashi Iwai5d417622011-06-20 11:32:27 +0200477static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin,
478 int pin_type, struct nid_path *path)
479{
480 struct via_spec *spec = codec->spec;
481 unsigned int caps;
482 hda_nid_t nid;
483 int i;
484
485 if (!pin)
486 return;
487
488 init_output_pin(codec, pin, pin_type);
489 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
490 if (caps & AC_AMPCAP_MUTE) {
491 unsigned int val;
492 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
493 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
494 AMP_OUT_MUTE | val);
495 }
496
497 /* initialize the output path */
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200498 for (i = path->depth - 1; i > 0; i--) {
499 nid = path->path[i - 1];
500 unmute_and_select(codec, path->path[i], nid, spec->aa_mix_nid);
Takashi Iwai5d417622011-06-20 11:32:27 +0200501 }
502}
503
Joseph Chanc577b8a2006-11-29 15:29:40 +0100504
505static void via_auto_init_multi_out(struct hda_codec *codec)
506{
507 struct via_spec *spec = codec->spec;
508 int i;
509
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200510 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwai5d417622011-06-20 11:32:27 +0200511 via_auto_init_output(codec, spec->autocfg.line_out_pins[i],
512 PIN_OUT, &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100513}
514
515static void via_auto_init_hp_out(struct hda_codec *codec)
516{
517 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100518
Takashi Iwai5d417622011-06-20 11:32:27 +0200519 if (spec->hp_dac_nid)
520 via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
521 &spec->hp_path);
522 else
523 via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
524 &spec->hp_dep_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100525}
526
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200527static void via_auto_init_speaker_out(struct hda_codec *codec)
528{
529 struct via_spec *spec = codec->spec;
530
531 if (spec->autocfg.speaker_outs)
532 via_auto_init_output(codec, spec->autocfg.speaker_pins[0],
533 PIN_OUT, &spec->speaker_path);
534}
535
Takashi Iwaif4a78282011-06-17 18:46:48 +0200536static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200537
Joseph Chanc577b8a2006-11-29 15:29:40 +0100538static void via_auto_init_analog_input(struct hda_codec *codec)
539{
540 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200541 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200542 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200543 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200544 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100545
Takashi Iwai096a8852011-06-20 12:09:02 +0200546 /* init ADCs */
547 for (i = 0; i < spec->num_adc_nids; i++) {
548 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
549 AC_VERB_SET_AMP_GAIN_MUTE,
550 AMP_IN_UNMUTE(0));
551 }
552
553 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200554 for (i = 0; i < cfg->num_inputs; i++) {
555 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200556 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200557 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100558 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200559 ctl = PIN_VREF50;
560 else
561 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100562 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200563 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100564 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200565
566 /* init input-src */
567 for (i = 0; i < spec->num_adc_nids; i++) {
568 const struct hda_input_mux *imux = spec->input_mux;
569 if (!imux || !spec->mux_nids[i])
570 continue;
571 snd_hda_codec_write(codec, spec->mux_nids[i], 0,
572 AC_VERB_SET_CONNECT_SEL,
573 imux->items[spec->cur_mux[i]].index);
574 }
575
576 /* init aa-mixer */
577 if (!spec->aa_mix_nid)
578 return;
579 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
580 ARRAY_SIZE(conn));
581 for (i = 0; i < num_conns; i++) {
582 unsigned int caps = get_wcaps(codec, conn[i]);
583 if (get_wcaps_type(caps) == AC_WID_PIN)
584 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
585 AC_VERB_SET_AMP_GAIN_MUTE,
586 AMP_IN_MUTE(i));
587 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100588}
Lydia Wangf5271102009-10-10 19:07:35 +0800589
590static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
591 unsigned int *affected_parm)
592{
593 unsigned parm;
594 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
595 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
596 >> AC_DEFCFG_MISC_SHIFT
597 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800598 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200599 unsigned present = 0;
600
601 no_presence |= spec->no_pin_power_ctl;
602 if (!no_presence)
603 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200604 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800605 || ((no_presence || present)
606 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800607 *affected_parm = AC_PWRST_D0; /* if it's connected */
608 parm = AC_PWRST_D0;
609 } else
610 parm = AC_PWRST_D3;
611
612 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
613}
614
Takashi Iwai24088a52011-06-17 16:59:21 +0200615static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
616 struct snd_ctl_elem_info *uinfo)
617{
618 static const char * const texts[] = {
619 "Disabled", "Enabled"
620 };
621
622 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
623 uinfo->count = 1;
624 uinfo->value.enumerated.items = 2;
625 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
626 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
627 strcpy(uinfo->value.enumerated.name,
628 texts[uinfo->value.enumerated.item]);
629 return 0;
630}
631
632static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
633 struct snd_ctl_elem_value *ucontrol)
634{
635 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
636 struct via_spec *spec = codec->spec;
637 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
638 return 0;
639}
640
641static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
642 struct snd_ctl_elem_value *ucontrol)
643{
644 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
645 struct via_spec *spec = codec->spec;
646 unsigned int val = !ucontrol->value.enumerated.item[0];
647
648 if (val == spec->no_pin_power_ctl)
649 return 0;
650 spec->no_pin_power_ctl = val;
651 set_widgets_power_state(codec);
652 return 1;
653}
654
655static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
656 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
657 .name = "Dynamic Power-Control",
658 .info = via_pin_power_ctl_info,
659 .get = via_pin_power_ctl_get,
660 .put = via_pin_power_ctl_put,
661};
662
663
Joseph Chanc577b8a2006-11-29 15:29:40 +0100664/*
665 * input MUX handling
666 */
667static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
668 struct snd_ctl_elem_info *uinfo)
669{
670 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
671 struct via_spec *spec = codec->spec;
672 return snd_hda_input_mux_info(spec->input_mux, uinfo);
673}
674
675static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
676 struct snd_ctl_elem_value *ucontrol)
677{
678 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
679 struct via_spec *spec = codec->spec;
680 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
681
682 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
683 return 0;
684}
685
686static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
687 struct snd_ctl_elem_value *ucontrol)
688{
689 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
690 struct via_spec *spec = codec->spec;
691 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800692 int ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100693
Takashi Iwai337b9d02009-07-07 18:18:59 +0200694 if (!spec->mux_nids[adc_idx])
695 return -EINVAL;
Lydia Wanga80e6e32009-10-10 19:07:55 +0800696 /* switch to D0 beofre change index */
697 if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
698 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
699 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
700 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800701
702 ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
703 spec->mux_nids[adc_idx],
704 &spec->cur_mux[adc_idx]);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800705 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800706 set_widgets_power_state(codec);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800707
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800708 return ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100709}
710
Harald Welte0aa62ae2008-09-09 15:58:27 +0800711static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
712 struct snd_ctl_elem_info *uinfo)
713{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200714 static const char * const texts[] = { "OFF", "ON" };
715
716 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
717 uinfo->count = 1;
718 uinfo->value.enumerated.items = 2;
719 if (uinfo->value.enumerated.item >= 2)
720 uinfo->value.enumerated.item = 1;
721 strcpy(uinfo->value.enumerated.name,
722 texts[uinfo->value.enumerated.item]);
723 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800724}
725
726static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
727 struct snd_ctl_elem_value *ucontrol)
728{
729 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800730 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800731
Takashi Iwaiece8d042011-06-19 16:24:21 +0200732 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800733 return 0;
734}
735
Harald Welte0aa62ae2008-09-09 15:58:27 +0800736static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
737 struct snd_ctl_elem_value *ucontrol)
738{
739 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
740 struct via_spec *spec = codec->spec;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200741 hda_nid_t nid, src;
742 int i, idx, num_conns;
743 struct nid_path *path;
744
745 spec->hp_independent_mode = !!ucontrol->value.enumerated.item[0];
746 if (spec->hp_independent_mode)
747 path = &spec->hp_path;
748 else
749 path = &spec->hp_dep_path;
750
751 /* re-route the output path */
752 for (i = path->depth - 1; i > 0; i--) {
753 nid = path->path[i];
754 src = path->path[i - 1];
755 idx = __get_connection_index(codec, nid, src, &num_conns);
756 if (idx < 0)
757 continue;
758 if (num_conns > 1 &&
759 get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
760 snd_hda_codec_write(codec, nid, 0,
761 AC_VERB_SET_CONNECT_SEL, idx);
762 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800763
Lydia Wangce0e5a92011-03-22 16:22:37 +0800764 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800765 set_widgets_power_state(codec);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800766 return 0;
767}
768
Takashi Iwaiece8d042011-06-19 16:24:21 +0200769static const struct snd_kcontrol_new via_hp_mixer = {
770 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
771 .name = "Independent HP",
772 .info = via_independent_hp_info,
773 .get = via_independent_hp_get,
774 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800775};
776
Takashi Iwai3d83e572010-04-14 14:36:23 +0200777static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100778{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200779 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100780 struct snd_kcontrol_new *knew;
781 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100782
Takashi Iwaiece8d042011-06-19 16:24:21 +0200783 nid = spec->autocfg.hp_pins[0];
784 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200785 if (knew == NULL)
786 return -ENOMEM;
787
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100788 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100789
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100790 return 0;
791}
792
Lydia Wang1564b282009-10-10 19:07:52 +0800793static void notify_aa_path_ctls(struct hda_codec *codec)
794{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200795 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800796 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800797
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200798 for (i = 0; i < spec->smart51_nums; i++) {
799 struct snd_kcontrol *ctl;
800 struct snd_ctl_elem_id id;
801 memset(&id, 0, sizeof(id));
802 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
803 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800804 ctl = snd_hda_find_mixer_ctl(codec, id.name);
805 if (ctl)
806 snd_ctl_notify(codec->bus->card,
807 SNDRV_CTL_EVENT_MASK_VALUE,
808 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800809 }
810}
811
812static void mute_aa_path(struct hda_codec *codec, int mute)
813{
814 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200815 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800816 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200817
Lydia Wang1564b282009-10-10 19:07:52 +0800818 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200819 for (i = 0; i < spec->smart51_nums; i++) {
820 if (spec->smart51_idxs[i] < 0)
821 continue;
822 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
823 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800824 HDA_AMP_MUTE, val);
825 }
826}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200827
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200828static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
829{
830 struct via_spec *spec = codec->spec;
831 int i;
832
833 for (i = 0; i < spec->smart51_nums; i++)
834 if (spec->smart51_pins[i] == pin)
835 return true;
836 return false;
837}
838
Lydia Wang1564b282009-10-10 19:07:52 +0800839static int via_smart51_info(struct snd_kcontrol *kcontrol,
840 struct snd_ctl_elem_info *uinfo)
841{
842 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
843 uinfo->count = 1;
844 uinfo->value.integer.min = 0;
845 uinfo->value.integer.max = 1;
846 return 0;
847}
848
849static int via_smart51_get(struct snd_kcontrol *kcontrol,
850 struct snd_ctl_elem_value *ucontrol)
851{
852 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
853 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800854 int on = 1;
855 int i;
856
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200857 for (i = 0; i < spec->smart51_nums; i++) {
858 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwaif4a78282011-06-17 18:46:48 +0200859 unsigned int ctl;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200860 ctl = snd_hda_codec_read(codec, nid, 0,
861 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200862 if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
863 on = 0;
Lydia Wang1564b282009-10-10 19:07:52 +0800864 }
865 *ucontrol->value.integer.value = on;
866 return 0;
867}
868
869static int via_smart51_put(struct snd_kcontrol *kcontrol,
870 struct snd_ctl_elem_value *ucontrol)
871{
872 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
873 struct via_spec *spec = codec->spec;
874 int out_in = *ucontrol->value.integer.value
875 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800876 int i;
877
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200878 for (i = 0; i < spec->smart51_nums; i++) {
879 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200880 unsigned int parm;
881
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200882 parm = snd_hda_codec_read(codec, nid, 0,
883 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
884 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
885 parm |= out_in;
886 snd_hda_codec_write(codec, nid, 0,
887 AC_VERB_SET_PIN_WIDGET_CONTROL,
888 parm);
889 if (out_in == AC_PINCTL_OUT_EN) {
890 mute_aa_path(codec, 1);
891 notify_aa_path_ctls(codec);
892 }
Lydia Wang1564b282009-10-10 19:07:52 +0800893 }
894 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800895 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800896 return 1;
897}
898
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200899static const struct snd_kcontrol_new via_smart51_mixer = {
900 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
901 .name = "Smart 5.1",
902 .count = 1,
903 .info = via_smart51_info,
904 .get = via_smart51_get,
905 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800906};
907
Takashi Iwaif4a78282011-06-17 18:46:48 +0200908static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100909{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200910 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100911
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200912 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800913 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200914 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100915 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100916 return 0;
917}
918
Takashi Iwaiada509e2011-06-20 15:40:19 +0200919/* check AA path's mute status */
920static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800921{
Lydia Wangf5271102009-10-10 19:07:35 +0800922 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200923 const struct hda_amp_list *p;
924 int i, ch, v;
925
926 for (i = 0; i < spec->num_loopbacks; i++) {
927 p = &spec->loopback_list[i];
928 for (ch = 0; ch < 2; ch++) {
929 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
930 p->idx);
931 if (!(v & HDA_AMP_MUTE) && v > 0)
932 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800933 }
934 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200935 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800936}
937
938/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200939static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800940{
941 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200942 bool enable;
943 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800944
Takashi Iwaiada509e2011-06-20 15:40:19 +0200945 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800946
947 /* decide low current mode's verb & parameter */
948 switch (spec->codec_type) {
949 case VT1708B_8CH:
950 case VT1708B_4CH:
951 verb = 0xf70;
952 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
953 break;
954 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800955 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800956 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800957 verb = 0xf73;
958 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
959 break;
960 case VT1702:
961 verb = 0xf73;
962 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
963 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800964 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800965 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800966 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800967 verb = 0xf93;
968 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
969 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800970 default:
971 return; /* other codecs are not supported */
972 }
973 /* send verb */
974 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
975}
976
Joseph Chanc577b8a2006-11-29 15:29:40 +0100977/*
978 * generic initialization of ADC, input mixers and output mixers
979 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200980static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800981 /* power down jack detect function */
982 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +0100983 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100984};
985
Takashi Iwaiada509e2011-06-20 15:40:19 +0200986static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200987{
Takashi Iwaiada509e2011-06-20 15:40:19 +0200988 struct via_spec *spec = codec->spec;
989
990 if (active)
991 spec->num_active_streams++;
992 else
993 spec->num_active_streams--;
994 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200995}
996
Takashi Iwaiece8d042011-06-19 16:24:21 +0200997static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100998 struct hda_codec *codec,
999 struct snd_pcm_substream *substream)
1000{
1001 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001002 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001003
1004 if (!spec->hp_independent_mode)
1005 spec->multiout.hp_nid = spec->hp_dac_nid;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001006 set_stream_active(codec, true);
1007 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1008 hinfo);
1009 if (err < 0) {
1010 spec->multiout.hp_nid = 0;
1011 set_stream_active(codec, false);
1012 return err;
1013 }
1014 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001015}
1016
Takashi Iwaiece8d042011-06-19 16:24:21 +02001017static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001018 struct hda_codec *codec,
1019 struct snd_pcm_substream *substream)
1020{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001021 struct via_spec *spec = codec->spec;
1022
1023 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001024 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001025 return 0;
1026}
1027
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001028static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1029 struct hda_codec *codec,
1030 struct snd_pcm_substream *substream)
1031{
1032 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001033
Takashi Iwaiece8d042011-06-19 16:24:21 +02001034 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001035 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001036 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1037 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001038 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001039 return 0;
1040}
1041
1042static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1043 struct hda_codec *codec,
1044 struct snd_pcm_substream *substream)
1045{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001046 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001047 return 0;
1048}
1049
1050static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1051 struct hda_codec *codec,
1052 unsigned int stream_tag,
1053 unsigned int format,
1054 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001055{
1056 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001057
Takashi Iwaiece8d042011-06-19 16:24:21 +02001058 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1059 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001060 vt1708_start_hp_work(spec);
1061 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001062}
1063
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001064static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1065 struct hda_codec *codec,
1066 unsigned int stream_tag,
1067 unsigned int format,
1068 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001069{
1070 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001071
Takashi Iwaiece8d042011-06-19 16:24:21 +02001072 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1073 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001074 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001075 return 0;
1076}
1077
1078static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1079 struct hda_codec *codec,
1080 struct snd_pcm_substream *substream)
1081{
1082 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001083
Takashi Iwaiece8d042011-06-19 16:24:21 +02001084 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001085 vt1708_stop_hp_work(spec);
1086 return 0;
1087}
1088
1089static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1090 struct hda_codec *codec,
1091 struct snd_pcm_substream *substream)
1092{
1093 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001094
Takashi Iwaiece8d042011-06-19 16:24:21 +02001095 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001096 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001097 return 0;
1098}
1099
Joseph Chanc577b8a2006-11-29 15:29:40 +01001100/*
1101 * Digital out
1102 */
1103static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1104 struct hda_codec *codec,
1105 struct snd_pcm_substream *substream)
1106{
1107 struct via_spec *spec = codec->spec;
1108 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1109}
1110
1111static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1112 struct hda_codec *codec,
1113 struct snd_pcm_substream *substream)
1114{
1115 struct via_spec *spec = codec->spec;
1116 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1117}
1118
Harald Welte5691ec72008-09-15 22:42:26 +08001119static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001120 struct hda_codec *codec,
1121 unsigned int stream_tag,
1122 unsigned int format,
1123 struct snd_pcm_substream *substream)
1124{
1125 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001126 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1127 stream_tag, format, substream);
1128}
Harald Welte5691ec72008-09-15 22:42:26 +08001129
Takashi Iwai9da29272009-05-07 16:31:14 +02001130static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1131 struct hda_codec *codec,
1132 struct snd_pcm_substream *substream)
1133{
1134 struct via_spec *spec = codec->spec;
1135 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001136 return 0;
1137}
1138
Joseph Chanc577b8a2006-11-29 15:29:40 +01001139/*
1140 * Analog capture
1141 */
1142static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1143 struct hda_codec *codec,
1144 unsigned int stream_tag,
1145 unsigned int format,
1146 struct snd_pcm_substream *substream)
1147{
1148 struct via_spec *spec = codec->spec;
1149
1150 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1151 stream_tag, 0, format);
1152 return 0;
1153}
1154
1155static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1156 struct hda_codec *codec,
1157 struct snd_pcm_substream *substream)
1158{
1159 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001160 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001161 return 0;
1162}
1163
Takashi Iwai9af74212011-06-18 16:17:45 +02001164static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001165 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001166 .channels_min = 2,
1167 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001168 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001169 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001170 .open = via_playback_multi_pcm_open,
1171 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001172 .prepare = via_playback_multi_pcm_prepare,
1173 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001174 },
1175};
1176
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001177static const struct hda_pcm_stream via_pcm_hp_playback = {
1178 .substreams = 1,
1179 .channels_min = 2,
1180 .channels_max = 2,
1181 /* NID is set in via_build_pcms */
1182 .ops = {
1183 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001184 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001185 .prepare = via_playback_hp_pcm_prepare,
1186 .cleanup = via_playback_hp_pcm_cleanup
1187 },
1188};
1189
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001190static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001191 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001192 .channels_min = 2,
1193 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001194 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001195 /* We got noisy outputs on the right channel on VT1708 when
1196 * 24bit samples are used. Until any workaround is found,
1197 * disable the 24bit format, so far.
1198 */
1199 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1200 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001201 .open = via_playback_multi_pcm_open,
1202 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001203 .prepare = via_playback_multi_pcm_prepare,
1204 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001205 },
1206};
1207
Takashi Iwai9af74212011-06-18 16:17:45 +02001208static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001209 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001210 .channels_min = 2,
1211 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001212 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001213 .ops = {
1214 .prepare = via_capture_pcm_prepare,
1215 .cleanup = via_capture_pcm_cleanup
1216 },
1217};
1218
Takashi Iwai9af74212011-06-18 16:17:45 +02001219static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001220 .substreams = 1,
1221 .channels_min = 2,
1222 .channels_max = 2,
1223 /* NID is set in via_build_pcms */
1224 .ops = {
1225 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001226 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001227 .prepare = via_dig_playback_pcm_prepare,
1228 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001229 },
1230};
1231
Takashi Iwai9af74212011-06-18 16:17:45 +02001232static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001233 .substreams = 1,
1234 .channels_min = 2,
1235 .channels_max = 2,
1236};
1237
Takashi Iwai370bafb2011-06-20 12:47:45 +02001238/*
1239 * slave controls for virtual master
1240 */
1241static const char * const via_slave_vols[] = {
1242 "Front Playback Volume",
1243 "Surround Playback Volume",
1244 "Center Playback Volume",
1245 "LFE Playback Volume",
1246 "Side Playback Volume",
1247 "Headphone Playback Volume",
1248 "Speaker Playback Volume",
1249 NULL,
1250};
1251
1252static const char * const via_slave_sws[] = {
1253 "Front Playback Switch",
1254 "Surround Playback Switch",
1255 "Center Playback Switch",
1256 "LFE Playback Switch",
1257 "Side Playback Switch",
1258 "Headphone Playback Switch",
1259 "Speaker Playback Switch",
1260 NULL,
1261};
1262
Joseph Chanc577b8a2006-11-29 15:29:40 +01001263static int via_build_controls(struct hda_codec *codec)
1264{
1265 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001266 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001267 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001268
Takashi Iwai24088a52011-06-17 16:59:21 +02001269 if (spec->set_widgets_power_state)
1270 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1271 return -ENOMEM;
1272
Joseph Chanc577b8a2006-11-29 15:29:40 +01001273 for (i = 0; i < spec->num_mixers; i++) {
1274 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1275 if (err < 0)
1276 return err;
1277 }
1278
1279 if (spec->multiout.dig_out_nid) {
1280 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001281 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001282 spec->multiout.dig_out_nid);
1283 if (err < 0)
1284 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001285 err = snd_hda_create_spdif_share_sw(codec,
1286 &spec->multiout);
1287 if (err < 0)
1288 return err;
1289 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001290 }
1291 if (spec->dig_in_nid) {
1292 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1293 if (err < 0)
1294 return err;
1295 }
Lydia Wang17314372009-10-10 19:07:37 +08001296
Takashi Iwai370bafb2011-06-20 12:47:45 +02001297 /* if we have no master control, let's create it */
1298 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1299 unsigned int vmaster_tlv[4];
1300 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1301 HDA_OUTPUT, vmaster_tlv);
1302 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1303 vmaster_tlv, via_slave_vols);
1304 if (err < 0)
1305 return err;
1306 }
1307 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1308 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1309 NULL, via_slave_sws);
1310 if (err < 0)
1311 return err;
1312 }
1313
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001314 /* assign Capture Source enums to NID */
1315 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1316 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001317 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001318 if (err < 0)
1319 return err;
1320 }
1321
Lydia Wang17314372009-10-10 19:07:37 +08001322 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001323 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001324 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001325
Takashi Iwai603c4012008-07-30 15:01:44 +02001326 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001327 return 0;
1328}
1329
1330static int via_build_pcms(struct hda_codec *codec)
1331{
1332 struct via_spec *spec = codec->spec;
1333 struct hda_pcm *info = spec->pcm_rec;
1334
1335 codec->num_pcms = 1;
1336 codec->pcm_info = info;
1337
Takashi Iwai82673bc2011-06-17 16:24:21 +02001338 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1339 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001340 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001341
1342 if (!spec->stream_analog_playback)
1343 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001344 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001345 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001346 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1347 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001348 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1349 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001350
1351 if (!spec->stream_analog_capture)
1352 spec->stream_analog_capture = &via_pcm_analog_capture;
1353 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1354 *spec->stream_analog_capture;
1355 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1356 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1357 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001358
1359 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1360 codec->num_pcms++;
1361 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001362 snprintf(spec->stream_name_digital,
1363 sizeof(spec->stream_name_digital),
1364 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001365 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001366 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001367 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001368 if (!spec->stream_digital_playback)
1369 spec->stream_digital_playback =
1370 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001371 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001372 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001373 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1374 spec->multiout.dig_out_nid;
1375 }
1376 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001377 if (!spec->stream_digital_capture)
1378 spec->stream_digital_capture =
1379 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001380 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001381 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001382 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1383 spec->dig_in_nid;
1384 }
1385 }
1386
Takashi Iwaiece8d042011-06-19 16:24:21 +02001387 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001388 codec->num_pcms++;
1389 info++;
1390 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1391 "%s HP", codec->chip_name);
1392 info->name = spec->stream_name_hp;
1393 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1394 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001395 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001396 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001397 return 0;
1398}
1399
1400static void via_free(struct hda_codec *codec)
1401{
1402 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001403
1404 if (!spec)
1405 return;
1406
Takashi Iwai603c4012008-07-30 15:01:44 +02001407 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001408 vt1708_stop_hp_work(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001409 kfree(codec->spec);
1410}
1411
Takashi Iwai64be2852011-06-17 16:51:39 +02001412/* mute/unmute outputs */
1413static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1414 hda_nid_t *pins, bool mute)
1415{
1416 int i;
1417 for (i = 0; i < num_pins; i++)
1418 snd_hda_codec_write(codec, pins[i], 0,
1419 AC_VERB_SET_PIN_WIDGET_CONTROL,
1420 mute ? 0 : PIN_OUT);
1421}
1422
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001423/* mute internal speaker if line-out is plugged */
1424static void via_line_automute(struct hda_codec *codec, int present)
1425{
1426 struct via_spec *spec = codec->spec;
1427
1428 if (!spec->autocfg.speaker_outs)
1429 return;
1430 if (!present)
1431 present = snd_hda_jack_detect(codec,
1432 spec->autocfg.line_out_pins[0]);
1433 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1434 spec->autocfg.speaker_pins,
1435 present);
1436}
1437
Harald Welte69e52a82008-09-09 15:57:32 +08001438/* mute internal speaker if HP is plugged */
1439static void via_hp_automute(struct hda_codec *codec)
1440{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001441 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001442 struct via_spec *spec = codec->spec;
1443
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001444 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
1445 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai64be2852011-06-17 16:51:39 +02001446 toggle_output_mutes(codec, spec->autocfg.line_outs,
1447 spec->autocfg.line_out_pins,
1448 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001449 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001450 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001451}
1452
Harald Welte69e52a82008-09-09 15:57:32 +08001453static void via_gpio_control(struct hda_codec *codec)
1454{
1455 unsigned int gpio_data;
1456 unsigned int vol_counter;
1457 unsigned int vol;
1458 unsigned int master_vol;
1459
1460 struct via_spec *spec = codec->spec;
1461
1462 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1463 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1464
1465 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1466 0xF84, 0) & 0x3F0000) >> 16;
1467
1468 vol = vol_counter & 0x1F;
1469 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1470 AC_VERB_GET_AMP_GAIN_MUTE,
1471 AC_AMP_GET_INPUT);
1472
1473 if (gpio_data == 0x02) {
1474 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001475 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1476 AC_VERB_SET_PIN_WIDGET_CONTROL,
1477 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001478 if (vol_counter & 0x20) {
1479 /* decrease volume */
1480 if (vol > master_vol)
1481 vol = master_vol;
1482 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1483 0, HDA_AMP_VOLMASK,
1484 master_vol-vol);
1485 } else {
1486 /* increase volume */
1487 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1488 HDA_AMP_VOLMASK,
1489 ((master_vol+vol) > 0x2A) ? 0x2A :
1490 (master_vol+vol));
1491 }
1492 } else if (!(gpio_data & 0x02)) {
1493 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001494 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1495 AC_VERB_SET_PIN_WIDGET_CONTROL,
1496 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001497 }
1498}
1499
1500/* unsolicited event for jack sensing */
1501static void via_unsol_event(struct hda_codec *codec,
1502 unsigned int res)
1503{
1504 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001505
Lydia Wanga34df192009-10-10 19:08:01 +08001506 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001507 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001508
1509 res &= ~VIA_JACK_EVENT;
1510
1511 if (res == VIA_HP_EVENT)
1512 via_hp_automute(codec);
1513 else if (res == VIA_GPIO_EVENT)
1514 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001515 else if (res == VIA_LINE_EVENT)
1516 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001517}
1518
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001519#ifdef SND_HDA_NEEDS_RESUME
1520static int via_suspend(struct hda_codec *codec, pm_message_t state)
1521{
1522 struct via_spec *spec = codec->spec;
1523 vt1708_stop_hp_work(spec);
1524 return 0;
1525}
1526#endif
1527
Takashi Iwaicb53c622007-08-10 17:21:45 +02001528#ifdef CONFIG_SND_HDA_POWER_SAVE
1529static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1530{
1531 struct via_spec *spec = codec->spec;
1532 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1533}
1534#endif
1535
Joseph Chanc577b8a2006-11-29 15:29:40 +01001536/*
1537 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001538
1539static int via_init(struct hda_codec *codec);
1540
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001541static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001542 .build_controls = via_build_controls,
1543 .build_pcms = via_build_pcms,
1544 .init = via_init,
1545 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001546 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001547#ifdef SND_HDA_NEEDS_RESUME
1548 .suspend = via_suspend,
1549#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001550#ifdef CONFIG_SND_HDA_POWER_SAVE
1551 .check_power_status = via_check_power_status,
1552#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001553};
1554
Takashi Iwai4a796162011-06-17 17:53:38 +02001555static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001556{
Takashi Iwai4a796162011-06-17 17:53:38 +02001557 struct via_spec *spec = codec->spec;
1558 int i;
1559
1560 for (i = 0; i < spec->multiout.num_dacs; i++) {
1561 if (spec->multiout.dac_nids[i] == dac)
1562 return false;
1563 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001564 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001565 return false;
1566 return true;
1567}
1568
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001569static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001570 hda_nid_t target_dac, struct nid_path *path,
1571 int depth, int wid_type)
1572{
1573 hda_nid_t conn[8];
1574 int i, nums;
1575
1576 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1577 for (i = 0; i < nums; i++) {
1578 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1579 continue;
1580 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001581 path->path[0] = conn[i];
1582 path->idx[0] = i;
1583 path->depth = 1;
Takashi Iwai4a796162011-06-17 17:53:38 +02001584 return true;
1585 }
1586 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001587 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001588 return false;
1589 for (i = 0; i < nums; i++) {
1590 unsigned int type;
1591 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1592 if (type == AC_WID_AUD_OUT ||
1593 (wid_type != -1 && type != wid_type))
1594 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001595 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai4a796162011-06-17 17:53:38 +02001596 path, depth + 1, AC_WID_AUD_SEL)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001597 path->path[path->depth] = conn[i];
1598 path->idx[path->depth] = i;
1599 path->depth++;
Takashi Iwai4a796162011-06-17 17:53:38 +02001600 return true;
1601 }
1602 }
1603 return false;
1604}
1605
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001606static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1607 hda_nid_t target_dac, struct nid_path *path)
1608{
1609 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1610 path->path[path->depth] = nid;
1611 path->depth++;
1612 return true;
1613 }
1614 return false;
1615}
1616
Takashi Iwai4a796162011-06-17 17:53:38 +02001617static int via_auto_fill_dac_nids(struct hda_codec *codec)
1618{
1619 struct via_spec *spec = codec->spec;
1620 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001621 int i;
1622 hda_nid_t nid;
1623
Joseph Chanc577b8a2006-11-29 15:29:40 +01001624 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001625 spec->multiout.num_dacs = cfg->line_outs;
1626 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001627 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001628 if (!nid)
1629 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001630 if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
1631 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001632 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001633 return 0;
1634}
1635
Takashi Iwai4a796162011-06-17 17:53:38 +02001636static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
1637 hda_nid_t pin, hda_nid_t dac, int chs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001638{
Takashi Iwai4a796162011-06-17 17:53:38 +02001639 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001640 char name[32];
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001641 hda_nid_t nid, sel, conn[8];
1642 int nums, err;
1643
1644 /* check selector widget connected to the pin */
1645 sel = 0;
1646 nums = snd_hda_get_connections(codec, pin, conn, ARRAY_SIZE(conn));
1647 if (nums == 1 && conn[0] != pin)
1648 sel = conn[0];
Takashi Iwai4a796162011-06-17 17:53:38 +02001649
Takashi Iwai8df2a312011-06-21 11:48:29 +02001650 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001651 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001652 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001653 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001654 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1655 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001656 else
1657 nid = 0;
1658 if (nid) {
1659 sprintf(name, "%s Playback Volume", pfx);
1660 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001661 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001662 if (err < 0)
1663 return err;
1664 }
1665
Takashi Iwai8df2a312011-06-21 11:48:29 +02001666 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001667 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001668 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001669 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001670 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1671 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001672 else
1673 nid = 0;
1674 if (nid) {
1675 sprintf(name, "%s Playback Switch", pfx);
1676 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1677 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1678 if (err < 0)
1679 return err;
1680 }
1681 return 0;
1682}
1683
Takashi Iwaif4a78282011-06-17 18:46:48 +02001684static void mangle_smart51(struct hda_codec *codec)
1685{
1686 struct via_spec *spec = codec->spec;
1687 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001688 struct auto_pin_cfg_item *ins = cfg->inputs;
1689 int i, j, nums, attr;
1690 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001691
Takashi Iwai0f98c242011-06-21 12:51:33 +02001692 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1693 nums = 0;
1694 for (i = 0; i < cfg->num_inputs; i++) {
1695 unsigned int def;
1696 if (ins[i].type > AUTO_PIN_LINE_IN)
1697 continue;
1698 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1699 if (snd_hda_get_input_pin_attr(def) != attr)
1700 continue;
1701 for (j = 0; j < nums; j++)
1702 if (ins[pins[j]].type < ins[i].type) {
1703 memmove(pins + j + 1, pins + j,
1704 (nums - j - 1) * sizeof(int));
1705 break;
1706 }
1707 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001708 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001709 }
1710 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001711 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001712 for (i = 0; i < nums; i++) {
1713 hda_nid_t pin = ins[pins[i]].pin;
1714 spec->smart51_pins[spec->smart51_nums++] = pin;
1715 cfg->line_out_pins[cfg->line_outs++] = pin;
1716 if (cfg->line_outs == 3)
1717 break;
1718 }
1719 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001720 }
1721}
1722
Takashi Iwai4a796162011-06-17 17:53:38 +02001723/* add playback controls from the parsed DAC table */
1724static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1725{
1726 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001727 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001728 static const char * const chname[4] = {
1729 "Front", "Surround", "C/LFE", "Side"
1730 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001731 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001732 int old_line_outs;
1733
1734 /* check smart51 */
1735 old_line_outs = cfg->line_outs;
1736 if (cfg->line_outs == 1)
1737 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001738
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001739 err = via_auto_fill_dac_nids(codec);
1740 if (err < 0)
1741 return err;
1742
Takashi Iwai4a796162011-06-17 17:53:38 +02001743 for (i = 0; i < cfg->line_outs; i++) {
1744 hda_nid_t pin, dac;
1745 pin = cfg->line_out_pins[i];
1746 dac = spec->multiout.dac_nids[i];
1747 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001748 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001749 if (i == HDA_CLFE) {
Takashi Iwai4a796162011-06-17 17:53:38 +02001750 err = create_ch_ctls(codec, "Center", pin, dac, 1);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001751 if (err < 0)
1752 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02001753 err = create_ch_ctls(codec, "LFE", pin, dac, 2);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001754 if (err < 0)
1755 return err;
1756 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001757 const char *pfx = chname[i];
1758 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1759 cfg->line_outs == 1)
1760 pfx = "Speaker";
1761 err = create_ch_ctls(codec, pfx, pin, dac, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001762 if (err < 0)
1763 return err;
1764 }
1765 }
1766
Takashi Iwai4a796162011-06-17 17:53:38 +02001767 idx = get_connection_index(codec, spec->aa_mix_nid,
1768 spec->multiout.dac_nids[0]);
1769 if (idx >= 0) {
1770 /* add control to mixer */
1771 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1772 "PCM Playback Volume",
1773 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1774 idx, HDA_INPUT));
1775 if (err < 0)
1776 return err;
1777 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1778 "PCM Playback Switch",
1779 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1780 idx, HDA_INPUT));
1781 if (err < 0)
1782 return err;
1783 }
1784
Takashi Iwaif4a78282011-06-17 18:46:48 +02001785 cfg->line_outs = old_line_outs;
1786
Joseph Chanc577b8a2006-11-29 15:29:40 +01001787 return 0;
1788}
1789
Takashi Iwai4a796162011-06-17 17:53:38 +02001790static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001791{
Takashi Iwai4a796162011-06-17 17:53:38 +02001792 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001793 int err;
1794
1795 if (!pin)
1796 return 0;
1797
Takashi Iwai8df2a312011-06-21 11:48:29 +02001798 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001799 spec->hp_dac_nid = spec->hp_path.path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001800
Takashi Iwaiece8d042011-06-19 16:24:21 +02001801 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001802 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001803 !spec->hp_dac_nid)
1804 return 0;
1805
Takashi Iwaiece8d042011-06-19 16:24:21 +02001806 err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001807 if (err < 0)
1808 return err;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001809
Joseph Chanc577b8a2006-11-29 15:29:40 +01001810 return 0;
1811}
1812
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001813static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1814{
1815 struct via_spec *spec = codec->spec;
1816 hda_nid_t pin, dac;
1817
1818 pin = spec->autocfg.speaker_pins[0];
1819 if (!spec->autocfg.speaker_outs || !pin)
1820 return 0;
1821
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001822 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1823 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001824 spec->multiout.extra_out_nid[0] = dac;
1825 return create_ch_ctls(codec, "Speaker", pin, dac, 3);
1826 }
1827 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001828 &spec->speaker_path))
1829 return create_ch_ctls(codec, "Speaker", pin, 0, 3);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001830
1831 return 0;
1832}
1833
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001834/* look for ADCs */
1835static int via_fill_adcs(struct hda_codec *codec)
1836{
1837 struct via_spec *spec = codec->spec;
1838 hda_nid_t nid = codec->start_nid;
1839 int i;
1840
1841 for (i = 0; i < codec->num_nodes; i++, nid++) {
1842 unsigned int wcaps = get_wcaps(codec, nid);
1843 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1844 continue;
1845 if (wcaps & AC_WCAP_DIGITAL)
1846 continue;
1847 if (!(wcaps & AC_WCAP_CONN_LIST))
1848 continue;
1849 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1850 return -ENOMEM;
1851 spec->adc_nids[spec->num_adc_nids++] = nid;
1852 }
1853 return 0;
1854}
1855
1856static int get_mux_nids(struct hda_codec *codec);
1857
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001858static const struct snd_kcontrol_new via_input_src_ctl = {
1859 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1860 /* The multiple "Capture Source" controls confuse alsamixer
1861 * So call somewhat different..
1862 */
1863 /* .name = "Capture Source", */
1864 .name = "Input Source",
1865 .info = via_mux_enum_info,
1866 .get = via_mux_enum_get,
1867 .put = via_mux_enum_put,
1868};
1869
Takashi Iwai13af8e72011-06-20 14:05:46 +02001870static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
1871{
1872 struct hda_amp_list *list;
1873
1874 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
1875 return;
1876 list = spec->loopback_list + spec->num_loopbacks;
1877 list->nid = mix;
1878 list->dir = HDA_INPUT;
1879 list->idx = idx;
1880 spec->num_loopbacks++;
1881 spec->loopback.amplist = spec->loopback_list;
1882}
Takashi Iwai13af8e72011-06-20 14:05:46 +02001883
Joseph Chanc577b8a2006-11-29 15:29:40 +01001884/* create playback/capture controls for input pins */
Takashi Iwai620e2b22011-06-17 17:19:19 +02001885static int via_auto_create_analog_input_ctls(struct hda_codec *codec,
1886 const struct auto_pin_cfg *cfg)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001887{
Takashi Iwai10a20af2010-09-09 16:28:02 +02001888 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001889 struct hda_input_mux *imux = &spec->private_imux[0];
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001890 int i, j, err, idx, idx2, type, type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02001891 const char *prev_label = NULL;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001892 hda_nid_t cap_nid;
1893 hda_nid_t pin_idxs[8];
1894 int num_idxs;
1895
1896 err = via_fill_adcs(codec);
1897 if (err < 0)
1898 return err;
1899 err = get_mux_nids(codec);
1900 if (err < 0)
1901 return err;
1902 cap_nid = spec->mux_nids[0];
1903
1904 num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
1905 ARRAY_SIZE(pin_idxs));
1906 if (num_idxs <= 0)
1907 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001908
1909 /* for internal loopback recording select */
Takashi Iwaif3268512010-08-30 11:00:19 +02001910 for (idx = 0; idx < num_idxs; idx++) {
Takashi Iwai620e2b22011-06-17 17:19:19 +02001911 if (pin_idxs[idx] == spec->aa_mix_nid) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001912 snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
Takashi Iwaif3268512010-08-30 11:00:19 +02001913 break;
1914 }
1915 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001916
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001917 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001918 const char *label;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001919 type = cfg->inputs[i].type;
Takashi Iwaif3268512010-08-30 11:00:19 +02001920 for (idx = 0; idx < num_idxs; idx++)
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001921 if (pin_idxs[idx] == cfg->inputs[i].pin)
Takashi Iwaif3268512010-08-30 11:00:19 +02001922 break;
1923 if (idx >= num_idxs)
1924 continue;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02001925 label = hda_get_autocfg_input_label(codec, cfg, i);
1926 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001927 type_idx++;
1928 else
1929 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02001930 prev_label = label;
Takashi Iwai620e2b22011-06-17 17:19:19 +02001931 idx2 = get_connection_index(codec, spec->aa_mix_nid,
1932 pin_idxs[idx]);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001933 if (idx2 >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08001934 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwai620e2b22011-06-17 17:19:19 +02001935 idx2, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001936 if (err < 0)
1937 return err;
1938 add_loopback_list(spec, spec->aa_mix_nid, idx2);
1939 }
Takashi Iwai10a20af2010-09-09 16:28:02 +02001940 snd_hda_add_imux_item(imux, label, idx, NULL);
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001941
1942 /* remember the label for smart51 control */
1943 for (j = 0; j < spec->smart51_nums; j++) {
1944 if (spec->smart51_pins[j] == cfg->inputs[i].pin) {
1945 spec->smart51_idxs[j] = idx;
1946 spec->smart51_labels[j] = label;
1947 break;
1948 }
1949 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001950 }
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001951
1952 /* create capture mixer elements */
1953 for (i = 0; i < spec->num_adc_nids; i++) {
1954 hda_nid_t adc = spec->adc_nids[i];
1955 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
1956 "Capture Volume", i,
1957 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1958 HDA_INPUT));
1959 if (err < 0)
1960 return err;
1961 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1962 "Capture Switch", i,
1963 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1964 HDA_INPUT));
1965 if (err < 0)
1966 return err;
1967 }
1968
1969 /* input-source control */
1970 for (i = 0; i < spec->num_adc_nids; i++)
1971 if (!spec->mux_nids[i])
1972 break;
1973 if (i) {
1974 struct snd_kcontrol_new *knew;
1975 knew = via_clone_control(spec, &via_input_src_ctl);
1976 if (!knew)
1977 return -ENOMEM;
1978 knew->count = i;
1979 }
1980
1981 /* mic-boosts */
1982 for (i = 0; i < cfg->num_inputs; i++) {
1983 hda_nid_t pin = cfg->inputs[i].pin;
1984 unsigned int caps;
1985 const char *label;
1986 char name[32];
1987
1988 if (cfg->inputs[i].type != AUTO_PIN_MIC)
1989 continue;
1990 caps = query_amp_caps(codec, pin, HDA_INPUT);
1991 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
1992 continue;
1993 label = hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwai30f7c5d2011-06-21 08:37:41 +02001994 snprintf(name, sizeof(name), "%s Boost Volume", label);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001995 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1996 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
1997 if (err < 0)
1998 return err;
1999 }
2000
Joseph Chanc577b8a2006-11-29 15:29:40 +01002001 return 0;
2002}
2003
Harald Welte76d9b0d2008-09-09 15:50:37 +08002004static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2005{
2006 unsigned int def_conf;
2007 unsigned char seqassoc;
2008
Takashi Iwai2f334f92009-02-20 14:37:42 +01002009 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002010 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2011 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002012 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2013 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2014 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2015 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002016 }
2017
2018 return;
2019}
2020
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002021static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002022 struct snd_ctl_elem_value *ucontrol)
2023{
2024 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2025 struct via_spec *spec = codec->spec;
2026
2027 if (spec->codec_type != VT1708)
2028 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002029 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002030 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002031 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002032 return 0;
2033}
2034
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002035static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002036 struct snd_ctl_elem_value *ucontrol)
2037{
2038 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2039 struct via_spec *spec = codec->spec;
2040 int change;
2041
2042 if (spec->codec_type != VT1708)
2043 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002044 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002045 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002046 == !spec->vt1708_jack_detect;
2047 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002048 mute_aa_path(codec, 1);
2049 notify_aa_path_ctls(codec);
2050 }
2051 return change;
2052}
2053
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002054static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2055 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2056 .name = "Jack Detect",
2057 .count = 1,
2058 .info = snd_ctl_boolean_mono_info,
2059 .get = vt1708_jack_detect_get,
2060 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002061};
2062
Takashi Iwai12daef62011-06-18 17:45:49 +02002063static void fill_dig_outs(struct hda_codec *codec);
2064static void fill_dig_in(struct hda_codec *codec);
2065
2066static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002067{
2068 struct via_spec *spec = codec->spec;
2069 int err;
2070
2071 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2072 if (err < 0)
2073 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002074 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002075 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002076
Takashi Iwai4a796162011-06-17 17:53:38 +02002077 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002078 if (err < 0)
2079 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002080 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002081 if (err < 0)
2082 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002083 err = via_auto_create_speaker_ctls(codec);
2084 if (err < 0)
2085 return err;
Takashi Iwai620e2b22011-06-17 17:19:19 +02002086 err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002087 if (err < 0)
2088 return err;
2089
2090 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2091
Takashi Iwai12daef62011-06-18 17:45:49 +02002092 fill_dig_outs(codec);
2093 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002094
Takashi Iwai603c4012008-07-30 15:01:44 +02002095 if (spec->kctls.list)
2096 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002097
Takashi Iwai096a8852011-06-20 12:09:02 +02002098 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002099
Harald Welte0aa62ae2008-09-09 15:58:27 +08002100 spec->input_mux = &spec->private_imux[0];
2101
Takashi Iwai8df2a312011-06-21 11:48:29 +02002102 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002103 err = via_hp_build(codec);
2104 if (err < 0)
2105 return err;
2106 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002107
Takashi Iwaif4a78282011-06-17 18:46:48 +02002108 err = via_smart51_build(codec);
2109 if (err < 0)
2110 return err;
2111
Takashi Iwai5d417622011-06-20 11:32:27 +02002112 /* assign slave outs */
2113 if (spec->slave_dig_outs[0])
2114 codec->slave_dig_outs = spec->slave_dig_outs;
2115
Joseph Chanc577b8a2006-11-29 15:29:40 +01002116 return 1;
2117}
2118
Takashi Iwai5d417622011-06-20 11:32:27 +02002119static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002120{
Lydia Wang25eaba22009-10-10 19:08:43 +08002121 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002122 if (spec->multiout.dig_out_nid)
2123 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2124 if (spec->slave_dig_outs[0])
2125 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2126}
Lydia Wang25eaba22009-10-10 19:08:43 +08002127
Takashi Iwai5d417622011-06-20 11:32:27 +02002128static void via_auto_init_dig_in(struct hda_codec *codec)
2129{
2130 struct via_spec *spec = codec->spec;
2131 if (!spec->dig_in_nid)
2132 return;
2133 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2134 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2135}
2136
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002137/* initialize the unsolicited events */
2138static void via_auto_init_unsol_event(struct hda_codec *codec)
2139{
2140 struct via_spec *spec = codec->spec;
2141 struct auto_pin_cfg *cfg = &spec->autocfg;
2142 unsigned int ev;
2143 int i;
2144
2145 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2146 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2147 AC_VERB_SET_UNSOLICITED_ENABLE,
2148 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2149
2150 if (cfg->speaker_pins[0])
2151 ev = VIA_LINE_EVENT;
2152 else
2153 ev = 0;
2154 for (i = 0; i < cfg->line_outs; i++) {
2155 if (cfg->line_out_pins[i] &&
2156 is_jack_detectable(codec, cfg->line_out_pins[i]))
2157 snd_hda_codec_write(codec, cfg->line_out_pins[0], 0,
2158 AC_VERB_SET_UNSOLICITED_ENABLE,
2159 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2160 }
2161
2162 for (i = 0; i < cfg->num_inputs; i++) {
2163 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2164 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2165 AC_VERB_SET_UNSOLICITED_ENABLE,
2166 AC_USRSP_EN | VIA_JACK_EVENT);
2167 }
2168}
2169
Takashi Iwai5d417622011-06-20 11:32:27 +02002170static int via_init(struct hda_codec *codec)
2171{
2172 struct via_spec *spec = codec->spec;
2173 int i;
2174
2175 for (i = 0; i < spec->num_iverbs; i++)
2176 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2177
Joseph Chanc577b8a2006-11-29 15:29:40 +01002178 via_auto_init_multi_out(codec);
2179 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002180 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002181 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002182 via_auto_init_dig_outs(codec);
2183 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002184
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002185 via_auto_init_unsol_event(codec);
2186
2187 via_hp_automute(codec);
2188 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002189
Joseph Chanc577b8a2006-11-29 15:29:40 +01002190 return 0;
2191}
2192
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002193static void vt1708_update_hp_jack_state(struct work_struct *work)
2194{
2195 struct via_spec *spec = container_of(work, struct via_spec,
2196 vt1708_hp_work.work);
2197 if (spec->codec_type != VT1708)
2198 return;
2199 /* if jack state toggled */
2200 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002201 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002202 spec->vt1708_hp_present ^= 1;
2203 via_hp_automute(spec->codec);
2204 }
2205 vt1708_start_hp_work(spec);
2206}
2207
Takashi Iwai337b9d02009-07-07 18:18:59 +02002208static int get_mux_nids(struct hda_codec *codec)
2209{
2210 struct via_spec *spec = codec->spec;
2211 hda_nid_t nid, conn[8];
2212 unsigned int type;
2213 int i, n;
2214
2215 for (i = 0; i < spec->num_adc_nids; i++) {
2216 nid = spec->adc_nids[i];
2217 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002218 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002219 if (type == AC_WID_PIN)
2220 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002221 n = snd_hda_get_connections(codec, nid, conn,
2222 ARRAY_SIZE(conn));
2223 if (n <= 0)
2224 break;
2225 if (n > 1) {
2226 spec->mux_nids[i] = nid;
2227 break;
2228 }
2229 nid = conn[0];
2230 }
2231 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002232 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002233}
2234
Joseph Chanc577b8a2006-11-29 15:29:40 +01002235static int patch_vt1708(struct hda_codec *codec)
2236{
2237 struct via_spec *spec;
2238 int err;
2239
2240 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002241 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002242 if (spec == NULL)
2243 return -ENOMEM;
2244
Takashi Iwai620e2b22011-06-17 17:19:19 +02002245 spec->aa_mix_nid = 0x17;
2246
Takashi Iwai12daef62011-06-18 17:45:49 +02002247 /* Add HP and CD pin config connect bit re-config action */
2248 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2249 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2250
Joseph Chanc577b8a2006-11-29 15:29:40 +01002251 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002252 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002253 if (err < 0) {
2254 via_free(codec);
2255 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002256 }
2257
Takashi Iwai12daef62011-06-18 17:45:49 +02002258 /* add jack detect on/off control */
2259 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2260 return -ENOMEM;
2261
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002262 /* disable 32bit format on VT1708 */
2263 if (codec->vendor_id == 0x11061708)
2264 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002265
Joseph Chanc577b8a2006-11-29 15:29:40 +01002266 codec->patch_ops = via_patch_ops;
2267
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002268 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002269 return 0;
2270}
2271
Joseph Chanc577b8a2006-11-29 15:29:40 +01002272static int patch_vt1709_10ch(struct hda_codec *codec)
2273{
2274 struct via_spec *spec;
2275 int err;
2276
2277 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002278 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002279 if (spec == NULL)
2280 return -ENOMEM;
2281
Takashi Iwai620e2b22011-06-17 17:19:19 +02002282 spec->aa_mix_nid = 0x18;
2283
Takashi Iwai12daef62011-06-18 17:45:49 +02002284 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002285 if (err < 0) {
2286 via_free(codec);
2287 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002288 }
2289
Joseph Chanc577b8a2006-11-29 15:29:40 +01002290 codec->patch_ops = via_patch_ops;
2291
Joseph Chanc577b8a2006-11-29 15:29:40 +01002292 return 0;
2293}
2294/*
2295 * generic initialization of ADC, input mixers and output mixers
2296 */
Joseph Chanc577b8a2006-11-29 15:29:40 +01002297static int patch_vt1709_6ch(struct hda_codec *codec)
2298{
2299 struct via_spec *spec;
2300 int err;
2301
2302 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002303 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002304 if (spec == NULL)
2305 return -ENOMEM;
2306
Takashi Iwai620e2b22011-06-17 17:19:19 +02002307 spec->aa_mix_nid = 0x18;
2308
Takashi Iwai12daef62011-06-18 17:45:49 +02002309 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002310 if (err < 0) {
2311 via_free(codec);
2312 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002313 }
2314
Joseph Chanc577b8a2006-11-29 15:29:40 +01002315 codec->patch_ops = via_patch_ops;
2316
Josepch Chanf7278fd2007-12-13 16:40:40 +01002317 return 0;
2318}
2319
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002320static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2321{
2322 struct via_spec *spec = codec->spec;
2323 int imux_is_smixer;
2324 unsigned int parm;
2325 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002326 if ((spec->codec_type != VT1708B_4CH) &&
2327 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002328 is_8ch = 1;
2329
2330 /* SW0 (17h) = stereo mixer */
2331 imux_is_smixer =
2332 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2333 == ((spec->codec_type == VT1708S) ? 5 : 0));
2334 /* inputs */
2335 /* PW 1/2/5 (1ah/1bh/1eh) */
2336 parm = AC_PWRST_D3;
2337 set_pin_power_state(codec, 0x1a, &parm);
2338 set_pin_power_state(codec, 0x1b, &parm);
2339 set_pin_power_state(codec, 0x1e, &parm);
2340 if (imux_is_smixer)
2341 parm = AC_PWRST_D0;
2342 /* SW0 (17h), AIW 0/1 (13h/14h) */
2343 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2344 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2345 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2346
2347 /* outputs */
2348 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2349 parm = AC_PWRST_D3;
2350 set_pin_power_state(codec, 0x19, &parm);
2351 if (spec->smart51_enabled)
2352 set_pin_power_state(codec, 0x1b, &parm);
2353 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2354 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2355
2356 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2357 if (is_8ch) {
2358 parm = AC_PWRST_D3;
2359 set_pin_power_state(codec, 0x22, &parm);
2360 if (spec->smart51_enabled)
2361 set_pin_power_state(codec, 0x1a, &parm);
2362 snd_hda_codec_write(codec, 0x26, 0,
2363 AC_VERB_SET_POWER_STATE, parm);
2364 snd_hda_codec_write(codec, 0x24, 0,
2365 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002366 } else if (codec->vendor_id == 0x11064397) {
2367 /* PW7(23h), SW2(27h), AOW2(25h) */
2368 parm = AC_PWRST_D3;
2369 set_pin_power_state(codec, 0x23, &parm);
2370 if (spec->smart51_enabled)
2371 set_pin_power_state(codec, 0x1a, &parm);
2372 snd_hda_codec_write(codec, 0x27, 0,
2373 AC_VERB_SET_POWER_STATE, parm);
2374 snd_hda_codec_write(codec, 0x25, 0,
2375 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002376 }
2377
2378 /* PW 3/4/7 (1ch/1dh/23h) */
2379 parm = AC_PWRST_D3;
2380 /* force to D0 for internal Speaker */
2381 set_pin_power_state(codec, 0x1c, &parm);
2382 set_pin_power_state(codec, 0x1d, &parm);
2383 if (is_8ch)
2384 set_pin_power_state(codec, 0x23, &parm);
2385
2386 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2387 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2388 imux_is_smixer ? AC_PWRST_D0 : parm);
2389 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2390 if (is_8ch) {
2391 snd_hda_codec_write(codec, 0x25, 0,
2392 AC_VERB_SET_POWER_STATE, parm);
2393 snd_hda_codec_write(codec, 0x27, 0,
2394 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002395 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2396 snd_hda_codec_write(codec, 0x25, 0,
2397 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002398}
2399
Lydia Wang518bf3b2009-10-10 19:07:29 +08002400static int patch_vt1708S(struct hda_codec *codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002401static int patch_vt1708B_8ch(struct hda_codec *codec)
2402{
2403 struct via_spec *spec;
2404 int err;
2405
Lydia Wang518bf3b2009-10-10 19:07:29 +08002406 if (get_codec_type(codec) == VT1708BCE)
2407 return patch_vt1708S(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002408 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002409 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002410 if (spec == NULL)
2411 return -ENOMEM;
2412
Takashi Iwai620e2b22011-06-17 17:19:19 +02002413 spec->aa_mix_nid = 0x16;
2414
Josepch Chanf7278fd2007-12-13 16:40:40 +01002415 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002416 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002417 if (err < 0) {
2418 via_free(codec);
2419 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002420 }
2421
Josepch Chanf7278fd2007-12-13 16:40:40 +01002422 codec->patch_ops = via_patch_ops;
2423
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002424 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2425
Josepch Chanf7278fd2007-12-13 16:40:40 +01002426 return 0;
2427}
2428
2429static int patch_vt1708B_4ch(struct hda_codec *codec)
2430{
2431 struct via_spec *spec;
2432 int err;
2433
2434 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002435 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002436 if (spec == NULL)
2437 return -ENOMEM;
2438
Josepch Chanf7278fd2007-12-13 16:40:40 +01002439 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002440 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002441 if (err < 0) {
2442 via_free(codec);
2443 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002444 }
2445
Josepch Chanf7278fd2007-12-13 16:40:40 +01002446 codec->patch_ops = via_patch_ops;
2447
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002448 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2449
Joseph Chanc577b8a2006-11-29 15:29:40 +01002450 return 0;
2451}
2452
Harald Welted949cac2008-09-09 15:56:01 +08002453/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002454static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002455 /* Enable Mic Boost Volume backdoor */
2456 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002457 /* don't bybass mixer */
2458 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002459 { }
2460};
2461
Takashi Iwai9da29272009-05-07 16:31:14 +02002462/* fill out digital output widgets; one for master and one for slave outputs */
2463static void fill_dig_outs(struct hda_codec *codec)
2464{
2465 struct via_spec *spec = codec->spec;
2466 int i;
2467
2468 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2469 hda_nid_t nid;
2470 int conn;
2471
2472 nid = spec->autocfg.dig_out_pins[i];
2473 if (!nid)
2474 continue;
2475 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2476 if (conn < 1)
2477 continue;
2478 if (!spec->multiout.dig_out_nid)
2479 spec->multiout.dig_out_nid = nid;
2480 else {
2481 spec->slave_dig_outs[0] = nid;
2482 break; /* at most two dig outs */
2483 }
2484 }
2485}
2486
Takashi Iwai12daef62011-06-18 17:45:49 +02002487static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002488{
2489 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002490 hda_nid_t dig_nid;
2491 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002492
Takashi Iwai12daef62011-06-18 17:45:49 +02002493 if (!spec->autocfg.dig_in_pin)
2494 return;
Harald Welted949cac2008-09-09 15:56:01 +08002495
Takashi Iwai12daef62011-06-18 17:45:49 +02002496 dig_nid = codec->start_nid;
2497 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2498 unsigned int wcaps = get_wcaps(codec, dig_nid);
2499 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2500 continue;
2501 if (!(wcaps & AC_WCAP_DIGITAL))
2502 continue;
2503 if (!(wcaps & AC_WCAP_CONN_LIST))
2504 continue;
2505 err = get_connection_index(codec, dig_nid,
2506 spec->autocfg.dig_in_pin);
2507 if (err >= 0) {
2508 spec->dig_in_nid = dig_nid;
2509 break;
2510 }
2511 }
Harald Welted949cac2008-09-09 15:56:01 +08002512}
2513
Lydia Wang6369bcf2009-10-10 19:08:31 +08002514static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2515 int offset, int num_steps, int step_size)
2516{
2517 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2518 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2519 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2520 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2521 (0 << AC_AMPCAP_MUTE_SHIFT));
2522}
2523
Harald Welted949cac2008-09-09 15:56:01 +08002524static int patch_vt1708S(struct hda_codec *codec)
2525{
2526 struct via_spec *spec;
2527 int err;
2528
2529 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002530 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002531 if (spec == NULL)
2532 return -ENOMEM;
2533
Takashi Iwai620e2b22011-06-17 17:19:19 +02002534 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002535 override_mic_boost(codec, 0x1a, 0, 3, 40);
2536 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002537
Harald Welted949cac2008-09-09 15:56:01 +08002538 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002539 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002540 if (err < 0) {
2541 via_free(codec);
2542 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002543 }
2544
Takashi Iwai096a8852011-06-20 12:09:02 +02002545 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002546
Harald Welted949cac2008-09-09 15:56:01 +08002547 codec->patch_ops = via_patch_ops;
2548
Lydia Wang518bf3b2009-10-10 19:07:29 +08002549 /* correct names for VT1708BCE */
2550 if (get_codec_type(codec) == VT1708BCE) {
2551 kfree(codec->chip_name);
2552 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2553 snprintf(codec->bus->card->mixername,
2554 sizeof(codec->bus->card->mixername),
2555 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002556 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002557 /* correct names for VT1705 */
2558 if (codec->vendor_id == 0x11064397) {
2559 kfree(codec->chip_name);
2560 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2561 snprintf(codec->bus->card->mixername,
2562 sizeof(codec->bus->card->mixername),
2563 "%s %s", codec->vendor_name, codec->chip_name);
2564 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002565 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002566 return 0;
2567}
2568
2569/* Patch for VT1702 */
2570
Takashi Iwai096a8852011-06-20 12:09:02 +02002571static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002572 /* mixer enable */
2573 {0x1, 0xF88, 0x3},
2574 /* GPIO 0~2 */
2575 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002576 { }
2577};
2578
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002579static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2580{
2581 int imux_is_smixer =
2582 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2583 unsigned int parm;
2584 /* inputs */
2585 /* PW 1/2/5 (14h/15h/18h) */
2586 parm = AC_PWRST_D3;
2587 set_pin_power_state(codec, 0x14, &parm);
2588 set_pin_power_state(codec, 0x15, &parm);
2589 set_pin_power_state(codec, 0x18, &parm);
2590 if (imux_is_smixer)
2591 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2592 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2593 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2594 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2595 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2596 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2597
2598 /* outputs */
2599 /* PW 3/4 (16h/17h) */
2600 parm = AC_PWRST_D3;
2601 set_pin_power_state(codec, 0x17, &parm);
2602 set_pin_power_state(codec, 0x16, &parm);
2603 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2604 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2605 imux_is_smixer ? AC_PWRST_D0 : parm);
2606 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2607 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2608}
2609
Harald Welted949cac2008-09-09 15:56:01 +08002610static int patch_vt1702(struct hda_codec *codec)
2611{
2612 struct via_spec *spec;
2613 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002614
2615 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002616 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002617 if (spec == NULL)
2618 return -ENOMEM;
2619
Takashi Iwai620e2b22011-06-17 17:19:19 +02002620 spec->aa_mix_nid = 0x1a;
2621
Takashi Iwai12daef62011-06-18 17:45:49 +02002622 /* limit AA path volume to 0 dB */
2623 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2624 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2625 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2626 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2627 (1 << AC_AMPCAP_MUTE_SHIFT));
2628
Harald Welted949cac2008-09-09 15:56:01 +08002629 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002630 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002631 if (err < 0) {
2632 via_free(codec);
2633 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002634 }
2635
Takashi Iwai096a8852011-06-20 12:09:02 +02002636 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002637
Harald Welted949cac2008-09-09 15:56:01 +08002638 codec->patch_ops = via_patch_ops;
2639
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002640 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002641 return 0;
2642}
2643
Lydia Wangeb7188c2009-10-10 19:08:34 +08002644/* Patch for VT1718S */
2645
Takashi Iwai096a8852011-06-20 12:09:02 +02002646static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002647 /* Enable MW0 adjust Gain 5 */
2648 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002649 /* Enable Boost Volume backdoor */
2650 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002651
Lydia Wangeb7188c2009-10-10 19:08:34 +08002652 { }
2653};
2654
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002655static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2656{
2657 struct via_spec *spec = codec->spec;
2658 int imux_is_smixer;
2659 unsigned int parm;
2660 /* MUX6 (1eh) = stereo mixer */
2661 imux_is_smixer =
2662 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2663 /* inputs */
2664 /* PW 5/6/7 (29h/2ah/2bh) */
2665 parm = AC_PWRST_D3;
2666 set_pin_power_state(codec, 0x29, &parm);
2667 set_pin_power_state(codec, 0x2a, &parm);
2668 set_pin_power_state(codec, 0x2b, &parm);
2669 if (imux_is_smixer)
2670 parm = AC_PWRST_D0;
2671 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2672 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2673 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2674 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2675 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2676
2677 /* outputs */
2678 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2679 parm = AC_PWRST_D3;
2680 set_pin_power_state(codec, 0x27, &parm);
2681 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2682 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2683
2684 /* PW2 (26h), AOW2 (ah) */
2685 parm = AC_PWRST_D3;
2686 set_pin_power_state(codec, 0x26, &parm);
2687 if (spec->smart51_enabled)
2688 set_pin_power_state(codec, 0x2b, &parm);
2689 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2690
2691 /* PW0 (24h), AOW0 (8h) */
2692 parm = AC_PWRST_D3;
2693 set_pin_power_state(codec, 0x24, &parm);
2694 if (!spec->hp_independent_mode) /* check for redirected HP */
2695 set_pin_power_state(codec, 0x28, &parm);
2696 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2697 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2698 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2699 imux_is_smixer ? AC_PWRST_D0 : parm);
2700
2701 /* PW1 (25h), AOW1 (9h) */
2702 parm = AC_PWRST_D3;
2703 set_pin_power_state(codec, 0x25, &parm);
2704 if (spec->smart51_enabled)
2705 set_pin_power_state(codec, 0x2a, &parm);
2706 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2707
2708 if (spec->hp_independent_mode) {
2709 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2710 parm = AC_PWRST_D3;
2711 set_pin_power_state(codec, 0x28, &parm);
2712 snd_hda_codec_write(codec, 0x1b, 0,
2713 AC_VERB_SET_POWER_STATE, parm);
2714 snd_hda_codec_write(codec, 0x34, 0,
2715 AC_VERB_SET_POWER_STATE, parm);
2716 snd_hda_codec_write(codec, 0xc, 0,
2717 AC_VERB_SET_POWER_STATE, parm);
2718 }
2719}
2720
Lydia Wangeb7188c2009-10-10 19:08:34 +08002721static int patch_vt1718S(struct hda_codec *codec)
2722{
2723 struct via_spec *spec;
2724 int err;
2725
2726 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002727 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002728 if (spec == NULL)
2729 return -ENOMEM;
2730
Takashi Iwai620e2b22011-06-17 17:19:19 +02002731 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002732 override_mic_boost(codec, 0x2b, 0, 3, 40);
2733 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002734
Lydia Wangeb7188c2009-10-10 19:08:34 +08002735 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002736 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002737 if (err < 0) {
2738 via_free(codec);
2739 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002740 }
2741
Takashi Iwai096a8852011-06-20 12:09:02 +02002742 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002743
Lydia Wangeb7188c2009-10-10 19:08:34 +08002744 codec->patch_ops = via_patch_ops;
2745
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002746 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2747
Lydia Wangeb7188c2009-10-10 19:08:34 +08002748 return 0;
2749}
Lydia Wangf3db4232009-10-10 19:08:41 +08002750
2751/* Patch for VT1716S */
2752
2753static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2754 struct snd_ctl_elem_info *uinfo)
2755{
2756 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2757 uinfo->count = 1;
2758 uinfo->value.integer.min = 0;
2759 uinfo->value.integer.max = 1;
2760 return 0;
2761}
2762
2763static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2764 struct snd_ctl_elem_value *ucontrol)
2765{
2766 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2767 int index = 0;
2768
2769 index = snd_hda_codec_read(codec, 0x26, 0,
2770 AC_VERB_GET_CONNECT_SEL, 0);
2771 if (index != -1)
2772 *ucontrol->value.integer.value = index;
2773
2774 return 0;
2775}
2776
2777static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2778 struct snd_ctl_elem_value *ucontrol)
2779{
2780 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2781 struct via_spec *spec = codec->spec;
2782 int index = *ucontrol->value.integer.value;
2783
2784 snd_hda_codec_write(codec, 0x26, 0,
2785 AC_VERB_SET_CONNECT_SEL, index);
2786 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002787 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002788 return 1;
2789}
2790
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002791static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002792 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2793 {
2794 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2795 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002796 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08002797 .count = 1,
2798 .info = vt1716s_dmic_info,
2799 .get = vt1716s_dmic_get,
2800 .put = vt1716s_dmic_put,
2801 },
2802 {} /* end */
2803};
2804
2805
2806/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002807static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002808 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
2809 { } /* end */
2810};
2811
Takashi Iwai096a8852011-06-20 12:09:02 +02002812static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002813 /* Enable Boost Volume backdoor */
2814 {0x1, 0xf8a, 0x80},
2815 /* don't bybass mixer */
2816 {0x1, 0xf88, 0xc0},
2817 /* Enable mono output */
2818 {0x1, 0xf90, 0x08},
2819 { }
2820};
2821
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002822static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
2823{
2824 struct via_spec *spec = codec->spec;
2825 int imux_is_smixer;
2826 unsigned int parm;
2827 unsigned int mono_out, present;
2828 /* SW0 (17h) = stereo mixer */
2829 imux_is_smixer =
2830 (snd_hda_codec_read(codec, 0x17, 0,
2831 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
2832 /* inputs */
2833 /* PW 1/2/5 (1ah/1bh/1eh) */
2834 parm = AC_PWRST_D3;
2835 set_pin_power_state(codec, 0x1a, &parm);
2836 set_pin_power_state(codec, 0x1b, &parm);
2837 set_pin_power_state(codec, 0x1e, &parm);
2838 if (imux_is_smixer)
2839 parm = AC_PWRST_D0;
2840 /* SW0 (17h), AIW0(13h) */
2841 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2842 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2843
2844 parm = AC_PWRST_D3;
2845 set_pin_power_state(codec, 0x1e, &parm);
2846 /* PW11 (22h) */
2847 if (spec->dmic_enabled)
2848 set_pin_power_state(codec, 0x22, &parm);
2849 else
2850 snd_hda_codec_write(codec, 0x22, 0,
2851 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
2852
2853 /* SW2(26h), AIW1(14h) */
2854 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
2855 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2856
2857 /* outputs */
2858 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2859 parm = AC_PWRST_D3;
2860 set_pin_power_state(codec, 0x19, &parm);
2861 /* Smart 5.1 PW2(1bh) */
2862 if (spec->smart51_enabled)
2863 set_pin_power_state(codec, 0x1b, &parm);
2864 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2865 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2866
2867 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
2868 parm = AC_PWRST_D3;
2869 set_pin_power_state(codec, 0x23, &parm);
2870 /* Smart 5.1 PW1(1ah) */
2871 if (spec->smart51_enabled)
2872 set_pin_power_state(codec, 0x1a, &parm);
2873 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
2874
2875 /* Smart 5.1 PW5(1eh) */
2876 if (spec->smart51_enabled)
2877 set_pin_power_state(codec, 0x1e, &parm);
2878 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
2879
2880 /* Mono out */
2881 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
2882 present = snd_hda_jack_detect(codec, 0x1c);
2883
2884 if (present)
2885 mono_out = 0;
2886 else {
2887 present = snd_hda_jack_detect(codec, 0x1d);
2888 if (!spec->hp_independent_mode && present)
2889 mono_out = 0;
2890 else
2891 mono_out = 1;
2892 }
2893 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
2894 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
2895 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
2896 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
2897
2898 /* PW 3/4 (1ch/1dh) */
2899 parm = AC_PWRST_D3;
2900 set_pin_power_state(codec, 0x1c, &parm);
2901 set_pin_power_state(codec, 0x1d, &parm);
2902 /* HP Independent Mode, power on AOW3 */
2903 if (spec->hp_independent_mode)
2904 snd_hda_codec_write(codec, 0x25, 0,
2905 AC_VERB_SET_POWER_STATE, parm);
2906
2907 /* force to D0 for internal Speaker */
2908 /* MW0 (16h), AOW0 (10h) */
2909 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2910 imux_is_smixer ? AC_PWRST_D0 : parm);
2911 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
2912 mono_out ? AC_PWRST_D0 : parm);
2913}
2914
Lydia Wangf3db4232009-10-10 19:08:41 +08002915static int patch_vt1716S(struct hda_codec *codec)
2916{
2917 struct via_spec *spec;
2918 int err;
2919
2920 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002921 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002922 if (spec == NULL)
2923 return -ENOMEM;
2924
Takashi Iwai620e2b22011-06-17 17:19:19 +02002925 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002926 override_mic_boost(codec, 0x1a, 0, 3, 40);
2927 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002928
Lydia Wangf3db4232009-10-10 19:08:41 +08002929 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002930 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002931 if (err < 0) {
2932 via_free(codec);
2933 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08002934 }
2935
Takashi Iwai096a8852011-06-20 12:09:02 +02002936 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08002937
Lydia Wangf3db4232009-10-10 19:08:41 +08002938 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
2939 spec->num_mixers++;
2940
2941 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
2942
2943 codec->patch_ops = via_patch_ops;
2944
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002945 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08002946 return 0;
2947}
Lydia Wang25eaba22009-10-10 19:08:43 +08002948
2949/* for vt2002P */
2950
Takashi Iwai096a8852011-06-20 12:09:02 +02002951static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08002952 /* Class-D speaker related verbs */
2953 {0x1, 0xfe0, 0x4},
2954 {0x1, 0xfe9, 0x80},
2955 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08002956 /* Enable Boost Volume backdoor */
2957 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08002958 /* Enable AOW0 to MW9 */
2959 {0x1, 0xfb8, 0x88},
2960 { }
2961};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002962
Takashi Iwai096a8852011-06-20 12:09:02 +02002963static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08002964 /* Enable Boost Volume backdoor */
2965 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08002966 /* Enable AOW0 to MW9 */
2967 {0x1, 0xfb8, 0x88},
2968 { }
2969};
Lydia Wang25eaba22009-10-10 19:08:43 +08002970
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002971static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
2972{
2973 struct via_spec *spec = codec->spec;
2974 int imux_is_smixer;
2975 unsigned int parm;
2976 unsigned int present;
2977 /* MUX9 (1eh) = stereo mixer */
2978 imux_is_smixer =
2979 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2980 /* inputs */
2981 /* PW 5/6/7 (29h/2ah/2bh) */
2982 parm = AC_PWRST_D3;
2983 set_pin_power_state(codec, 0x29, &parm);
2984 set_pin_power_state(codec, 0x2a, &parm);
2985 set_pin_power_state(codec, 0x2b, &parm);
2986 parm = AC_PWRST_D0;
2987 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
2988 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2989 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2990 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2991 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2992
2993 /* outputs */
2994 /* AOW0 (8h)*/
2995 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2996
Lydia Wang118909562011-03-23 17:57:34 +08002997 if (spec->codec_type == VT1802) {
2998 /* PW4 (28h), MW4 (18h), MUX4(38h) */
2999 parm = AC_PWRST_D3;
3000 set_pin_power_state(codec, 0x28, &parm);
3001 snd_hda_codec_write(codec, 0x18, 0,
3002 AC_VERB_SET_POWER_STATE, parm);
3003 snd_hda_codec_write(codec, 0x38, 0,
3004 AC_VERB_SET_POWER_STATE, parm);
3005 } else {
3006 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3007 parm = AC_PWRST_D3;
3008 set_pin_power_state(codec, 0x26, &parm);
3009 snd_hda_codec_write(codec, 0x1c, 0,
3010 AC_VERB_SET_POWER_STATE, parm);
3011 snd_hda_codec_write(codec, 0x37, 0,
3012 AC_VERB_SET_POWER_STATE, parm);
3013 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003014
Lydia Wang118909562011-03-23 17:57:34 +08003015 if (spec->codec_type == VT1802) {
3016 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3017 parm = AC_PWRST_D3;
3018 set_pin_power_state(codec, 0x25, &parm);
3019 snd_hda_codec_write(codec, 0x15, 0,
3020 AC_VERB_SET_POWER_STATE, parm);
3021 snd_hda_codec_write(codec, 0x35, 0,
3022 AC_VERB_SET_POWER_STATE, parm);
3023 } else {
3024 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3025 parm = AC_PWRST_D3;
3026 set_pin_power_state(codec, 0x25, &parm);
3027 snd_hda_codec_write(codec, 0x19, 0,
3028 AC_VERB_SET_POWER_STATE, parm);
3029 snd_hda_codec_write(codec, 0x35, 0,
3030 AC_VERB_SET_POWER_STATE, parm);
3031 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003032
3033 if (spec->hp_independent_mode)
3034 snd_hda_codec_write(codec, 0x9, 0,
3035 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3036
3037 /* Class-D */
3038 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3039 present = snd_hda_jack_detect(codec, 0x25);
3040
3041 parm = AC_PWRST_D3;
3042 set_pin_power_state(codec, 0x24, &parm);
3043 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003044 if (spec->codec_type == VT1802)
3045 snd_hda_codec_write(codec, 0x14, 0,
3046 AC_VERB_SET_POWER_STATE, parm);
3047 else
3048 snd_hda_codec_write(codec, 0x18, 0,
3049 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003050 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3051
3052 /* Mono Out */
3053 present = snd_hda_jack_detect(codec, 0x26);
3054
3055 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003056 if (spec->codec_type == VT1802) {
3057 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3058 snd_hda_codec_write(codec, 0x33, 0,
3059 AC_VERB_SET_POWER_STATE, parm);
3060 snd_hda_codec_write(codec, 0x1c, 0,
3061 AC_VERB_SET_POWER_STATE, parm);
3062 snd_hda_codec_write(codec, 0x3c, 0,
3063 AC_VERB_SET_POWER_STATE, parm);
3064 } else {
3065 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3066 snd_hda_codec_write(codec, 0x31, 0,
3067 AC_VERB_SET_POWER_STATE, parm);
3068 snd_hda_codec_write(codec, 0x17, 0,
3069 AC_VERB_SET_POWER_STATE, parm);
3070 snd_hda_codec_write(codec, 0x3b, 0,
3071 AC_VERB_SET_POWER_STATE, parm);
3072 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003073 /* MW9 (21h) */
3074 if (imux_is_smixer || !is_aa_path_mute(codec))
3075 snd_hda_codec_write(codec, 0x21, 0,
3076 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3077 else
3078 snd_hda_codec_write(codec, 0x21, 0,
3079 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3080}
Lydia Wang25eaba22009-10-10 19:08:43 +08003081
3082/* patch for vt2002P */
3083static int patch_vt2002P(struct hda_codec *codec)
3084{
3085 struct via_spec *spec;
3086 int err;
3087
3088 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003089 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003090 if (spec == NULL)
3091 return -ENOMEM;
3092
Takashi Iwai620e2b22011-06-17 17:19:19 +02003093 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003094 override_mic_boost(codec, 0x2b, 0, 3, 40);
3095 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003096
Lydia Wang25eaba22009-10-10 19:08:43 +08003097 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003098 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003099 if (err < 0) {
3100 via_free(codec);
3101 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003102 }
3103
Lydia Wang118909562011-03-23 17:57:34 +08003104 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003105 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003106 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003107 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003108
Lydia Wang25eaba22009-10-10 19:08:43 +08003109 codec->patch_ops = via_patch_ops;
3110
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003111 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003112 return 0;
3113}
Lydia Wangab6734e2009-10-10 19:08:46 +08003114
3115/* for vt1812 */
3116
Takashi Iwai096a8852011-06-20 12:09:02 +02003117static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003118 /* Enable Boost Volume backdoor */
3119 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003120 /* Enable AOW0 to MW9 */
3121 {0x1, 0xfb8, 0xa8},
3122 { }
3123};
3124
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003125static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3126{
3127 struct via_spec *spec = codec->spec;
3128 int imux_is_smixer =
3129 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3130 unsigned int parm;
3131 unsigned int present;
3132 /* MUX10 (1eh) = stereo mixer */
3133 imux_is_smixer =
3134 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3135 /* inputs */
3136 /* PW 5/6/7 (29h/2ah/2bh) */
3137 parm = AC_PWRST_D3;
3138 set_pin_power_state(codec, 0x29, &parm);
3139 set_pin_power_state(codec, 0x2a, &parm);
3140 set_pin_power_state(codec, 0x2b, &parm);
3141 parm = AC_PWRST_D0;
3142 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3143 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3144 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3145 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3146 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3147
3148 /* outputs */
3149 /* AOW0 (8h)*/
3150 snd_hda_codec_write(codec, 0x8, 0,
3151 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3152
3153 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3154 parm = AC_PWRST_D3;
3155 set_pin_power_state(codec, 0x28, &parm);
3156 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3157 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3158
3159 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3160 parm = AC_PWRST_D3;
3161 set_pin_power_state(codec, 0x25, &parm);
3162 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3163 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3164 if (spec->hp_independent_mode)
3165 snd_hda_codec_write(codec, 0x9, 0,
3166 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3167
3168 /* Internal Speaker */
3169 /* PW0 (24h), MW0(14h), MUX0(34h) */
3170 present = snd_hda_jack_detect(codec, 0x25);
3171
3172 parm = AC_PWRST_D3;
3173 set_pin_power_state(codec, 0x24, &parm);
3174 if (present) {
3175 snd_hda_codec_write(codec, 0x14, 0,
3176 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3177 snd_hda_codec_write(codec, 0x34, 0,
3178 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3179 } else {
3180 snd_hda_codec_write(codec, 0x14, 0,
3181 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3182 snd_hda_codec_write(codec, 0x34, 0,
3183 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3184 }
3185
3186
3187 /* Mono Out */
3188 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3189 present = snd_hda_jack_detect(codec, 0x28);
3190
3191 parm = AC_PWRST_D3;
3192 set_pin_power_state(codec, 0x31, &parm);
3193 if (present) {
3194 snd_hda_codec_write(codec, 0x1c, 0,
3195 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3196 snd_hda_codec_write(codec, 0x3c, 0,
3197 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3198 snd_hda_codec_write(codec, 0x3e, 0,
3199 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3200 } else {
3201 snd_hda_codec_write(codec, 0x1c, 0,
3202 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3203 snd_hda_codec_write(codec, 0x3c, 0,
3204 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3205 snd_hda_codec_write(codec, 0x3e, 0,
3206 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3207 }
3208
3209 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3210 parm = AC_PWRST_D3;
3211 set_pin_power_state(codec, 0x33, &parm);
3212 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3213 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3214
3215}
Lydia Wangab6734e2009-10-10 19:08:46 +08003216
3217/* patch for vt1812 */
3218static int patch_vt1812(struct hda_codec *codec)
3219{
3220 struct via_spec *spec;
3221 int err;
3222
3223 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003224 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003225 if (spec == NULL)
3226 return -ENOMEM;
3227
Takashi Iwai620e2b22011-06-17 17:19:19 +02003228 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003229 override_mic_boost(codec, 0x2b, 0, 3, 40);
3230 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003231
Lydia Wangab6734e2009-10-10 19:08:46 +08003232 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003233 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003234 if (err < 0) {
3235 via_free(codec);
3236 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003237 }
3238
Takashi Iwai096a8852011-06-20 12:09:02 +02003239 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003240
Lydia Wangab6734e2009-10-10 19:08:46 +08003241 codec->patch_ops = via_patch_ops;
3242
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003243 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003244 return 0;
3245}
3246
Joseph Chanc577b8a2006-11-29 15:29:40 +01003247/*
3248 * patch entries
3249 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003250static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003251 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3252 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3253 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3254 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3255 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003256 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003257 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003258 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003259 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003260 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003261 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003262 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003263 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003264 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003265 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003266 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003267 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003268 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003269 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003270 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003271 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003272 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003273 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003274 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003275 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003276 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003277 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003278 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003279 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003280 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003281 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003282 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003283 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003284 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003285 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003286 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003287 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003288 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003289 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003290 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003291 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003292 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003293 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003294 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003295 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003296 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003297 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003298 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003299 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003300 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003301 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003302 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003303 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003304 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003305 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003306 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003307 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003308 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003309 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003310 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003311 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003312 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003313 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003314 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003315 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003316 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003317 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003318 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003319 { .id = 0x11060428, .name = "VT1718S",
3320 .patch = patch_vt1718S},
3321 { .id = 0x11064428, .name = "VT1718S",
3322 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003323 { .id = 0x11060441, .name = "VT2020",
3324 .patch = patch_vt1718S},
3325 { .id = 0x11064441, .name = "VT1828S",
3326 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003327 { .id = 0x11060433, .name = "VT1716S",
3328 .patch = patch_vt1716S},
3329 { .id = 0x1106a721, .name = "VT1716S",
3330 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003331 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3332 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003333 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003334 { .id = 0x11060440, .name = "VT1818S",
3335 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003336 { .id = 0x11060446, .name = "VT1802",
3337 .patch = patch_vt2002P},
3338 { .id = 0x11068446, .name = "VT1802",
3339 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003340 {} /* terminator */
3341};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003342
3343MODULE_ALIAS("snd-hda-codec-id:1106*");
3344
3345static struct hda_codec_preset_list via_list = {
3346 .preset = snd_hda_preset_via,
3347 .owner = THIS_MODULE,
3348};
3349
3350MODULE_LICENSE("GPL");
3351MODULE_DESCRIPTION("VIA HD-audio codec");
3352
3353static int __init patch_via_init(void)
3354{
3355 return snd_hda_add_codec_preset(&via_list);
3356}
3357
3358static void __exit patch_via_exit(void)
3359{
3360 snd_hda_delete_codec_preset(&via_list);
3361}
3362
3363module_init(patch_via_init)
3364module_exit(patch_via_exit)