blob: e445a4d247787dc2d677dbc8a45645bceb3eb3ae [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 */
141 const struct hda_input_mux *hp_mux;
142 unsigned int hp_independent_mode;
143 unsigned int hp_independent_mode_index;
Lydia Wangf3db4232009-10-10 19:08:41 +0800144 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200145 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800146 enum VIA_HDA_CODEC codec_type;
147
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200148 /* smart51 setup */
149 unsigned int smart51_nums;
150 hda_nid_t smart51_pins[2];
151 int smart51_idxs[2];
152 const char *smart51_labels[2];
153 unsigned int smart51_enabled;
154
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800155 /* work to check hp jack state */
156 struct hda_codec *codec;
157 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200158 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800159 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800160
161 void (*set_widgets_power_state)(struct hda_codec *codec);
162
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800163 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200164 int num_loopbacks;
165 struct hda_amp_list loopback_list[8];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800166};
167
Lydia Wang0341ccd2011-03-22 16:25:03 +0800168static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100169static struct via_spec * via_new_spec(struct hda_codec *codec)
170{
171 struct via_spec *spec;
172
173 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
174 if (spec == NULL)
175 return NULL;
176
177 codec->spec = spec;
178 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800179 spec->codec_type = get_codec_type(codec);
180 /* VT1708BCE & VT1708S are almost same */
181 if (spec->codec_type == VT1708BCE)
182 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100183 return spec;
184}
185
Lydia Wang744ff5f2009-10-10 19:07:26 +0800186static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800187{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800188 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800189 u16 ven_id = vendor_id >> 16;
190 u16 dev_id = vendor_id & 0xffff;
191 enum VIA_HDA_CODEC codec_type;
192
193 /* get codec type */
194 if (ven_id != 0x1106)
195 codec_type = UNKNOWN;
196 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
197 codec_type = VT1708;
198 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
199 codec_type = VT1709_10CH;
200 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
201 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800202 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800203 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800204 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
205 codec_type = VT1708BCE;
206 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800207 codec_type = VT1708B_4CH;
208 else if ((dev_id & 0xfff) == 0x397
209 && (dev_id >> 12) < 8)
210 codec_type = VT1708S;
211 else if ((dev_id & 0xfff) == 0x398
212 && (dev_id >> 12) < 8)
213 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800214 else if ((dev_id & 0xfff) == 0x428
215 && (dev_id >> 12) < 8)
216 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800217 else if (dev_id == 0x0433 || dev_id == 0xa721)
218 codec_type = VT1716S;
Lydia Wangbb3c6bf2009-10-10 19:08:39 +0800219 else if (dev_id == 0x0441 || dev_id == 0x4441)
220 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800221 else if (dev_id == 0x0438 || dev_id == 0x4438)
222 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800223 else if (dev_id == 0x0448)
224 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800225 else if (dev_id == 0x0440)
226 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800227 else if ((dev_id & 0xfff) == 0x446)
228 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800229 else
230 codec_type = UNKNOWN;
231 return codec_type;
232};
233
Lydia Wangec7e7e42011-03-24 12:43:44 +0800234#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800235#define VIA_HP_EVENT 0x01
236#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200237#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800238
Joseph Chanc577b8a2006-11-29 15:29:40 +0100239enum {
240 VIA_CTL_WIDGET_VOL,
241 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800242 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100243};
244
Takashi Iwaiada509e2011-06-20 15:40:19 +0200245static void analog_low_current_mode(struct hda_codec *codec);
246static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800247
248static void vt1708_start_hp_work(struct via_spec *spec)
249{
250 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
251 return;
252 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200253 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800254 if (!delayed_work_pending(&spec->vt1708_hp_work))
255 schedule_delayed_work(&spec->vt1708_hp_work,
256 msecs_to_jiffies(100));
257}
258
259static void vt1708_stop_hp_work(struct via_spec *spec)
260{
261 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
262 return;
263 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
264 && !is_aa_path_mute(spec->codec))
265 return;
266 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200267 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100268 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800269}
Lydia Wangf5271102009-10-10 19:07:35 +0800270
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800271static void set_widgets_power_state(struct hda_codec *codec)
272{
273 struct via_spec *spec = codec->spec;
274 if (spec->set_widgets_power_state)
275 spec->set_widgets_power_state(codec);
276}
Lydia Wang25eaba22009-10-10 19:08:43 +0800277
Lydia Wangf5271102009-10-10 19:07:35 +0800278static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
279 struct snd_ctl_elem_value *ucontrol)
280{
281 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
282 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
283
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800284 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200285 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800286 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
287 if (is_aa_path_mute(codec))
288 vt1708_start_hp_work(codec->spec);
289 else
290 vt1708_stop_hp_work(codec->spec);
291 }
Lydia Wangf5271102009-10-10 19:07:35 +0800292 return change;
293}
294
295/* modify .put = snd_hda_mixer_amp_switch_put */
296#define ANALOG_INPUT_MUTE \
297 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
298 .name = NULL, \
299 .index = 0, \
300 .info = snd_hda_mixer_amp_switch_info, \
301 .get = snd_hda_mixer_amp_switch_get, \
302 .put = analog_input_switch_put, \
303 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
304
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200305static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100306 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
307 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800308 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100309};
310
Lydia Wangab6734e2009-10-10 19:08:46 +0800311
Joseph Chanc577b8a2006-11-29 15:29:40 +0100312/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200313static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
314 const struct snd_kcontrol_new *tmpl,
315 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100316{
317 struct snd_kcontrol_new *knew;
318
Takashi Iwai603c4012008-07-30 15:01:44 +0200319 snd_array_init(&spec->kctls, sizeof(*knew), 32);
320 knew = snd_array_new(&spec->kctls);
321 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200322 return NULL;
323 *knew = *tmpl;
324 if (!name)
325 name = tmpl->name;
326 if (name) {
327 knew->name = kstrdup(name, GFP_KERNEL);
328 if (!knew->name)
329 return NULL;
330 }
331 return knew;
332}
333
334static int __via_add_control(struct via_spec *spec, int type, const char *name,
335 int idx, unsigned long val)
336{
337 struct snd_kcontrol_new *knew;
338
339 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
340 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100341 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200342 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100343 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100344 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100345 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100346 return 0;
347}
348
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200349#define via_add_control(spec, type, name, val) \
350 __via_add_control(spec, type, name, 0, val)
351
Takashi Iwai291c9e32011-06-17 16:15:26 +0200352#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100353
Takashi Iwai603c4012008-07-30 15:01:44 +0200354static void via_free_kctls(struct hda_codec *codec)
355{
356 struct via_spec *spec = codec->spec;
357
358 if (spec->kctls.list) {
359 struct snd_kcontrol_new *kctl = spec->kctls.list;
360 int i;
361 for (i = 0; i < spec->kctls.used; i++)
362 kfree(kctl[i].name);
363 }
364 snd_array_free(&spec->kctls);
365}
366
Joseph Chanc577b8a2006-11-29 15:29:40 +0100367/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800368static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200369 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100370{
371 char name[32];
372 int err;
373
374 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200375 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100376 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
377 if (err < 0)
378 return err;
379 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200380 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100381 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
382 if (err < 0)
383 return err;
384 return 0;
385}
386
Takashi Iwai5d417622011-06-20 11:32:27 +0200387/* return the index of the given widget nid as the source of mux;
388 * return -1 if not found;
389 * if num_conns is non-NULL, set the total number of connections
390 */
391static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux,
392 hda_nid_t nid, int *num_conns)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100393{
Takashi Iwai5d417622011-06-20 11:32:27 +0200394 hda_nid_t conn[HDA_MAX_NUM_INPUTS];
395 int i, nums;
396
397 nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
398 if (num_conns)
399 *num_conns = nums;
400 for (i = 0; i < nums; i++)
401 if (conn[i] == nid)
402 return i;
403 return -1;
404}
405
406#define get_connection_index(codec, mux, nid) \
407 __get_connection_index(codec, mux, nid, NULL)
408
409/* unmute input amp and select the specificed source */
410static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid,
411 hda_nid_t src, hda_nid_t mix)
412{
413 int idx, num_conns;
414
415 idx = __get_connection_index(codec, nid, src, &num_conns);
416 if (idx < 0)
417 return;
418
419 /* select the route explicitly when multiple connections exist */
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200420 if (num_conns > 1 &&
421 get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
Lydia Wang377ff312009-10-10 19:08:55 +0800422 snd_hda_codec_write(codec, nid, 0,
Takashi Iwai5d417622011-06-20 11:32:27 +0200423 AC_VERB_SET_CONNECT_SEL, idx);
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200424
Takashi Iwai5d417622011-06-20 11:32:27 +0200425 /* unmute if the input amp is present */
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200426 if (query_amp_caps(codec, nid, HDA_INPUT) &
427 (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE))
428 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
429 AMP_IN_UNMUTE(idx));
430
431 /* unmute the src output */
432 if (query_amp_caps(codec, src, HDA_OUTPUT) &
433 (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE))
434 snd_hda_codec_write(codec, src, 0, AC_VERB_SET_AMP_GAIN_MUTE,
435 AMP_OUT_UNMUTE);
Takashi Iwai5d417622011-06-20 11:32:27 +0200436
437 /* unmute AA-path if present */
438 if (!mix)
439 return;
440 idx = __get_connection_index(codec, nid, mix, NULL);
441 if (idx >= 0)
442 snd_hda_codec_write(codec, nid, 0,
443 AC_VERB_SET_AMP_GAIN_MUTE,
444 AMP_IN_UNMUTE(idx));
445}
446
447/* set the given pin as output */
448static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
449 int pin_type)
450{
451 if (!pin)
452 return;
453 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
454 pin_type);
455 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
456 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200457 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100458}
459
Takashi Iwai5d417622011-06-20 11:32:27 +0200460static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin,
461 int pin_type, struct nid_path *path)
462{
463 struct via_spec *spec = codec->spec;
464 unsigned int caps;
465 hda_nid_t nid;
466 int i;
467
468 if (!pin)
469 return;
470
471 init_output_pin(codec, pin, pin_type);
472 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
473 if (caps & AC_AMPCAP_MUTE) {
474 unsigned int val;
475 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
476 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
477 AMP_OUT_MUTE | val);
478 }
479
480 /* initialize the output path */
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200481 for (i = path->depth - 1; i > 0; i--) {
482 nid = path->path[i - 1];
483 unmute_and_select(codec, path->path[i], nid, spec->aa_mix_nid);
Takashi Iwai5d417622011-06-20 11:32:27 +0200484 }
485}
486
Joseph Chanc577b8a2006-11-29 15:29:40 +0100487
488static void via_auto_init_multi_out(struct hda_codec *codec)
489{
490 struct via_spec *spec = codec->spec;
491 int i;
492
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200493 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwai5d417622011-06-20 11:32:27 +0200494 via_auto_init_output(codec, spec->autocfg.line_out_pins[i],
495 PIN_OUT, &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100496}
497
498static void via_auto_init_hp_out(struct hda_codec *codec)
499{
500 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100501
Takashi Iwai5d417622011-06-20 11:32:27 +0200502 if (spec->hp_dac_nid)
503 via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
504 &spec->hp_path);
505 else
506 via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
507 &spec->hp_dep_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100508}
509
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200510static void via_auto_init_speaker_out(struct hda_codec *codec)
511{
512 struct via_spec *spec = codec->spec;
513
514 if (spec->autocfg.speaker_outs)
515 via_auto_init_output(codec, spec->autocfg.speaker_pins[0],
516 PIN_OUT, &spec->speaker_path);
517}
518
Takashi Iwaif4a78282011-06-17 18:46:48 +0200519static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200520
Joseph Chanc577b8a2006-11-29 15:29:40 +0100521static void via_auto_init_analog_input(struct hda_codec *codec)
522{
523 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200524 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200525 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200526 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200527 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100528
Takashi Iwai096a8852011-06-20 12:09:02 +0200529 /* init ADCs */
530 for (i = 0; i < spec->num_adc_nids; i++) {
531 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
532 AC_VERB_SET_AMP_GAIN_MUTE,
533 AMP_IN_UNMUTE(0));
534 }
535
536 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200537 for (i = 0; i < cfg->num_inputs; i++) {
538 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200539 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200540 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100541 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200542 ctl = PIN_VREF50;
543 else
544 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100545 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200546 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100547 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200548
549 /* init input-src */
550 for (i = 0; i < spec->num_adc_nids; i++) {
551 const struct hda_input_mux *imux = spec->input_mux;
552 if (!imux || !spec->mux_nids[i])
553 continue;
554 snd_hda_codec_write(codec, spec->mux_nids[i], 0,
555 AC_VERB_SET_CONNECT_SEL,
556 imux->items[spec->cur_mux[i]].index);
557 }
558
559 /* init aa-mixer */
560 if (!spec->aa_mix_nid)
561 return;
562 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
563 ARRAY_SIZE(conn));
564 for (i = 0; i < num_conns; i++) {
565 unsigned int caps = get_wcaps(codec, conn[i]);
566 if (get_wcaps_type(caps) == AC_WID_PIN)
567 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
568 AC_VERB_SET_AMP_GAIN_MUTE,
569 AMP_IN_MUTE(i));
570 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100571}
Lydia Wangf5271102009-10-10 19:07:35 +0800572
573static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
574 unsigned int *affected_parm)
575{
576 unsigned parm;
577 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
578 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
579 >> AC_DEFCFG_MISC_SHIFT
580 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800581 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200582 unsigned present = 0;
583
584 no_presence |= spec->no_pin_power_ctl;
585 if (!no_presence)
586 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200587 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800588 || ((no_presence || present)
589 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800590 *affected_parm = AC_PWRST_D0; /* if it's connected */
591 parm = AC_PWRST_D0;
592 } else
593 parm = AC_PWRST_D3;
594
595 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
596}
597
Takashi Iwai24088a52011-06-17 16:59:21 +0200598static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
599 struct snd_ctl_elem_info *uinfo)
600{
601 static const char * const texts[] = {
602 "Disabled", "Enabled"
603 };
604
605 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
606 uinfo->count = 1;
607 uinfo->value.enumerated.items = 2;
608 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
609 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
610 strcpy(uinfo->value.enumerated.name,
611 texts[uinfo->value.enumerated.item]);
612 return 0;
613}
614
615static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
616 struct snd_ctl_elem_value *ucontrol)
617{
618 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
619 struct via_spec *spec = codec->spec;
620 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
621 return 0;
622}
623
624static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
625 struct snd_ctl_elem_value *ucontrol)
626{
627 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
628 struct via_spec *spec = codec->spec;
629 unsigned int val = !ucontrol->value.enumerated.item[0];
630
631 if (val == spec->no_pin_power_ctl)
632 return 0;
633 spec->no_pin_power_ctl = val;
634 set_widgets_power_state(codec);
635 return 1;
636}
637
638static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
639 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
640 .name = "Dynamic Power-Control",
641 .info = via_pin_power_ctl_info,
642 .get = via_pin_power_ctl_get,
643 .put = via_pin_power_ctl_put,
644};
645
646
Joseph Chanc577b8a2006-11-29 15:29:40 +0100647/*
648 * input MUX handling
649 */
650static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
651 struct snd_ctl_elem_info *uinfo)
652{
653 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
654 struct via_spec *spec = codec->spec;
655 return snd_hda_input_mux_info(spec->input_mux, uinfo);
656}
657
658static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
659 struct snd_ctl_elem_value *ucontrol)
660{
661 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
662 struct via_spec *spec = codec->spec;
663 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
664
665 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
666 return 0;
667}
668
669static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
670 struct snd_ctl_elem_value *ucontrol)
671{
672 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
673 struct via_spec *spec = codec->spec;
674 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800675 int ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100676
Takashi Iwai337b9d02009-07-07 18:18:59 +0200677 if (!spec->mux_nids[adc_idx])
678 return -EINVAL;
Lydia Wanga80e6e32009-10-10 19:07:55 +0800679 /* switch to D0 beofre change index */
680 if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
681 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
682 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
683 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800684
685 ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
686 spec->mux_nids[adc_idx],
687 &spec->cur_mux[adc_idx]);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800688 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800689 set_widgets_power_state(codec);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800690
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800691 return ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100692}
693
Harald Welte0aa62ae2008-09-09 15:58:27 +0800694static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
695 struct snd_ctl_elem_info *uinfo)
696{
697 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
698 struct via_spec *spec = codec->spec;
699 return snd_hda_input_mux_info(spec->hp_mux, uinfo);
700}
701
702static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
703 struct snd_ctl_elem_value *ucontrol)
704{
705 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800706 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800707
Takashi Iwaiece8d042011-06-19 16:24:21 +0200708 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800709 return 0;
710}
711
Harald Welte0aa62ae2008-09-09 15:58:27 +0800712static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
713 struct snd_ctl_elem_value *ucontrol)
714{
715 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
716 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100717 hda_nid_t nid = kcontrol->private_value;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800718 unsigned int pinsel = ucontrol->value.enumerated.item[0];
Lydia Wangcdc17842009-10-10 19:07:47 +0800719 /* Get Independent Mode index of headphone pin widget */
720 spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
721 ? 1 : 0;
Takashi Iwaiece8d042011-06-19 16:24:21 +0200722 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800723
Lydia Wangce0e5a92011-03-22 16:22:37 +0800724 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800725 set_widgets_power_state(codec);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800726 return 0;
727}
728
Takashi Iwaiece8d042011-06-19 16:24:21 +0200729static const struct snd_kcontrol_new via_hp_mixer = {
730 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
731 .name = "Independent HP",
732 .info = via_independent_hp_info,
733 .get = via_independent_hp_get,
734 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800735};
736
Takashi Iwai3d83e572010-04-14 14:36:23 +0200737static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100738{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200739 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100740 struct snd_kcontrol_new *knew;
741 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100742
Takashi Iwaiece8d042011-06-19 16:24:21 +0200743 nid = spec->autocfg.hp_pins[0];
744 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200745 if (knew == NULL)
746 return -ENOMEM;
747
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100748 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
749 knew->private_value = nid;
750
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100751 return 0;
752}
753
Lydia Wang1564b282009-10-10 19:07:52 +0800754static void notify_aa_path_ctls(struct hda_codec *codec)
755{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200756 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800757 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800758
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200759 for (i = 0; i < spec->smart51_nums; i++) {
760 struct snd_kcontrol *ctl;
761 struct snd_ctl_elem_id id;
762 memset(&id, 0, sizeof(id));
763 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
764 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800765 ctl = snd_hda_find_mixer_ctl(codec, id.name);
766 if (ctl)
767 snd_ctl_notify(codec->bus->card,
768 SNDRV_CTL_EVENT_MASK_VALUE,
769 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800770 }
771}
772
773static void mute_aa_path(struct hda_codec *codec, int mute)
774{
775 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200776 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800777 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200778
Lydia Wang1564b282009-10-10 19:07:52 +0800779 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200780 for (i = 0; i < spec->smart51_nums; i++) {
781 if (spec->smart51_idxs[i] < 0)
782 continue;
783 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
784 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800785 HDA_AMP_MUTE, val);
786 }
787}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200788
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200789static bool is_smart51_candidate(struct hda_codec *codec, hda_nid_t pin)
Lydia Wang1564b282009-10-10 19:07:52 +0800790{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200791 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200792 const struct auto_pin_cfg *cfg = &spec->autocfg;
793 int i;
794
795 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaif4a78282011-06-17 18:46:48 +0200796 unsigned int defcfg;
797 if (pin != cfg->inputs[i].pin)
798 continue;
799 if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
800 return false;
801 defcfg = snd_hda_codec_get_pincfg(codec, pin);
802 if (snd_hda_get_input_pin_attr(defcfg) < INPUT_PIN_ATTR_NORMAL)
803 return false;
804 return true;
Lydia Wang1564b282009-10-10 19:07:52 +0800805 }
Takashi Iwaif4a78282011-06-17 18:46:48 +0200806 return false;
Lydia Wang1564b282009-10-10 19:07:52 +0800807}
808
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200809static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
810{
811 struct via_spec *spec = codec->spec;
812 int i;
813
814 for (i = 0; i < spec->smart51_nums; i++)
815 if (spec->smart51_pins[i] == pin)
816 return true;
817 return false;
818}
819
Lydia Wang1564b282009-10-10 19:07:52 +0800820static int via_smart51_info(struct snd_kcontrol *kcontrol,
821 struct snd_ctl_elem_info *uinfo)
822{
823 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
824 uinfo->count = 1;
825 uinfo->value.integer.min = 0;
826 uinfo->value.integer.max = 1;
827 return 0;
828}
829
830static int via_smart51_get(struct snd_kcontrol *kcontrol,
831 struct snd_ctl_elem_value *ucontrol)
832{
833 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
834 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800835 int on = 1;
836 int i;
837
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200838 for (i = 0; i < spec->smart51_nums; i++) {
839 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwaif4a78282011-06-17 18:46:48 +0200840 unsigned int ctl;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200841 ctl = snd_hda_codec_read(codec, nid, 0,
842 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200843 if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
844 on = 0;
Lydia Wang1564b282009-10-10 19:07:52 +0800845 }
846 *ucontrol->value.integer.value = on;
847 return 0;
848}
849
850static int via_smart51_put(struct snd_kcontrol *kcontrol,
851 struct snd_ctl_elem_value *ucontrol)
852{
853 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
854 struct via_spec *spec = codec->spec;
855 int out_in = *ucontrol->value.integer.value
856 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800857 int i;
858
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200859 for (i = 0; i < spec->smart51_nums; i++) {
860 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200861 unsigned int parm;
862
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200863 parm = snd_hda_codec_read(codec, nid, 0,
864 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
865 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
866 parm |= out_in;
867 snd_hda_codec_write(codec, nid, 0,
868 AC_VERB_SET_PIN_WIDGET_CONTROL,
869 parm);
870 if (out_in == AC_PINCTL_OUT_EN) {
871 mute_aa_path(codec, 1);
872 notify_aa_path_ctls(codec);
873 }
Lydia Wang1564b282009-10-10 19:07:52 +0800874 }
875 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800876 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800877 return 1;
878}
879
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200880static const struct snd_kcontrol_new via_smart51_mixer = {
881 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
882 .name = "Smart 5.1",
883 .count = 1,
884 .info = via_smart51_info,
885 .get = via_smart51_get,
886 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800887};
888
Takashi Iwaif4a78282011-06-17 18:46:48 +0200889static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100890{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200891 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100892
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200893 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800894 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200895 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100896 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100897 return 0;
898}
899
Takashi Iwaiada509e2011-06-20 15:40:19 +0200900/* check AA path's mute status */
901static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800902{
Lydia Wangf5271102009-10-10 19:07:35 +0800903 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200904 const struct hda_amp_list *p;
905 int i, ch, v;
906
907 for (i = 0; i < spec->num_loopbacks; i++) {
908 p = &spec->loopback_list[i];
909 for (ch = 0; ch < 2; ch++) {
910 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
911 p->idx);
912 if (!(v & HDA_AMP_MUTE) && v > 0)
913 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800914 }
915 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200916 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800917}
918
919/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200920static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800921{
922 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200923 bool enable;
924 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800925
Takashi Iwaiada509e2011-06-20 15:40:19 +0200926 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800927
928 /* decide low current mode's verb & parameter */
929 switch (spec->codec_type) {
930 case VT1708B_8CH:
931 case VT1708B_4CH:
932 verb = 0xf70;
933 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
934 break;
935 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800936 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800937 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800938 verb = 0xf73;
939 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
940 break;
941 case VT1702:
942 verb = 0xf73;
943 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
944 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800945 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800946 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800947 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800948 verb = 0xf93;
949 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
950 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800951 default:
952 return; /* other codecs are not supported */
953 }
954 /* send verb */
955 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
956}
957
Joseph Chanc577b8a2006-11-29 15:29:40 +0100958/*
959 * generic initialization of ADC, input mixers and output mixers
960 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200961static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800962 /* power down jack detect function */
963 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +0100964 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100965};
966
Takashi Iwaiada509e2011-06-20 15:40:19 +0200967static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200968{
Takashi Iwaiada509e2011-06-20 15:40:19 +0200969 struct via_spec *spec = codec->spec;
970
971 if (active)
972 spec->num_active_streams++;
973 else
974 spec->num_active_streams--;
975 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200976}
977
Takashi Iwaiece8d042011-06-19 16:24:21 +0200978static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100979 struct hda_codec *codec,
980 struct snd_pcm_substream *substream)
981{
982 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200983 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +0200984
985 if (!spec->hp_independent_mode)
986 spec->multiout.hp_nid = spec->hp_dac_nid;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200987 set_stream_active(codec, true);
988 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
989 hinfo);
990 if (err < 0) {
991 spec->multiout.hp_nid = 0;
992 set_stream_active(codec, false);
993 return err;
994 }
995 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100996}
997
Takashi Iwaiece8d042011-06-19 16:24:21 +0200998static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +0200999 struct hda_codec *codec,
1000 struct snd_pcm_substream *substream)
1001{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001002 struct via_spec *spec = codec->spec;
1003
1004 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001005 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001006 return 0;
1007}
1008
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001009static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1010 struct hda_codec *codec,
1011 struct snd_pcm_substream *substream)
1012{
1013 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001014
Takashi Iwaiece8d042011-06-19 16:24:21 +02001015 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001016 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001017 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1018 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001019 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001020 return 0;
1021}
1022
1023static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1024 struct hda_codec *codec,
1025 struct snd_pcm_substream *substream)
1026{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001027 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001028 return 0;
1029}
1030
1031static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1032 struct hda_codec *codec,
1033 unsigned int stream_tag,
1034 unsigned int format,
1035 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001036{
1037 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001038
Takashi Iwaiece8d042011-06-19 16:24:21 +02001039 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1040 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001041 vt1708_start_hp_work(spec);
1042 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001043}
1044
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001045static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1046 struct hda_codec *codec,
1047 unsigned int stream_tag,
1048 unsigned int format,
1049 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001050{
1051 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001052
Takashi Iwaiece8d042011-06-19 16:24:21 +02001053 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1054 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001055 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001056 return 0;
1057}
1058
1059static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1060 struct hda_codec *codec,
1061 struct snd_pcm_substream *substream)
1062{
1063 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001064
Takashi Iwaiece8d042011-06-19 16:24:21 +02001065 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001066 vt1708_stop_hp_work(spec);
1067 return 0;
1068}
1069
1070static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1071 struct hda_codec *codec,
1072 struct snd_pcm_substream *substream)
1073{
1074 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001075
Takashi Iwaiece8d042011-06-19 16:24:21 +02001076 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001077 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001078 return 0;
1079}
1080
Joseph Chanc577b8a2006-11-29 15:29:40 +01001081/*
1082 * Digital out
1083 */
1084static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1085 struct hda_codec *codec,
1086 struct snd_pcm_substream *substream)
1087{
1088 struct via_spec *spec = codec->spec;
1089 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1090}
1091
1092static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1093 struct hda_codec *codec,
1094 struct snd_pcm_substream *substream)
1095{
1096 struct via_spec *spec = codec->spec;
1097 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1098}
1099
Harald Welte5691ec72008-09-15 22:42:26 +08001100static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001101 struct hda_codec *codec,
1102 unsigned int stream_tag,
1103 unsigned int format,
1104 struct snd_pcm_substream *substream)
1105{
1106 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001107 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1108 stream_tag, format, substream);
1109}
Harald Welte5691ec72008-09-15 22:42:26 +08001110
Takashi Iwai9da29272009-05-07 16:31:14 +02001111static int via_dig_playback_pcm_cleanup(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 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001117 return 0;
1118}
1119
Joseph Chanc577b8a2006-11-29 15:29:40 +01001120/*
1121 * Analog capture
1122 */
1123static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1124 struct hda_codec *codec,
1125 unsigned int stream_tag,
1126 unsigned int format,
1127 struct snd_pcm_substream *substream)
1128{
1129 struct via_spec *spec = codec->spec;
1130
1131 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1132 stream_tag, 0, format);
1133 return 0;
1134}
1135
1136static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1137 struct hda_codec *codec,
1138 struct snd_pcm_substream *substream)
1139{
1140 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001141 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001142 return 0;
1143}
1144
Takashi Iwai9af74212011-06-18 16:17:45 +02001145static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001146 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001147 .channels_min = 2,
1148 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001149 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001150 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001151 .open = via_playback_multi_pcm_open,
1152 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001153 .prepare = via_playback_multi_pcm_prepare,
1154 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001155 },
1156};
1157
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001158static const struct hda_pcm_stream via_pcm_hp_playback = {
1159 .substreams = 1,
1160 .channels_min = 2,
1161 .channels_max = 2,
1162 /* NID is set in via_build_pcms */
1163 .ops = {
1164 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001165 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001166 .prepare = via_playback_hp_pcm_prepare,
1167 .cleanup = via_playback_hp_pcm_cleanup
1168 },
1169};
1170
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001171static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001172 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001173 .channels_min = 2,
1174 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001175 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001176 /* We got noisy outputs on the right channel on VT1708 when
1177 * 24bit samples are used. Until any workaround is found,
1178 * disable the 24bit format, so far.
1179 */
1180 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1181 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001182 .open = via_playback_multi_pcm_open,
1183 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001184 .prepare = via_playback_multi_pcm_prepare,
1185 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001186 },
1187};
1188
Takashi Iwai9af74212011-06-18 16:17:45 +02001189static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001190 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001191 .channels_min = 2,
1192 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001193 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001194 .ops = {
1195 .prepare = via_capture_pcm_prepare,
1196 .cleanup = via_capture_pcm_cleanup
1197 },
1198};
1199
Takashi Iwai9af74212011-06-18 16:17:45 +02001200static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001201 .substreams = 1,
1202 .channels_min = 2,
1203 .channels_max = 2,
1204 /* NID is set in via_build_pcms */
1205 .ops = {
1206 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001207 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001208 .prepare = via_dig_playback_pcm_prepare,
1209 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001210 },
1211};
1212
Takashi Iwai9af74212011-06-18 16:17:45 +02001213static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001214 .substreams = 1,
1215 .channels_min = 2,
1216 .channels_max = 2,
1217};
1218
Takashi Iwai370bafb2011-06-20 12:47:45 +02001219/*
1220 * slave controls for virtual master
1221 */
1222static const char * const via_slave_vols[] = {
1223 "Front Playback Volume",
1224 "Surround Playback Volume",
1225 "Center Playback Volume",
1226 "LFE Playback Volume",
1227 "Side Playback Volume",
1228 "Headphone Playback Volume",
1229 "Speaker Playback Volume",
1230 NULL,
1231};
1232
1233static const char * const via_slave_sws[] = {
1234 "Front Playback Switch",
1235 "Surround Playback Switch",
1236 "Center Playback Switch",
1237 "LFE Playback Switch",
1238 "Side Playback Switch",
1239 "Headphone Playback Switch",
1240 "Speaker Playback Switch",
1241 NULL,
1242};
1243
Joseph Chanc577b8a2006-11-29 15:29:40 +01001244static int via_build_controls(struct hda_codec *codec)
1245{
1246 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001247 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001248 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001249
Takashi Iwai24088a52011-06-17 16:59:21 +02001250 if (spec->set_widgets_power_state)
1251 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1252 return -ENOMEM;
1253
Joseph Chanc577b8a2006-11-29 15:29:40 +01001254 for (i = 0; i < spec->num_mixers; i++) {
1255 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1256 if (err < 0)
1257 return err;
1258 }
1259
1260 if (spec->multiout.dig_out_nid) {
1261 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001262 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001263 spec->multiout.dig_out_nid);
1264 if (err < 0)
1265 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001266 err = snd_hda_create_spdif_share_sw(codec,
1267 &spec->multiout);
1268 if (err < 0)
1269 return err;
1270 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001271 }
1272 if (spec->dig_in_nid) {
1273 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1274 if (err < 0)
1275 return err;
1276 }
Lydia Wang17314372009-10-10 19:07:37 +08001277
Takashi Iwai370bafb2011-06-20 12:47:45 +02001278 /* if we have no master control, let's create it */
1279 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1280 unsigned int vmaster_tlv[4];
1281 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1282 HDA_OUTPUT, vmaster_tlv);
1283 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1284 vmaster_tlv, via_slave_vols);
1285 if (err < 0)
1286 return err;
1287 }
1288 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1289 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1290 NULL, via_slave_sws);
1291 if (err < 0)
1292 return err;
1293 }
1294
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001295 /* assign Capture Source enums to NID */
1296 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1297 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001298 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001299 if (err < 0)
1300 return err;
1301 }
1302
Lydia Wang17314372009-10-10 19:07:37 +08001303 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001304 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001305 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001306
Takashi Iwai603c4012008-07-30 15:01:44 +02001307 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001308 return 0;
1309}
1310
1311static int via_build_pcms(struct hda_codec *codec)
1312{
1313 struct via_spec *spec = codec->spec;
1314 struct hda_pcm *info = spec->pcm_rec;
1315
1316 codec->num_pcms = 1;
1317 codec->pcm_info = info;
1318
Takashi Iwai82673bc2011-06-17 16:24:21 +02001319 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1320 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001321 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001322
1323 if (!spec->stream_analog_playback)
1324 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001325 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001326 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001327 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1328 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001329 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1330 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001331
1332 if (!spec->stream_analog_capture)
1333 spec->stream_analog_capture = &via_pcm_analog_capture;
1334 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1335 *spec->stream_analog_capture;
1336 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1337 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1338 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001339
1340 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1341 codec->num_pcms++;
1342 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001343 snprintf(spec->stream_name_digital,
1344 sizeof(spec->stream_name_digital),
1345 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001346 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001347 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001348 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001349 if (!spec->stream_digital_playback)
1350 spec->stream_digital_playback =
1351 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001352 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001353 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001354 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1355 spec->multiout.dig_out_nid;
1356 }
1357 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001358 if (!spec->stream_digital_capture)
1359 spec->stream_digital_capture =
1360 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001361 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001362 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001363 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1364 spec->dig_in_nid;
1365 }
1366 }
1367
Takashi Iwaiece8d042011-06-19 16:24:21 +02001368 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001369 codec->num_pcms++;
1370 info++;
1371 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1372 "%s HP", codec->chip_name);
1373 info->name = spec->stream_name_hp;
1374 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1375 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001376 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001377 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001378 return 0;
1379}
1380
1381static void via_free(struct hda_codec *codec)
1382{
1383 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001384
1385 if (!spec)
1386 return;
1387
Takashi Iwai603c4012008-07-30 15:01:44 +02001388 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001389 vt1708_stop_hp_work(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001390 kfree(codec->spec);
1391}
1392
Takashi Iwai64be2852011-06-17 16:51:39 +02001393/* mute/unmute outputs */
1394static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1395 hda_nid_t *pins, bool mute)
1396{
1397 int i;
1398 for (i = 0; i < num_pins; i++)
1399 snd_hda_codec_write(codec, pins[i], 0,
1400 AC_VERB_SET_PIN_WIDGET_CONTROL,
1401 mute ? 0 : PIN_OUT);
1402}
1403
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001404/* mute internal speaker if line-out is plugged */
1405static void via_line_automute(struct hda_codec *codec, int present)
1406{
1407 struct via_spec *spec = codec->spec;
1408
1409 if (!spec->autocfg.speaker_outs)
1410 return;
1411 if (!present)
1412 present = snd_hda_jack_detect(codec,
1413 spec->autocfg.line_out_pins[0]);
1414 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1415 spec->autocfg.speaker_pins,
1416 present);
1417}
1418
Harald Welte69e52a82008-09-09 15:57:32 +08001419/* mute internal speaker if HP is plugged */
1420static void via_hp_automute(struct hda_codec *codec)
1421{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001422 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001423 struct via_spec *spec = codec->spec;
1424
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001425 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
1426 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai64be2852011-06-17 16:51:39 +02001427 toggle_output_mutes(codec, spec->autocfg.line_outs,
1428 spec->autocfg.line_out_pins,
1429 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001430 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001431 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001432}
1433
Harald Welte69e52a82008-09-09 15:57:32 +08001434static void via_gpio_control(struct hda_codec *codec)
1435{
1436 unsigned int gpio_data;
1437 unsigned int vol_counter;
1438 unsigned int vol;
1439 unsigned int master_vol;
1440
1441 struct via_spec *spec = codec->spec;
1442
1443 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1444 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1445
1446 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1447 0xF84, 0) & 0x3F0000) >> 16;
1448
1449 vol = vol_counter & 0x1F;
1450 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1451 AC_VERB_GET_AMP_GAIN_MUTE,
1452 AC_AMP_GET_INPUT);
1453
1454 if (gpio_data == 0x02) {
1455 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001456 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1457 AC_VERB_SET_PIN_WIDGET_CONTROL,
1458 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001459 if (vol_counter & 0x20) {
1460 /* decrease volume */
1461 if (vol > master_vol)
1462 vol = master_vol;
1463 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1464 0, HDA_AMP_VOLMASK,
1465 master_vol-vol);
1466 } else {
1467 /* increase volume */
1468 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1469 HDA_AMP_VOLMASK,
1470 ((master_vol+vol) > 0x2A) ? 0x2A :
1471 (master_vol+vol));
1472 }
1473 } else if (!(gpio_data & 0x02)) {
1474 /* mute 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 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001478 }
1479}
1480
1481/* unsolicited event for jack sensing */
1482static void via_unsol_event(struct hda_codec *codec,
1483 unsigned int res)
1484{
1485 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001486
Lydia Wanga34df192009-10-10 19:08:01 +08001487 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001488 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001489
1490 res &= ~VIA_JACK_EVENT;
1491
1492 if (res == VIA_HP_EVENT)
1493 via_hp_automute(codec);
1494 else if (res == VIA_GPIO_EVENT)
1495 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001496 else if (res == VIA_LINE_EVENT)
1497 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001498}
1499
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001500#ifdef SND_HDA_NEEDS_RESUME
1501static int via_suspend(struct hda_codec *codec, pm_message_t state)
1502{
1503 struct via_spec *spec = codec->spec;
1504 vt1708_stop_hp_work(spec);
1505 return 0;
1506}
1507#endif
1508
Takashi Iwaicb53c622007-08-10 17:21:45 +02001509#ifdef CONFIG_SND_HDA_POWER_SAVE
1510static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1511{
1512 struct via_spec *spec = codec->spec;
1513 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1514}
1515#endif
1516
Joseph Chanc577b8a2006-11-29 15:29:40 +01001517/*
1518 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001519
1520static int via_init(struct hda_codec *codec);
1521
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001522static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001523 .build_controls = via_build_controls,
1524 .build_pcms = via_build_pcms,
1525 .init = via_init,
1526 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001527 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001528#ifdef SND_HDA_NEEDS_RESUME
1529 .suspend = via_suspend,
1530#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001531#ifdef CONFIG_SND_HDA_POWER_SAVE
1532 .check_power_status = via_check_power_status,
1533#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001534};
1535
Takashi Iwai4a796162011-06-17 17:53:38 +02001536static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001537{
Takashi Iwai4a796162011-06-17 17:53:38 +02001538 struct via_spec *spec = codec->spec;
1539 int i;
1540
1541 for (i = 0; i < spec->multiout.num_dacs; i++) {
1542 if (spec->multiout.dac_nids[i] == dac)
1543 return false;
1544 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001545 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001546 return false;
1547 return true;
1548}
1549
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001550static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001551 hda_nid_t target_dac, struct nid_path *path,
1552 int depth, int wid_type)
1553{
1554 hda_nid_t conn[8];
1555 int i, nums;
1556
1557 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1558 for (i = 0; i < nums; i++) {
1559 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1560 continue;
1561 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001562 path->path[0] = conn[i];
1563 path->idx[0] = i;
1564 path->depth = 1;
Takashi Iwai4a796162011-06-17 17:53:38 +02001565 return true;
1566 }
1567 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001568 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001569 return false;
1570 for (i = 0; i < nums; i++) {
1571 unsigned int type;
1572 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1573 if (type == AC_WID_AUD_OUT ||
1574 (wid_type != -1 && type != wid_type))
1575 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001576 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai4a796162011-06-17 17:53:38 +02001577 path, depth + 1, AC_WID_AUD_SEL)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001578 path->path[path->depth] = conn[i];
1579 path->idx[path->depth] = i;
1580 path->depth++;
Takashi Iwai4a796162011-06-17 17:53:38 +02001581 return true;
1582 }
1583 }
1584 return false;
1585}
1586
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001587static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1588 hda_nid_t target_dac, struct nid_path *path)
1589{
1590 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1591 path->path[path->depth] = nid;
1592 path->depth++;
1593 return true;
1594 }
1595 return false;
1596}
1597
Takashi Iwai4a796162011-06-17 17:53:38 +02001598static int via_auto_fill_dac_nids(struct hda_codec *codec)
1599{
1600 struct via_spec *spec = codec->spec;
1601 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001602 int i;
1603 hda_nid_t nid;
1604
Joseph Chanc577b8a2006-11-29 15:29:40 +01001605 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001606 spec->multiout.num_dacs = cfg->line_outs;
1607 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001608 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001609 if (!nid)
1610 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001611 if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
1612 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001613 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001614 return 0;
1615}
1616
Takashi Iwai4a796162011-06-17 17:53:38 +02001617static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
1618 hda_nid_t pin, hda_nid_t dac, int chs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001619{
Takashi Iwai4a796162011-06-17 17:53:38 +02001620 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001621 char name[32];
Takashi Iwai4a796162011-06-17 17:53:38 +02001622 hda_nid_t nid;
1623 int err;
1624
1625 if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
1626 nid = dac;
1627 else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
1628 nid = pin;
1629 else
1630 nid = 0;
1631 if (nid) {
1632 sprintf(name, "%s Playback Volume", pfx);
1633 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1634 HDA_COMPOSE_AMP_VAL(dac, chs, 0, HDA_OUTPUT));
1635 if (err < 0)
1636 return err;
1637 }
1638
1639 if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE)
1640 nid = dac;
1641 else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE)
1642 nid = pin;
1643 else
1644 nid = 0;
1645 if (nid) {
1646 sprintf(name, "%s Playback Switch", pfx);
1647 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1648 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1649 if (err < 0)
1650 return err;
1651 }
1652 return 0;
1653}
1654
Takashi Iwaif4a78282011-06-17 18:46:48 +02001655static void mangle_smart51(struct hda_codec *codec)
1656{
1657 struct via_spec *spec = codec->spec;
1658 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001659 int i, nums = 0;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001660
1661 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001662 if (is_smart51_candidate(codec, cfg->inputs[i].pin))
1663 nums++;
1664 }
1665 if (cfg->line_outs + nums < 3)
1666 return;
1667 for (i = 0; i < cfg->num_inputs; i++) {
1668 if (!is_smart51_candidate(codec, cfg->inputs[i].pin))
Takashi Iwaif4a78282011-06-17 18:46:48 +02001669 continue;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001670 spec->smart51_pins[spec->smart51_nums++] = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001671 cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin;
1672 if (cfg->line_outs == 3)
1673 break;
1674 }
1675}
1676
Takashi Iwai4a796162011-06-17 17:53:38 +02001677/* add playback controls from the parsed DAC table */
1678static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1679{
1680 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001681 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001682 static const char * const chname[4] = {
1683 "Front", "Surround", "C/LFE", "Side"
1684 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001685 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001686 int old_line_outs;
1687
1688 /* check smart51 */
1689 old_line_outs = cfg->line_outs;
1690 if (cfg->line_outs == 1)
1691 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001692
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001693 err = via_auto_fill_dac_nids(codec);
1694 if (err < 0)
1695 return err;
1696
Takashi Iwai4a796162011-06-17 17:53:38 +02001697 for (i = 0; i < cfg->line_outs; i++) {
1698 hda_nid_t pin, dac;
1699 pin = cfg->line_out_pins[i];
1700 dac = spec->multiout.dac_nids[i];
1701 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001702 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001703 if (i == HDA_CLFE) {
Takashi Iwai4a796162011-06-17 17:53:38 +02001704 err = create_ch_ctls(codec, "Center", pin, dac, 1);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001705 if (err < 0)
1706 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02001707 err = create_ch_ctls(codec, "LFE", pin, dac, 2);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001708 if (err < 0)
1709 return err;
1710 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001711 const char *pfx = chname[i];
1712 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1713 cfg->line_outs == 1)
1714 pfx = "Speaker";
1715 err = create_ch_ctls(codec, pfx, pin, dac, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001716 if (err < 0)
1717 return err;
1718 }
1719 }
1720
Takashi Iwai4a796162011-06-17 17:53:38 +02001721 idx = get_connection_index(codec, spec->aa_mix_nid,
1722 spec->multiout.dac_nids[0]);
1723 if (idx >= 0) {
1724 /* add control to mixer */
1725 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1726 "PCM Playback Volume",
1727 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1728 idx, HDA_INPUT));
1729 if (err < 0)
1730 return err;
1731 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1732 "PCM Playback Switch",
1733 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1734 idx, HDA_INPUT));
1735 if (err < 0)
1736 return err;
1737 }
1738
Takashi Iwaif4a78282011-06-17 18:46:48 +02001739 cfg->line_outs = old_line_outs;
1740
Joseph Chanc577b8a2006-11-29 15:29:40 +01001741 return 0;
1742}
1743
Harald Welte0aa62ae2008-09-09 15:58:27 +08001744static void create_hp_imux(struct via_spec *spec)
1745{
1746 int i;
1747 struct hda_input_mux *imux = &spec->private_imux[1];
Takashi Iwaiea734962011-01-17 11:29:34 +01001748 static const char * const texts[] = { "OFF", "ON", NULL};
Harald Welte0aa62ae2008-09-09 15:58:27 +08001749
1750 /* for hp mode select */
Takashi Iwai10a20af2010-09-09 16:28:02 +02001751 for (i = 0; texts[i]; i++)
1752 snd_hda_add_imux_item(imux, texts[i], i, NULL);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001753
1754 spec->hp_mux = &spec->private_imux[1];
1755}
1756
Takashi Iwai4a796162011-06-17 17:53:38 +02001757static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001758{
Takashi Iwai4a796162011-06-17 17:53:38 +02001759 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001760 int err;
1761
1762 if (!pin)
1763 return 0;
1764
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001765 if (parse_output_path(codec, pin, 0, &spec->hp_path)) {
1766 spec->hp_dac_nid = spec->hp_path.path[0];
1767 spec->hp_independent_mode_index = spec->hp_path.idx[0];
Takashi Iwai4a796162011-06-17 17:53:38 +02001768 create_hp_imux(spec);
1769 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001770
Takashi Iwaiece8d042011-06-19 16:24:21 +02001771 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001772 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001773 !spec->hp_dac_nid)
1774 return 0;
1775
1776
1777 err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001778 if (err < 0)
1779 return err;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001780
Joseph Chanc577b8a2006-11-29 15:29:40 +01001781 return 0;
1782}
1783
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001784static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1785{
1786 struct via_spec *spec = codec->spec;
1787 hda_nid_t pin, dac;
1788
1789 pin = spec->autocfg.speaker_pins[0];
1790 if (!spec->autocfg.speaker_outs || !pin)
1791 return 0;
1792
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001793 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1794 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001795 spec->multiout.extra_out_nid[0] = dac;
1796 return create_ch_ctls(codec, "Speaker", pin, dac, 3);
1797 }
1798 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001799 &spec->speaker_path))
1800 return create_ch_ctls(codec, "Speaker", pin, 0, 3);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001801
1802 return 0;
1803}
1804
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001805/* look for ADCs */
1806static int via_fill_adcs(struct hda_codec *codec)
1807{
1808 struct via_spec *spec = codec->spec;
1809 hda_nid_t nid = codec->start_nid;
1810 int i;
1811
1812 for (i = 0; i < codec->num_nodes; i++, nid++) {
1813 unsigned int wcaps = get_wcaps(codec, nid);
1814 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1815 continue;
1816 if (wcaps & AC_WCAP_DIGITAL)
1817 continue;
1818 if (!(wcaps & AC_WCAP_CONN_LIST))
1819 continue;
1820 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1821 return -ENOMEM;
1822 spec->adc_nids[spec->num_adc_nids++] = nid;
1823 }
1824 return 0;
1825}
1826
1827static int get_mux_nids(struct hda_codec *codec);
1828
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001829static const struct snd_kcontrol_new via_input_src_ctl = {
1830 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1831 /* The multiple "Capture Source" controls confuse alsamixer
1832 * So call somewhat different..
1833 */
1834 /* .name = "Capture Source", */
1835 .name = "Input Source",
1836 .info = via_mux_enum_info,
1837 .get = via_mux_enum_get,
1838 .put = via_mux_enum_put,
1839};
1840
Takashi Iwai13af8e72011-06-20 14:05:46 +02001841static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
1842{
1843 struct hda_amp_list *list;
1844
1845 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
1846 return;
1847 list = spec->loopback_list + spec->num_loopbacks;
1848 list->nid = mix;
1849 list->dir = HDA_INPUT;
1850 list->idx = idx;
1851 spec->num_loopbacks++;
1852 spec->loopback.amplist = spec->loopback_list;
1853}
Takashi Iwai13af8e72011-06-20 14:05:46 +02001854
Joseph Chanc577b8a2006-11-29 15:29:40 +01001855/* create playback/capture controls for input pins */
Takashi Iwai620e2b22011-06-17 17:19:19 +02001856static int via_auto_create_analog_input_ctls(struct hda_codec *codec,
1857 const struct auto_pin_cfg *cfg)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001858{
Takashi Iwai10a20af2010-09-09 16:28:02 +02001859 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001860 struct hda_input_mux *imux = &spec->private_imux[0];
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001861 int i, j, err, idx, idx2, type, type_idx = 0;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001862 hda_nid_t cap_nid;
1863 hda_nid_t pin_idxs[8];
1864 int num_idxs;
1865
1866 err = via_fill_adcs(codec);
1867 if (err < 0)
1868 return err;
1869 err = get_mux_nids(codec);
1870 if (err < 0)
1871 return err;
1872 cap_nid = spec->mux_nids[0];
1873
1874 num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
1875 ARRAY_SIZE(pin_idxs));
1876 if (num_idxs <= 0)
1877 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001878
1879 /* for internal loopback recording select */
Takashi Iwaif3268512010-08-30 11:00:19 +02001880 for (idx = 0; idx < num_idxs; idx++) {
Takashi Iwai620e2b22011-06-17 17:19:19 +02001881 if (pin_idxs[idx] == spec->aa_mix_nid) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001882 snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
Takashi Iwaif3268512010-08-30 11:00:19 +02001883 break;
1884 }
1885 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001886
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001887 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001888 const char *label;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001889 type = cfg->inputs[i].type;
Takashi Iwaif3268512010-08-30 11:00:19 +02001890 for (idx = 0; idx < num_idxs; idx++)
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001891 if (pin_idxs[idx] == cfg->inputs[i].pin)
Takashi Iwaif3268512010-08-30 11:00:19 +02001892 break;
1893 if (idx >= num_idxs)
1894 continue;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001895 if (i > 0 && type == cfg->inputs[i - 1].type)
1896 type_idx++;
1897 else
1898 type_idx = 0;
Takashi Iwai10a20af2010-09-09 16:28:02 +02001899 label = hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwai620e2b22011-06-17 17:19:19 +02001900 idx2 = get_connection_index(codec, spec->aa_mix_nid,
1901 pin_idxs[idx]);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001902 if (idx2 >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08001903 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwai620e2b22011-06-17 17:19:19 +02001904 idx2, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001905 if (err < 0)
1906 return err;
1907 add_loopback_list(spec, spec->aa_mix_nid, idx2);
1908 }
Takashi Iwai10a20af2010-09-09 16:28:02 +02001909 snd_hda_add_imux_item(imux, label, idx, NULL);
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001910
1911 /* remember the label for smart51 control */
1912 for (j = 0; j < spec->smart51_nums; j++) {
1913 if (spec->smart51_pins[j] == cfg->inputs[i].pin) {
1914 spec->smart51_idxs[j] = idx;
1915 spec->smart51_labels[j] = label;
1916 break;
1917 }
1918 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001919 }
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001920
1921 /* create capture mixer elements */
1922 for (i = 0; i < spec->num_adc_nids; i++) {
1923 hda_nid_t adc = spec->adc_nids[i];
1924 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
1925 "Capture Volume", i,
1926 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1927 HDA_INPUT));
1928 if (err < 0)
1929 return err;
1930 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1931 "Capture Switch", i,
1932 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1933 HDA_INPUT));
1934 if (err < 0)
1935 return err;
1936 }
1937
1938 /* input-source control */
1939 for (i = 0; i < spec->num_adc_nids; i++)
1940 if (!spec->mux_nids[i])
1941 break;
1942 if (i) {
1943 struct snd_kcontrol_new *knew;
1944 knew = via_clone_control(spec, &via_input_src_ctl);
1945 if (!knew)
1946 return -ENOMEM;
1947 knew->count = i;
1948 }
1949
1950 /* mic-boosts */
1951 for (i = 0; i < cfg->num_inputs; i++) {
1952 hda_nid_t pin = cfg->inputs[i].pin;
1953 unsigned int caps;
1954 const char *label;
1955 char name[32];
1956
1957 if (cfg->inputs[i].type != AUTO_PIN_MIC)
1958 continue;
1959 caps = query_amp_caps(codec, pin, HDA_INPUT);
1960 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
1961 continue;
1962 label = hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwai30f7c5d2011-06-21 08:37:41 +02001963 snprintf(name, sizeof(name), "%s Boost Volume", label);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001964 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1965 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
1966 if (err < 0)
1967 return err;
1968 }
1969
Joseph Chanc577b8a2006-11-29 15:29:40 +01001970 return 0;
1971}
1972
Harald Welte76d9b0d2008-09-09 15:50:37 +08001973static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
1974{
1975 unsigned int def_conf;
1976 unsigned char seqassoc;
1977
Takashi Iwai2f334f92009-02-20 14:37:42 +01001978 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08001979 seqassoc = (unsigned char) get_defcfg_association(def_conf);
1980 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08001981 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
1982 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
1983 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
1984 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08001985 }
1986
1987 return;
1988}
1989
Takashi Iwaie06e5a22011-06-17 15:46:13 +02001990static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001991 struct snd_ctl_elem_value *ucontrol)
1992{
1993 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1994 struct via_spec *spec = codec->spec;
1995
1996 if (spec->codec_type != VT1708)
1997 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02001998 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001999 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002000 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002001 return 0;
2002}
2003
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002004static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002005 struct snd_ctl_elem_value *ucontrol)
2006{
2007 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2008 struct via_spec *spec = codec->spec;
2009 int change;
2010
2011 if (spec->codec_type != VT1708)
2012 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002013 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002014 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002015 == !spec->vt1708_jack_detect;
2016 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002017 mute_aa_path(codec, 1);
2018 notify_aa_path_ctls(codec);
2019 }
2020 return change;
2021}
2022
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002023static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2024 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2025 .name = "Jack Detect",
2026 .count = 1,
2027 .info = snd_ctl_boolean_mono_info,
2028 .get = vt1708_jack_detect_get,
2029 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002030};
2031
Takashi Iwai12daef62011-06-18 17:45:49 +02002032static void fill_dig_outs(struct hda_codec *codec);
2033static void fill_dig_in(struct hda_codec *codec);
2034
2035static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002036{
2037 struct via_spec *spec = codec->spec;
2038 int err;
2039
2040 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2041 if (err < 0)
2042 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002043 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002044 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002045
Takashi Iwai4a796162011-06-17 17:53:38 +02002046 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002047 if (err < 0)
2048 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002049 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002050 if (err < 0)
2051 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002052 err = via_auto_create_speaker_ctls(codec);
2053 if (err < 0)
2054 return err;
Takashi Iwai620e2b22011-06-17 17:19:19 +02002055 err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002056 if (err < 0)
2057 return err;
2058
2059 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2060
Takashi Iwai12daef62011-06-18 17:45:49 +02002061 fill_dig_outs(codec);
2062 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002063
Takashi Iwai603c4012008-07-30 15:01:44 +02002064 if (spec->kctls.list)
2065 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002066
Takashi Iwai096a8852011-06-20 12:09:02 +02002067 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002068
Harald Welte0aa62ae2008-09-09 15:58:27 +08002069 spec->input_mux = &spec->private_imux[0];
2070
Takashi Iwaiece8d042011-06-19 16:24:21 +02002071 if (spec->hp_mux) {
2072 err = via_hp_build(codec);
2073 if (err < 0)
2074 return err;
2075 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002076
Takashi Iwaif4a78282011-06-17 18:46:48 +02002077 err = via_smart51_build(codec);
2078 if (err < 0)
2079 return err;
2080
Takashi Iwai5d417622011-06-20 11:32:27 +02002081 /* assign slave outs */
2082 if (spec->slave_dig_outs[0])
2083 codec->slave_dig_outs = spec->slave_dig_outs;
2084
Joseph Chanc577b8a2006-11-29 15:29:40 +01002085 return 1;
2086}
2087
Takashi Iwai5d417622011-06-20 11:32:27 +02002088static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002089{
Lydia Wang25eaba22009-10-10 19:08:43 +08002090 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002091 if (spec->multiout.dig_out_nid)
2092 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2093 if (spec->slave_dig_outs[0])
2094 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2095}
Lydia Wang25eaba22009-10-10 19:08:43 +08002096
Takashi Iwai5d417622011-06-20 11:32:27 +02002097static void via_auto_init_dig_in(struct hda_codec *codec)
2098{
2099 struct via_spec *spec = codec->spec;
2100 if (!spec->dig_in_nid)
2101 return;
2102 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2103 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2104}
2105
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002106/* initialize the unsolicited events */
2107static void via_auto_init_unsol_event(struct hda_codec *codec)
2108{
2109 struct via_spec *spec = codec->spec;
2110 struct auto_pin_cfg *cfg = &spec->autocfg;
2111 unsigned int ev;
2112 int i;
2113
2114 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2115 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2116 AC_VERB_SET_UNSOLICITED_ENABLE,
2117 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2118
2119 if (cfg->speaker_pins[0])
2120 ev = VIA_LINE_EVENT;
2121 else
2122 ev = 0;
2123 for (i = 0; i < cfg->line_outs; i++) {
2124 if (cfg->line_out_pins[i] &&
2125 is_jack_detectable(codec, cfg->line_out_pins[i]))
2126 snd_hda_codec_write(codec, cfg->line_out_pins[0], 0,
2127 AC_VERB_SET_UNSOLICITED_ENABLE,
2128 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2129 }
2130
2131 for (i = 0; i < cfg->num_inputs; i++) {
2132 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2133 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2134 AC_VERB_SET_UNSOLICITED_ENABLE,
2135 AC_USRSP_EN | VIA_JACK_EVENT);
2136 }
2137}
2138
Takashi Iwai5d417622011-06-20 11:32:27 +02002139static int via_init(struct hda_codec *codec)
2140{
2141 struct via_spec *spec = codec->spec;
2142 int i;
2143
2144 for (i = 0; i < spec->num_iverbs; i++)
2145 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2146
Joseph Chanc577b8a2006-11-29 15:29:40 +01002147 via_auto_init_multi_out(codec);
2148 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002149 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002150 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002151 via_auto_init_dig_outs(codec);
2152 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002153
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002154 via_auto_init_unsol_event(codec);
2155
2156 via_hp_automute(codec);
2157 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002158
Joseph Chanc577b8a2006-11-29 15:29:40 +01002159 return 0;
2160}
2161
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002162static void vt1708_update_hp_jack_state(struct work_struct *work)
2163{
2164 struct via_spec *spec = container_of(work, struct via_spec,
2165 vt1708_hp_work.work);
2166 if (spec->codec_type != VT1708)
2167 return;
2168 /* if jack state toggled */
2169 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002170 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002171 spec->vt1708_hp_present ^= 1;
2172 via_hp_automute(spec->codec);
2173 }
2174 vt1708_start_hp_work(spec);
2175}
2176
Takashi Iwai337b9d02009-07-07 18:18:59 +02002177static int get_mux_nids(struct hda_codec *codec)
2178{
2179 struct via_spec *spec = codec->spec;
2180 hda_nid_t nid, conn[8];
2181 unsigned int type;
2182 int i, n;
2183
2184 for (i = 0; i < spec->num_adc_nids; i++) {
2185 nid = spec->adc_nids[i];
2186 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002187 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002188 if (type == AC_WID_PIN)
2189 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002190 n = snd_hda_get_connections(codec, nid, conn,
2191 ARRAY_SIZE(conn));
2192 if (n <= 0)
2193 break;
2194 if (n > 1) {
2195 spec->mux_nids[i] = nid;
2196 break;
2197 }
2198 nid = conn[0];
2199 }
2200 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002201 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002202}
2203
Joseph Chanc577b8a2006-11-29 15:29:40 +01002204static int patch_vt1708(struct hda_codec *codec)
2205{
2206 struct via_spec *spec;
2207 int err;
2208
2209 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002210 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002211 if (spec == NULL)
2212 return -ENOMEM;
2213
Takashi Iwai620e2b22011-06-17 17:19:19 +02002214 spec->aa_mix_nid = 0x17;
2215
Takashi Iwai12daef62011-06-18 17:45:49 +02002216 /* Add HP and CD pin config connect bit re-config action */
2217 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2218 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2219
Joseph Chanc577b8a2006-11-29 15:29:40 +01002220 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002221 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002222 if (err < 0) {
2223 via_free(codec);
2224 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002225 }
2226
Takashi Iwai12daef62011-06-18 17:45:49 +02002227 /* add jack detect on/off control */
2228 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2229 return -ENOMEM;
2230
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002231 /* disable 32bit format on VT1708 */
2232 if (codec->vendor_id == 0x11061708)
2233 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002234
Joseph Chanc577b8a2006-11-29 15:29:40 +01002235 codec->patch_ops = via_patch_ops;
2236
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002237 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002238 return 0;
2239}
2240
Joseph Chanc577b8a2006-11-29 15:29:40 +01002241static int patch_vt1709_10ch(struct hda_codec *codec)
2242{
2243 struct via_spec *spec;
2244 int err;
2245
2246 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002247 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002248 if (spec == NULL)
2249 return -ENOMEM;
2250
Takashi Iwai620e2b22011-06-17 17:19:19 +02002251 spec->aa_mix_nid = 0x18;
2252
Takashi Iwai12daef62011-06-18 17:45:49 +02002253 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002254 if (err < 0) {
2255 via_free(codec);
2256 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002257 }
2258
Joseph Chanc577b8a2006-11-29 15:29:40 +01002259 codec->patch_ops = via_patch_ops;
2260
Joseph Chanc577b8a2006-11-29 15:29:40 +01002261 return 0;
2262}
2263/*
2264 * generic initialization of ADC, input mixers and output mixers
2265 */
Joseph Chanc577b8a2006-11-29 15:29:40 +01002266static int patch_vt1709_6ch(struct hda_codec *codec)
2267{
2268 struct via_spec *spec;
2269 int err;
2270
2271 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002272 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002273 if (spec == NULL)
2274 return -ENOMEM;
2275
Takashi Iwai620e2b22011-06-17 17:19:19 +02002276 spec->aa_mix_nid = 0x18;
2277
Takashi Iwai12daef62011-06-18 17:45:49 +02002278 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002279 if (err < 0) {
2280 via_free(codec);
2281 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002282 }
2283
Joseph Chanc577b8a2006-11-29 15:29:40 +01002284 codec->patch_ops = via_patch_ops;
2285
Josepch Chanf7278fd2007-12-13 16:40:40 +01002286 return 0;
2287}
2288
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002289static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2290{
2291 struct via_spec *spec = codec->spec;
2292 int imux_is_smixer;
2293 unsigned int parm;
2294 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002295 if ((spec->codec_type != VT1708B_4CH) &&
2296 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002297 is_8ch = 1;
2298
2299 /* SW0 (17h) = stereo mixer */
2300 imux_is_smixer =
2301 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2302 == ((spec->codec_type == VT1708S) ? 5 : 0));
2303 /* inputs */
2304 /* PW 1/2/5 (1ah/1bh/1eh) */
2305 parm = AC_PWRST_D3;
2306 set_pin_power_state(codec, 0x1a, &parm);
2307 set_pin_power_state(codec, 0x1b, &parm);
2308 set_pin_power_state(codec, 0x1e, &parm);
2309 if (imux_is_smixer)
2310 parm = AC_PWRST_D0;
2311 /* SW0 (17h), AIW 0/1 (13h/14h) */
2312 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2313 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2314 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2315
2316 /* outputs */
2317 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2318 parm = AC_PWRST_D3;
2319 set_pin_power_state(codec, 0x19, &parm);
2320 if (spec->smart51_enabled)
2321 set_pin_power_state(codec, 0x1b, &parm);
2322 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2323 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2324
2325 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2326 if (is_8ch) {
2327 parm = AC_PWRST_D3;
2328 set_pin_power_state(codec, 0x22, &parm);
2329 if (spec->smart51_enabled)
2330 set_pin_power_state(codec, 0x1a, &parm);
2331 snd_hda_codec_write(codec, 0x26, 0,
2332 AC_VERB_SET_POWER_STATE, parm);
2333 snd_hda_codec_write(codec, 0x24, 0,
2334 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002335 } else if (codec->vendor_id == 0x11064397) {
2336 /* PW7(23h), SW2(27h), AOW2(25h) */
2337 parm = AC_PWRST_D3;
2338 set_pin_power_state(codec, 0x23, &parm);
2339 if (spec->smart51_enabled)
2340 set_pin_power_state(codec, 0x1a, &parm);
2341 snd_hda_codec_write(codec, 0x27, 0,
2342 AC_VERB_SET_POWER_STATE, parm);
2343 snd_hda_codec_write(codec, 0x25, 0,
2344 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002345 }
2346
2347 /* PW 3/4/7 (1ch/1dh/23h) */
2348 parm = AC_PWRST_D3;
2349 /* force to D0 for internal Speaker */
2350 set_pin_power_state(codec, 0x1c, &parm);
2351 set_pin_power_state(codec, 0x1d, &parm);
2352 if (is_8ch)
2353 set_pin_power_state(codec, 0x23, &parm);
2354
2355 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2356 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2357 imux_is_smixer ? AC_PWRST_D0 : parm);
2358 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2359 if (is_8ch) {
2360 snd_hda_codec_write(codec, 0x25, 0,
2361 AC_VERB_SET_POWER_STATE, parm);
2362 snd_hda_codec_write(codec, 0x27, 0,
2363 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002364 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2365 snd_hda_codec_write(codec, 0x25, 0,
2366 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002367}
2368
Lydia Wang518bf3b2009-10-10 19:07:29 +08002369static int patch_vt1708S(struct hda_codec *codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002370static int patch_vt1708B_8ch(struct hda_codec *codec)
2371{
2372 struct via_spec *spec;
2373 int err;
2374
Lydia Wang518bf3b2009-10-10 19:07:29 +08002375 if (get_codec_type(codec) == VT1708BCE)
2376 return patch_vt1708S(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002377 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002378 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002379 if (spec == NULL)
2380 return -ENOMEM;
2381
Takashi Iwai620e2b22011-06-17 17:19:19 +02002382 spec->aa_mix_nid = 0x16;
2383
Josepch Chanf7278fd2007-12-13 16:40:40 +01002384 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002385 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002386 if (err < 0) {
2387 via_free(codec);
2388 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002389 }
2390
Josepch Chanf7278fd2007-12-13 16:40:40 +01002391 codec->patch_ops = via_patch_ops;
2392
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002393 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2394
Josepch Chanf7278fd2007-12-13 16:40:40 +01002395 return 0;
2396}
2397
2398static int patch_vt1708B_4ch(struct hda_codec *codec)
2399{
2400 struct via_spec *spec;
2401 int err;
2402
2403 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002404 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002405 if (spec == NULL)
2406 return -ENOMEM;
2407
Josepch Chanf7278fd2007-12-13 16:40:40 +01002408 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002409 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002410 if (err < 0) {
2411 via_free(codec);
2412 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002413 }
2414
Josepch Chanf7278fd2007-12-13 16:40:40 +01002415 codec->patch_ops = via_patch_ops;
2416
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002417 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2418
Joseph Chanc577b8a2006-11-29 15:29:40 +01002419 return 0;
2420}
2421
Harald Welted949cac2008-09-09 15:56:01 +08002422/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002423static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002424 /* Enable Mic Boost Volume backdoor */
2425 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002426 /* don't bybass mixer */
2427 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002428 { }
2429};
2430
Takashi Iwai9da29272009-05-07 16:31:14 +02002431/* fill out digital output widgets; one for master and one for slave outputs */
2432static void fill_dig_outs(struct hda_codec *codec)
2433{
2434 struct via_spec *spec = codec->spec;
2435 int i;
2436
2437 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2438 hda_nid_t nid;
2439 int conn;
2440
2441 nid = spec->autocfg.dig_out_pins[i];
2442 if (!nid)
2443 continue;
2444 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2445 if (conn < 1)
2446 continue;
2447 if (!spec->multiout.dig_out_nid)
2448 spec->multiout.dig_out_nid = nid;
2449 else {
2450 spec->slave_dig_outs[0] = nid;
2451 break; /* at most two dig outs */
2452 }
2453 }
2454}
2455
Takashi Iwai12daef62011-06-18 17:45:49 +02002456static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002457{
2458 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002459 hda_nid_t dig_nid;
2460 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002461
Takashi Iwai12daef62011-06-18 17:45:49 +02002462 if (!spec->autocfg.dig_in_pin)
2463 return;
Harald Welted949cac2008-09-09 15:56:01 +08002464
Takashi Iwai12daef62011-06-18 17:45:49 +02002465 dig_nid = codec->start_nid;
2466 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2467 unsigned int wcaps = get_wcaps(codec, dig_nid);
2468 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2469 continue;
2470 if (!(wcaps & AC_WCAP_DIGITAL))
2471 continue;
2472 if (!(wcaps & AC_WCAP_CONN_LIST))
2473 continue;
2474 err = get_connection_index(codec, dig_nid,
2475 spec->autocfg.dig_in_pin);
2476 if (err >= 0) {
2477 spec->dig_in_nid = dig_nid;
2478 break;
2479 }
2480 }
Harald Welted949cac2008-09-09 15:56:01 +08002481}
2482
Lydia Wang6369bcf2009-10-10 19:08:31 +08002483static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2484 int offset, int num_steps, int step_size)
2485{
2486 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2487 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2488 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2489 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2490 (0 << AC_AMPCAP_MUTE_SHIFT));
2491}
2492
Harald Welted949cac2008-09-09 15:56:01 +08002493static int patch_vt1708S(struct hda_codec *codec)
2494{
2495 struct via_spec *spec;
2496 int err;
2497
2498 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002499 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002500 if (spec == NULL)
2501 return -ENOMEM;
2502
Takashi Iwai620e2b22011-06-17 17:19:19 +02002503 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002504 override_mic_boost(codec, 0x1a, 0, 3, 40);
2505 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002506
Harald Welted949cac2008-09-09 15:56:01 +08002507 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002508 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002509 if (err < 0) {
2510 via_free(codec);
2511 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002512 }
2513
Takashi Iwai096a8852011-06-20 12:09:02 +02002514 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002515
Harald Welted949cac2008-09-09 15:56:01 +08002516 codec->patch_ops = via_patch_ops;
2517
Lydia Wang518bf3b2009-10-10 19:07:29 +08002518 /* correct names for VT1708BCE */
2519 if (get_codec_type(codec) == VT1708BCE) {
2520 kfree(codec->chip_name);
2521 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2522 snprintf(codec->bus->card->mixername,
2523 sizeof(codec->bus->card->mixername),
2524 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002525 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002526 /* correct names for VT1705 */
2527 if (codec->vendor_id == 0x11064397) {
2528 kfree(codec->chip_name);
2529 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2530 snprintf(codec->bus->card->mixername,
2531 sizeof(codec->bus->card->mixername),
2532 "%s %s", codec->vendor_name, codec->chip_name);
2533 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002534 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002535 return 0;
2536}
2537
2538/* Patch for VT1702 */
2539
Takashi Iwai096a8852011-06-20 12:09:02 +02002540static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002541 /* mixer enable */
2542 {0x1, 0xF88, 0x3},
2543 /* GPIO 0~2 */
2544 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002545 { }
2546};
2547
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002548static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2549{
2550 int imux_is_smixer =
2551 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2552 unsigned int parm;
2553 /* inputs */
2554 /* PW 1/2/5 (14h/15h/18h) */
2555 parm = AC_PWRST_D3;
2556 set_pin_power_state(codec, 0x14, &parm);
2557 set_pin_power_state(codec, 0x15, &parm);
2558 set_pin_power_state(codec, 0x18, &parm);
2559 if (imux_is_smixer)
2560 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2561 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2562 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2563 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2564 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2565 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2566
2567 /* outputs */
2568 /* PW 3/4 (16h/17h) */
2569 parm = AC_PWRST_D3;
2570 set_pin_power_state(codec, 0x17, &parm);
2571 set_pin_power_state(codec, 0x16, &parm);
2572 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2573 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2574 imux_is_smixer ? AC_PWRST_D0 : parm);
2575 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2576 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2577}
2578
Harald Welted949cac2008-09-09 15:56:01 +08002579static int patch_vt1702(struct hda_codec *codec)
2580{
2581 struct via_spec *spec;
2582 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002583
2584 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002585 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002586 if (spec == NULL)
2587 return -ENOMEM;
2588
Takashi Iwai620e2b22011-06-17 17:19:19 +02002589 spec->aa_mix_nid = 0x1a;
2590
Takashi Iwai12daef62011-06-18 17:45:49 +02002591 /* limit AA path volume to 0 dB */
2592 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2593 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2594 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2595 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2596 (1 << AC_AMPCAP_MUTE_SHIFT));
2597
Harald Welted949cac2008-09-09 15:56:01 +08002598 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002599 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002600 if (err < 0) {
2601 via_free(codec);
2602 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002603 }
2604
Takashi Iwai096a8852011-06-20 12:09:02 +02002605 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002606
Harald Welted949cac2008-09-09 15:56:01 +08002607 codec->patch_ops = via_patch_ops;
2608
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002609 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002610 return 0;
2611}
2612
Lydia Wangeb7188c2009-10-10 19:08:34 +08002613/* Patch for VT1718S */
2614
Takashi Iwai096a8852011-06-20 12:09:02 +02002615static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002616 /* Enable MW0 adjust Gain 5 */
2617 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002618 /* Enable Boost Volume backdoor */
2619 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002620
Lydia Wangeb7188c2009-10-10 19:08:34 +08002621 { }
2622};
2623
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002624static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2625{
2626 struct via_spec *spec = codec->spec;
2627 int imux_is_smixer;
2628 unsigned int parm;
2629 /* MUX6 (1eh) = stereo mixer */
2630 imux_is_smixer =
2631 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2632 /* inputs */
2633 /* PW 5/6/7 (29h/2ah/2bh) */
2634 parm = AC_PWRST_D3;
2635 set_pin_power_state(codec, 0x29, &parm);
2636 set_pin_power_state(codec, 0x2a, &parm);
2637 set_pin_power_state(codec, 0x2b, &parm);
2638 if (imux_is_smixer)
2639 parm = AC_PWRST_D0;
2640 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2641 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2642 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2643 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2644 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2645
2646 /* outputs */
2647 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2648 parm = AC_PWRST_D3;
2649 set_pin_power_state(codec, 0x27, &parm);
2650 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2651 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2652
2653 /* PW2 (26h), AOW2 (ah) */
2654 parm = AC_PWRST_D3;
2655 set_pin_power_state(codec, 0x26, &parm);
2656 if (spec->smart51_enabled)
2657 set_pin_power_state(codec, 0x2b, &parm);
2658 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2659
2660 /* PW0 (24h), AOW0 (8h) */
2661 parm = AC_PWRST_D3;
2662 set_pin_power_state(codec, 0x24, &parm);
2663 if (!spec->hp_independent_mode) /* check for redirected HP */
2664 set_pin_power_state(codec, 0x28, &parm);
2665 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2666 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2667 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2668 imux_is_smixer ? AC_PWRST_D0 : parm);
2669
2670 /* PW1 (25h), AOW1 (9h) */
2671 parm = AC_PWRST_D3;
2672 set_pin_power_state(codec, 0x25, &parm);
2673 if (spec->smart51_enabled)
2674 set_pin_power_state(codec, 0x2a, &parm);
2675 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2676
2677 if (spec->hp_independent_mode) {
2678 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2679 parm = AC_PWRST_D3;
2680 set_pin_power_state(codec, 0x28, &parm);
2681 snd_hda_codec_write(codec, 0x1b, 0,
2682 AC_VERB_SET_POWER_STATE, parm);
2683 snd_hda_codec_write(codec, 0x34, 0,
2684 AC_VERB_SET_POWER_STATE, parm);
2685 snd_hda_codec_write(codec, 0xc, 0,
2686 AC_VERB_SET_POWER_STATE, parm);
2687 }
2688}
2689
Lydia Wangeb7188c2009-10-10 19:08:34 +08002690static int patch_vt1718S(struct hda_codec *codec)
2691{
2692 struct via_spec *spec;
2693 int err;
2694
2695 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002696 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002697 if (spec == NULL)
2698 return -ENOMEM;
2699
Takashi Iwai620e2b22011-06-17 17:19:19 +02002700 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002701 override_mic_boost(codec, 0x2b, 0, 3, 40);
2702 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002703
Lydia Wangeb7188c2009-10-10 19:08:34 +08002704 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002705 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002706 if (err < 0) {
2707 via_free(codec);
2708 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002709 }
2710
Takashi Iwai096a8852011-06-20 12:09:02 +02002711 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002712
Lydia Wangeb7188c2009-10-10 19:08:34 +08002713 codec->patch_ops = via_patch_ops;
2714
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002715 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2716
Lydia Wangeb7188c2009-10-10 19:08:34 +08002717 return 0;
2718}
Lydia Wangf3db4232009-10-10 19:08:41 +08002719
2720/* Patch for VT1716S */
2721
2722static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2723 struct snd_ctl_elem_info *uinfo)
2724{
2725 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2726 uinfo->count = 1;
2727 uinfo->value.integer.min = 0;
2728 uinfo->value.integer.max = 1;
2729 return 0;
2730}
2731
2732static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2733 struct snd_ctl_elem_value *ucontrol)
2734{
2735 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2736 int index = 0;
2737
2738 index = snd_hda_codec_read(codec, 0x26, 0,
2739 AC_VERB_GET_CONNECT_SEL, 0);
2740 if (index != -1)
2741 *ucontrol->value.integer.value = index;
2742
2743 return 0;
2744}
2745
2746static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2747 struct snd_ctl_elem_value *ucontrol)
2748{
2749 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2750 struct via_spec *spec = codec->spec;
2751 int index = *ucontrol->value.integer.value;
2752
2753 snd_hda_codec_write(codec, 0x26, 0,
2754 AC_VERB_SET_CONNECT_SEL, index);
2755 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002756 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002757 return 1;
2758}
2759
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002760static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002761 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2762 {
2763 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2764 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002765 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08002766 .count = 1,
2767 .info = vt1716s_dmic_info,
2768 .get = vt1716s_dmic_get,
2769 .put = vt1716s_dmic_put,
2770 },
2771 {} /* end */
2772};
2773
2774
2775/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002776static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002777 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
2778 { } /* end */
2779};
2780
Takashi Iwai096a8852011-06-20 12:09:02 +02002781static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002782 /* Enable Boost Volume backdoor */
2783 {0x1, 0xf8a, 0x80},
2784 /* don't bybass mixer */
2785 {0x1, 0xf88, 0xc0},
2786 /* Enable mono output */
2787 {0x1, 0xf90, 0x08},
2788 { }
2789};
2790
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002791static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
2792{
2793 struct via_spec *spec = codec->spec;
2794 int imux_is_smixer;
2795 unsigned int parm;
2796 unsigned int mono_out, present;
2797 /* SW0 (17h) = stereo mixer */
2798 imux_is_smixer =
2799 (snd_hda_codec_read(codec, 0x17, 0,
2800 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
2801 /* inputs */
2802 /* PW 1/2/5 (1ah/1bh/1eh) */
2803 parm = AC_PWRST_D3;
2804 set_pin_power_state(codec, 0x1a, &parm);
2805 set_pin_power_state(codec, 0x1b, &parm);
2806 set_pin_power_state(codec, 0x1e, &parm);
2807 if (imux_is_smixer)
2808 parm = AC_PWRST_D0;
2809 /* SW0 (17h), AIW0(13h) */
2810 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2811 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2812
2813 parm = AC_PWRST_D3;
2814 set_pin_power_state(codec, 0x1e, &parm);
2815 /* PW11 (22h) */
2816 if (spec->dmic_enabled)
2817 set_pin_power_state(codec, 0x22, &parm);
2818 else
2819 snd_hda_codec_write(codec, 0x22, 0,
2820 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
2821
2822 /* SW2(26h), AIW1(14h) */
2823 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
2824 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2825
2826 /* outputs */
2827 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2828 parm = AC_PWRST_D3;
2829 set_pin_power_state(codec, 0x19, &parm);
2830 /* Smart 5.1 PW2(1bh) */
2831 if (spec->smart51_enabled)
2832 set_pin_power_state(codec, 0x1b, &parm);
2833 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2834 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2835
2836 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
2837 parm = AC_PWRST_D3;
2838 set_pin_power_state(codec, 0x23, &parm);
2839 /* Smart 5.1 PW1(1ah) */
2840 if (spec->smart51_enabled)
2841 set_pin_power_state(codec, 0x1a, &parm);
2842 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
2843
2844 /* Smart 5.1 PW5(1eh) */
2845 if (spec->smart51_enabled)
2846 set_pin_power_state(codec, 0x1e, &parm);
2847 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
2848
2849 /* Mono out */
2850 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
2851 present = snd_hda_jack_detect(codec, 0x1c);
2852
2853 if (present)
2854 mono_out = 0;
2855 else {
2856 present = snd_hda_jack_detect(codec, 0x1d);
2857 if (!spec->hp_independent_mode && present)
2858 mono_out = 0;
2859 else
2860 mono_out = 1;
2861 }
2862 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
2863 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
2864 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
2865 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
2866
2867 /* PW 3/4 (1ch/1dh) */
2868 parm = AC_PWRST_D3;
2869 set_pin_power_state(codec, 0x1c, &parm);
2870 set_pin_power_state(codec, 0x1d, &parm);
2871 /* HP Independent Mode, power on AOW3 */
2872 if (spec->hp_independent_mode)
2873 snd_hda_codec_write(codec, 0x25, 0,
2874 AC_VERB_SET_POWER_STATE, parm);
2875
2876 /* force to D0 for internal Speaker */
2877 /* MW0 (16h), AOW0 (10h) */
2878 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2879 imux_is_smixer ? AC_PWRST_D0 : parm);
2880 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
2881 mono_out ? AC_PWRST_D0 : parm);
2882}
2883
Lydia Wangf3db4232009-10-10 19:08:41 +08002884static int patch_vt1716S(struct hda_codec *codec)
2885{
2886 struct via_spec *spec;
2887 int err;
2888
2889 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002890 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002891 if (spec == NULL)
2892 return -ENOMEM;
2893
Takashi Iwai620e2b22011-06-17 17:19:19 +02002894 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002895 override_mic_boost(codec, 0x1a, 0, 3, 40);
2896 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002897
Lydia Wangf3db4232009-10-10 19:08:41 +08002898 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002899 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002900 if (err < 0) {
2901 via_free(codec);
2902 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08002903 }
2904
Takashi Iwai096a8852011-06-20 12:09:02 +02002905 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08002906
Lydia Wangf3db4232009-10-10 19:08:41 +08002907 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
2908 spec->num_mixers++;
2909
2910 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
2911
2912 codec->patch_ops = via_patch_ops;
2913
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002914 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08002915 return 0;
2916}
Lydia Wang25eaba22009-10-10 19:08:43 +08002917
2918/* for vt2002P */
2919
Takashi Iwai096a8852011-06-20 12:09:02 +02002920static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08002921 /* Class-D speaker related verbs */
2922 {0x1, 0xfe0, 0x4},
2923 {0x1, 0xfe9, 0x80},
2924 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08002925 /* Enable Boost Volume backdoor */
2926 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08002927 /* Enable AOW0 to MW9 */
2928 {0x1, 0xfb8, 0x88},
2929 { }
2930};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002931
Takashi Iwai096a8852011-06-20 12:09:02 +02002932static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08002933 /* Enable Boost Volume backdoor */
2934 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08002935 /* Enable AOW0 to MW9 */
2936 {0x1, 0xfb8, 0x88},
2937 { }
2938};
Lydia Wang25eaba22009-10-10 19:08:43 +08002939
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002940static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
2941{
2942 struct via_spec *spec = codec->spec;
2943 int imux_is_smixer;
2944 unsigned int parm;
2945 unsigned int present;
2946 /* MUX9 (1eh) = stereo mixer */
2947 imux_is_smixer =
2948 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2949 /* inputs */
2950 /* PW 5/6/7 (29h/2ah/2bh) */
2951 parm = AC_PWRST_D3;
2952 set_pin_power_state(codec, 0x29, &parm);
2953 set_pin_power_state(codec, 0x2a, &parm);
2954 set_pin_power_state(codec, 0x2b, &parm);
2955 parm = AC_PWRST_D0;
2956 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
2957 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2958 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2959 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2960 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2961
2962 /* outputs */
2963 /* AOW0 (8h)*/
2964 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2965
Lydia Wang118909562011-03-23 17:57:34 +08002966 if (spec->codec_type == VT1802) {
2967 /* PW4 (28h), MW4 (18h), MUX4(38h) */
2968 parm = AC_PWRST_D3;
2969 set_pin_power_state(codec, 0x28, &parm);
2970 snd_hda_codec_write(codec, 0x18, 0,
2971 AC_VERB_SET_POWER_STATE, parm);
2972 snd_hda_codec_write(codec, 0x38, 0,
2973 AC_VERB_SET_POWER_STATE, parm);
2974 } else {
2975 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
2976 parm = AC_PWRST_D3;
2977 set_pin_power_state(codec, 0x26, &parm);
2978 snd_hda_codec_write(codec, 0x1c, 0,
2979 AC_VERB_SET_POWER_STATE, parm);
2980 snd_hda_codec_write(codec, 0x37, 0,
2981 AC_VERB_SET_POWER_STATE, parm);
2982 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002983
Lydia Wang118909562011-03-23 17:57:34 +08002984 if (spec->codec_type == VT1802) {
2985 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
2986 parm = AC_PWRST_D3;
2987 set_pin_power_state(codec, 0x25, &parm);
2988 snd_hda_codec_write(codec, 0x15, 0,
2989 AC_VERB_SET_POWER_STATE, parm);
2990 snd_hda_codec_write(codec, 0x35, 0,
2991 AC_VERB_SET_POWER_STATE, parm);
2992 } else {
2993 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
2994 parm = AC_PWRST_D3;
2995 set_pin_power_state(codec, 0x25, &parm);
2996 snd_hda_codec_write(codec, 0x19, 0,
2997 AC_VERB_SET_POWER_STATE, parm);
2998 snd_hda_codec_write(codec, 0x35, 0,
2999 AC_VERB_SET_POWER_STATE, parm);
3000 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003001
3002 if (spec->hp_independent_mode)
3003 snd_hda_codec_write(codec, 0x9, 0,
3004 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3005
3006 /* Class-D */
3007 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3008 present = snd_hda_jack_detect(codec, 0x25);
3009
3010 parm = AC_PWRST_D3;
3011 set_pin_power_state(codec, 0x24, &parm);
3012 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003013 if (spec->codec_type == VT1802)
3014 snd_hda_codec_write(codec, 0x14, 0,
3015 AC_VERB_SET_POWER_STATE, parm);
3016 else
3017 snd_hda_codec_write(codec, 0x18, 0,
3018 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003019 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3020
3021 /* Mono Out */
3022 present = snd_hda_jack_detect(codec, 0x26);
3023
3024 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003025 if (spec->codec_type == VT1802) {
3026 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3027 snd_hda_codec_write(codec, 0x33, 0,
3028 AC_VERB_SET_POWER_STATE, parm);
3029 snd_hda_codec_write(codec, 0x1c, 0,
3030 AC_VERB_SET_POWER_STATE, parm);
3031 snd_hda_codec_write(codec, 0x3c, 0,
3032 AC_VERB_SET_POWER_STATE, parm);
3033 } else {
3034 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3035 snd_hda_codec_write(codec, 0x31, 0,
3036 AC_VERB_SET_POWER_STATE, parm);
3037 snd_hda_codec_write(codec, 0x17, 0,
3038 AC_VERB_SET_POWER_STATE, parm);
3039 snd_hda_codec_write(codec, 0x3b, 0,
3040 AC_VERB_SET_POWER_STATE, parm);
3041 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003042 /* MW9 (21h) */
3043 if (imux_is_smixer || !is_aa_path_mute(codec))
3044 snd_hda_codec_write(codec, 0x21, 0,
3045 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3046 else
3047 snd_hda_codec_write(codec, 0x21, 0,
3048 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3049}
Lydia Wang25eaba22009-10-10 19:08:43 +08003050
3051/* patch for vt2002P */
3052static int patch_vt2002P(struct hda_codec *codec)
3053{
3054 struct via_spec *spec;
3055 int err;
3056
3057 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003058 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003059 if (spec == NULL)
3060 return -ENOMEM;
3061
Takashi Iwai620e2b22011-06-17 17:19:19 +02003062 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003063 override_mic_boost(codec, 0x2b, 0, 3, 40);
3064 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003065
Lydia Wang25eaba22009-10-10 19:08:43 +08003066 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003067 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003068 if (err < 0) {
3069 via_free(codec);
3070 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003071 }
3072
Lydia Wang118909562011-03-23 17:57:34 +08003073 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003074 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003075 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003076 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003077
Lydia Wang25eaba22009-10-10 19:08:43 +08003078 codec->patch_ops = via_patch_ops;
3079
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003080 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003081 return 0;
3082}
Lydia Wangab6734e2009-10-10 19:08:46 +08003083
3084/* for vt1812 */
3085
Takashi Iwai096a8852011-06-20 12:09:02 +02003086static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003087 /* Enable Boost Volume backdoor */
3088 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003089 /* Enable AOW0 to MW9 */
3090 {0x1, 0xfb8, 0xa8},
3091 { }
3092};
3093
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003094static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3095{
3096 struct via_spec *spec = codec->spec;
3097 int imux_is_smixer =
3098 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3099 unsigned int parm;
3100 unsigned int present;
3101 /* MUX10 (1eh) = stereo mixer */
3102 imux_is_smixer =
3103 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3104 /* inputs */
3105 /* PW 5/6/7 (29h/2ah/2bh) */
3106 parm = AC_PWRST_D3;
3107 set_pin_power_state(codec, 0x29, &parm);
3108 set_pin_power_state(codec, 0x2a, &parm);
3109 set_pin_power_state(codec, 0x2b, &parm);
3110 parm = AC_PWRST_D0;
3111 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3112 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3113 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3114 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3115 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3116
3117 /* outputs */
3118 /* AOW0 (8h)*/
3119 snd_hda_codec_write(codec, 0x8, 0,
3120 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3121
3122 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3123 parm = AC_PWRST_D3;
3124 set_pin_power_state(codec, 0x28, &parm);
3125 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3126 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3127
3128 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3129 parm = AC_PWRST_D3;
3130 set_pin_power_state(codec, 0x25, &parm);
3131 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3132 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3133 if (spec->hp_independent_mode)
3134 snd_hda_codec_write(codec, 0x9, 0,
3135 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3136
3137 /* Internal Speaker */
3138 /* PW0 (24h), MW0(14h), MUX0(34h) */
3139 present = snd_hda_jack_detect(codec, 0x25);
3140
3141 parm = AC_PWRST_D3;
3142 set_pin_power_state(codec, 0x24, &parm);
3143 if (present) {
3144 snd_hda_codec_write(codec, 0x14, 0,
3145 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3146 snd_hda_codec_write(codec, 0x34, 0,
3147 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3148 } else {
3149 snd_hda_codec_write(codec, 0x14, 0,
3150 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3151 snd_hda_codec_write(codec, 0x34, 0,
3152 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3153 }
3154
3155
3156 /* Mono Out */
3157 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3158 present = snd_hda_jack_detect(codec, 0x28);
3159
3160 parm = AC_PWRST_D3;
3161 set_pin_power_state(codec, 0x31, &parm);
3162 if (present) {
3163 snd_hda_codec_write(codec, 0x1c, 0,
3164 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3165 snd_hda_codec_write(codec, 0x3c, 0,
3166 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3167 snd_hda_codec_write(codec, 0x3e, 0,
3168 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3169 } else {
3170 snd_hda_codec_write(codec, 0x1c, 0,
3171 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3172 snd_hda_codec_write(codec, 0x3c, 0,
3173 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3174 snd_hda_codec_write(codec, 0x3e, 0,
3175 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3176 }
3177
3178 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3179 parm = AC_PWRST_D3;
3180 set_pin_power_state(codec, 0x33, &parm);
3181 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3182 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3183
3184}
Lydia Wangab6734e2009-10-10 19:08:46 +08003185
3186/* patch for vt1812 */
3187static int patch_vt1812(struct hda_codec *codec)
3188{
3189 struct via_spec *spec;
3190 int err;
3191
3192 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003193 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003194 if (spec == NULL)
3195 return -ENOMEM;
3196
Takashi Iwai620e2b22011-06-17 17:19:19 +02003197 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003198 override_mic_boost(codec, 0x2b, 0, 3, 40);
3199 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003200
Lydia Wangab6734e2009-10-10 19:08:46 +08003201 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003202 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003203 if (err < 0) {
3204 via_free(codec);
3205 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003206 }
3207
Takashi Iwai096a8852011-06-20 12:09:02 +02003208 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003209
Lydia Wangab6734e2009-10-10 19:08:46 +08003210 codec->patch_ops = via_patch_ops;
3211
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003212 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003213 return 0;
3214}
3215
Joseph Chanc577b8a2006-11-29 15:29:40 +01003216/*
3217 * patch entries
3218 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003219static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003220 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3221 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3222 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3223 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3224 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003225 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003226 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003227 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003228 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003229 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003230 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003231 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003232 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003233 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003234 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003235 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003236 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003237 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003238 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003239 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003240 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003241 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003242 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003243 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003244 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003245 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003246 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003247 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003248 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003249 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003250 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003251 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003252 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003253 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003254 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003255 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003256 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003257 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003258 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003259 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003260 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003261 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003262 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003263 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003264 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003265 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003266 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003267 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003268 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003269 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003270 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003271 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003272 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003273 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003274 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003275 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003276 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003277 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003278 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003279 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003280 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003281 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003282 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003283 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003284 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003285 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003286 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003287 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003288 { .id = 0x11060428, .name = "VT1718S",
3289 .patch = patch_vt1718S},
3290 { .id = 0x11064428, .name = "VT1718S",
3291 .patch = patch_vt1718S},
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08003292 { .id = 0x11060441, .name = "VT2020",
3293 .patch = patch_vt1718S},
3294 { .id = 0x11064441, .name = "VT1828S",
3295 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003296 { .id = 0x11060433, .name = "VT1716S",
3297 .patch = patch_vt1716S},
3298 { .id = 0x1106a721, .name = "VT1716S",
3299 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003300 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3301 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003302 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003303 { .id = 0x11060440, .name = "VT1818S",
3304 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003305 { .id = 0x11060446, .name = "VT1802",
3306 .patch = patch_vt2002P},
3307 { .id = 0x11068446, .name = "VT1802",
3308 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003309 {} /* terminator */
3310};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003311
3312MODULE_ALIAS("snd-hda-codec-id:1106*");
3313
3314static struct hda_codec_preset_list via_list = {
3315 .preset = snd_hda_preset_via,
3316 .owner = THIS_MODULE,
3317};
3318
3319MODULE_LICENSE("GPL");
3320MODULE_DESCRIPTION("VIA HD-audio codec");
3321
3322static int __init patch_via_init(void)
3323{
3324 return snd_hda_add_codec_preset(&via_list);
3325}
3326
3327static void __exit patch_via_exit(void)
3328{
3329 snd_hda_delete_codec_preset(&via_list);
3330}
3331
3332module_init(patch_via_init)
3333module_exit(patch_via_exit)