blob: bd6ffa602f2250fcc8d89a0eac229a6f8a3caa65 [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
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +010057#define NID_MAPPING (-1)
58
Joseph Chanc577b8a2006-11-29 15:29:40 +010059/* amp values */
60#define AMP_VAL_IDX_SHIFT 19
61#define AMP_VAL_IDX_MASK (0x0f<<19)
62
Joseph Chanc577b8a2006-11-29 15:29:40 +010063/* Pin Widget NID */
64#define VT1708_HP_NID 0x13
65#define VT1708_DIGOUT_NID 0x14
66#define VT1708_DIGIN_NID 0x16
Josepch Chanf7278fd2007-12-13 16:40:40 +010067#define VT1708_DIGIN_PIN 0x26
Harald Welted949cac2008-09-09 15:56:01 +080068#define VT1708_HP_PIN_NID 0x20
69#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010070
71#define VT1709_HP_DAC_NID 0x28
72#define VT1709_DIGOUT_NID 0x13
73#define VT1709_DIGIN_NID 0x17
Josepch Chanf7278fd2007-12-13 16:40:40 +010074#define VT1709_DIGIN_PIN 0x25
75
76#define VT1708B_HP_NID 0x25
77#define VT1708B_DIGOUT_NID 0x12
78#define VT1708B_DIGIN_NID 0x15
79#define VT1708B_DIGIN_PIN 0x21
Joseph Chanc577b8a2006-11-29 15:29:40 +010080
Harald Welted949cac2008-09-09 15:56:01 +080081#define VT1708S_HP_NID 0x25
82#define VT1708S_DIGOUT_NID 0x12
83
84#define VT1702_HP_NID 0x17
85#define VT1702_DIGOUT_NID 0x11
86
Harald Welted7426322008-09-15 22:43:23 +080087enum VIA_HDA_CODEC {
88 UNKNOWN = -1,
89 VT1708,
90 VT1709_10CH,
91 VT1709_6CH,
92 VT1708B_8CH,
93 VT1708B_4CH,
94 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080095 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080096 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080097 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080098 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080099 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +0800100 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +0800101 VT1802,
Harald Welted7426322008-09-15 22:43:23 +0800102 CODEC_TYPES,
103};
104
Lydia Wang118909562011-03-23 17:57:34 +0800105#define VT2002P_COMPATIBLE(spec) \
106 ((spec)->codec_type == VT2002P ||\
107 (spec)->codec_type == VT1812 ||\
108 (spec)->codec_type == VT1802)
109
Takashi Iwai4a796162011-06-17 17:53:38 +0200110struct nid_path {
111 int depth;
112 hda_nid_t path[5];
113 short idx[5];
114};
115
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800116struct via_spec {
117 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200118 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800119 unsigned int num_mixers;
120
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200121 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800122 unsigned int num_iverbs;
123
Takashi Iwai82673bc2011-06-17 16:24:21 +0200124 char stream_name_analog[32];
Takashi Iwai7eb56e842011-06-18 16:40:14 +0200125 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200126 const struct hda_pcm_stream *stream_analog_playback;
127 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800128
Takashi Iwai82673bc2011-06-17 16:24:21 +0200129 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200130 const struct hda_pcm_stream *stream_digital_playback;
131 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800132
133 /* playback */
134 struct hda_multi_out multiout;
135 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200136 hda_nid_t hp_dac_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800137
Takashi Iwai4a796162011-06-17 17:53:38 +0200138 struct nid_path out_path[4];
139 struct nid_path hp_path;
140 struct nid_path hp_dep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200141 struct nid_path speaker_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200142
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800143 /* capture */
144 unsigned int num_adc_nids;
Takashi Iwaia766d0d2011-06-17 09:01:29 +0200145 hda_nid_t adc_nids[3];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800146 hda_nid_t mux_nids[3];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200147 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800148 hda_nid_t dig_in_nid;
149 hda_nid_t dig_in_pin;
150
151 /* capture source */
152 const struct hda_input_mux *input_mux;
153 unsigned int cur_mux[3];
154
155 /* PCM information */
156 struct hda_pcm pcm_rec[3];
157
158 /* dynamic controls, init_verbs and input_mux */
159 struct auto_pin_cfg autocfg;
160 struct snd_array kctls;
161 struct hda_input_mux private_imux[2];
162 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
163
164 /* HP mode source */
165 const struct hda_input_mux *hp_mux;
166 unsigned int hp_independent_mode;
167 unsigned int hp_independent_mode_index;
Lydia Wangf3db4232009-10-10 19:08:41 +0800168 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200169 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800170 enum VIA_HDA_CODEC codec_type;
171
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200172 /* smart51 setup */
173 unsigned int smart51_nums;
174 hda_nid_t smart51_pins[2];
175 int smart51_idxs[2];
176 const char *smart51_labels[2];
177 unsigned int smart51_enabled;
178
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800179 /* work to check hp jack state */
180 struct hda_codec *codec;
181 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200182 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800183 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800184
185 void (*set_widgets_power_state)(struct hda_codec *codec);
186
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800187#ifdef CONFIG_SND_HDA_POWER_SAVE
188 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200189 int num_loopbacks;
190 struct hda_amp_list loopback_list[8];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800191#endif
192};
193
Lydia Wang0341ccd2011-03-22 16:25:03 +0800194static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100195static struct via_spec * via_new_spec(struct hda_codec *codec)
196{
197 struct via_spec *spec;
198
199 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
200 if (spec == NULL)
201 return NULL;
202
203 codec->spec = spec;
204 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800205 spec->codec_type = get_codec_type(codec);
206 /* VT1708BCE & VT1708S are almost same */
207 if (spec->codec_type == VT1708BCE)
208 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100209 return spec;
210}
211
Lydia Wang744ff5f2009-10-10 19:07:26 +0800212static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800213{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800214 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800215 u16 ven_id = vendor_id >> 16;
216 u16 dev_id = vendor_id & 0xffff;
217 enum VIA_HDA_CODEC codec_type;
218
219 /* get codec type */
220 if (ven_id != 0x1106)
221 codec_type = UNKNOWN;
222 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
223 codec_type = VT1708;
224 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
225 codec_type = VT1709_10CH;
226 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
227 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800228 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800229 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800230 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
231 codec_type = VT1708BCE;
232 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800233 codec_type = VT1708B_4CH;
234 else if ((dev_id & 0xfff) == 0x397
235 && (dev_id >> 12) < 8)
236 codec_type = VT1708S;
237 else if ((dev_id & 0xfff) == 0x398
238 && (dev_id >> 12) < 8)
239 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800240 else if ((dev_id & 0xfff) == 0x428
241 && (dev_id >> 12) < 8)
242 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800243 else if (dev_id == 0x0433 || dev_id == 0xa721)
244 codec_type = VT1716S;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +0800245 else if (dev_id == 0x0441 || dev_id == 0x4441)
246 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800247 else if (dev_id == 0x0438 || dev_id == 0x4438)
248 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800249 else if (dev_id == 0x0448)
250 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800251 else if (dev_id == 0x0440)
252 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800253 else if ((dev_id & 0xfff) == 0x446)
254 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800255 else
256 codec_type = UNKNOWN;
257 return codec_type;
258};
259
Lydia Wangec7e7e42011-03-24 12:43:44 +0800260#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800261#define VIA_HP_EVENT 0x01
262#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200263#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800264
Joseph Chanc577b8a2006-11-29 15:29:40 +0100265enum {
266 VIA_CTL_WIDGET_VOL,
267 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800268 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100269};
270
Lydia Wangf5271102009-10-10 19:07:35 +0800271static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800272static int is_aa_path_mute(struct hda_codec *codec);
273
274static void vt1708_start_hp_work(struct via_spec *spec)
275{
276 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
277 return;
278 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200279 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800280 if (!delayed_work_pending(&spec->vt1708_hp_work))
281 schedule_delayed_work(&spec->vt1708_hp_work,
282 msecs_to_jiffies(100));
283}
284
285static void vt1708_stop_hp_work(struct via_spec *spec)
286{
287 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
288 return;
289 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
290 && !is_aa_path_mute(spec->codec))
291 return;
292 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200293 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100294 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800295}
Lydia Wangf5271102009-10-10 19:07:35 +0800296
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800297static void set_widgets_power_state(struct hda_codec *codec)
298{
299 struct via_spec *spec = codec->spec;
300 if (spec->set_widgets_power_state)
301 spec->set_widgets_power_state(codec);
302}
Lydia Wang25eaba22009-10-10 19:08:43 +0800303
Lydia Wangf5271102009-10-10 19:07:35 +0800304static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
305 struct snd_ctl_elem_value *ucontrol)
306{
307 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
308 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
309
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800310 set_widgets_power_state(codec);
Lydia Wangf5271102009-10-10 19:07:35 +0800311 analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800312 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
313 if (is_aa_path_mute(codec))
314 vt1708_start_hp_work(codec->spec);
315 else
316 vt1708_stop_hp_work(codec->spec);
317 }
Lydia Wangf5271102009-10-10 19:07:35 +0800318 return change;
319}
320
321/* modify .put = snd_hda_mixer_amp_switch_put */
322#define ANALOG_INPUT_MUTE \
323 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
324 .name = NULL, \
325 .index = 0, \
326 .info = snd_hda_mixer_amp_switch_info, \
327 .get = snd_hda_mixer_amp_switch_get, \
328 .put = analog_input_switch_put, \
329 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
330
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200331static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100332 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
333 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800334 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100335};
336
Lydia Wangab6734e2009-10-10 19:08:46 +0800337
Joseph Chanc577b8a2006-11-29 15:29:40 +0100338/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200339static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
340 const struct snd_kcontrol_new *tmpl,
341 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100342{
343 struct snd_kcontrol_new *knew;
344
Takashi Iwai603c4012008-07-30 15:01:44 +0200345 snd_array_init(&spec->kctls, sizeof(*knew), 32);
346 knew = snd_array_new(&spec->kctls);
347 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200348 return NULL;
349 *knew = *tmpl;
350 if (!name)
351 name = tmpl->name;
352 if (name) {
353 knew->name = kstrdup(name, GFP_KERNEL);
354 if (!knew->name)
355 return NULL;
356 }
357 return knew;
358}
359
360static int __via_add_control(struct via_spec *spec, int type, const char *name,
361 int idx, unsigned long val)
362{
363 struct snd_kcontrol_new *knew;
364
365 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
366 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100367 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200368 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100369 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100370 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100371 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100372 return 0;
373}
374
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200375#define via_add_control(spec, type, name, val) \
376 __via_add_control(spec, type, name, 0, val)
377
Takashi Iwai291c9e32011-06-17 16:15:26 +0200378#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100379
Takashi Iwai603c4012008-07-30 15:01:44 +0200380static void via_free_kctls(struct hda_codec *codec)
381{
382 struct via_spec *spec = codec->spec;
383
384 if (spec->kctls.list) {
385 struct snd_kcontrol_new *kctl = spec->kctls.list;
386 int i;
387 for (i = 0; i < spec->kctls.used; i++)
388 kfree(kctl[i].name);
389 }
390 snd_array_free(&spec->kctls);
391}
392
Joseph Chanc577b8a2006-11-29 15:29:40 +0100393/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800394static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200395 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100396{
397 char name[32];
398 int err;
399
400 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200401 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100402 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
403 if (err < 0)
404 return err;
405 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200406 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100407 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
408 if (err < 0)
409 return err;
410 return 0;
411}
412
Takashi Iwai5d417622011-06-20 11:32:27 +0200413/* return the index of the given widget nid as the source of mux;
414 * return -1 if not found;
415 * if num_conns is non-NULL, set the total number of connections
416 */
417static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux,
418 hda_nid_t nid, int *num_conns)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100419{
Takashi Iwai5d417622011-06-20 11:32:27 +0200420 hda_nid_t conn[HDA_MAX_NUM_INPUTS];
421 int i, nums;
422
423 nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
424 if (num_conns)
425 *num_conns = nums;
426 for (i = 0; i < nums; i++)
427 if (conn[i] == nid)
428 return i;
429 return -1;
430}
431
432#define get_connection_index(codec, mux, nid) \
433 __get_connection_index(codec, mux, nid, NULL)
434
435/* unmute input amp and select the specificed source */
436static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid,
437 hda_nid_t src, hda_nid_t mix)
438{
439 int idx, num_conns;
440
441 idx = __get_connection_index(codec, nid, src, &num_conns);
442 if (idx < 0)
443 return;
444
445 /* select the route explicitly when multiple connections exist */
446 if (num_conns > 1)
Lydia Wang377ff312009-10-10 19:08:55 +0800447 snd_hda_codec_write(codec, nid, 0,
Takashi Iwai5d417622011-06-20 11:32:27 +0200448 AC_VERB_SET_CONNECT_SEL, idx);
449 /* unmute if the input amp is present */
450 if (!(query_amp_caps(codec, nid, HDA_INPUT) &
451 (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)))
452 return;
453 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
454 AMP_IN_UNMUTE(idx));
455
456 /* unmute AA-path if present */
457 if (!mix)
458 return;
459 idx = __get_connection_index(codec, nid, mix, NULL);
460 if (idx >= 0)
461 snd_hda_codec_write(codec, nid, 0,
462 AC_VERB_SET_AMP_GAIN_MUTE,
463 AMP_IN_UNMUTE(idx));
464}
465
466/* set the given pin as output */
467static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
468 int pin_type)
469{
470 if (!pin)
471 return;
472 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
473 pin_type);
474 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
475 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200476 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100477}
478
Takashi Iwai5d417622011-06-20 11:32:27 +0200479static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin,
480 int pin_type, struct nid_path *path)
481{
482 struct via_spec *spec = codec->spec;
483 unsigned int caps;
484 hda_nid_t nid;
485 int i;
486
487 if (!pin)
488 return;
489
490 init_output_pin(codec, pin, pin_type);
491 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
492 if (caps & AC_AMPCAP_MUTE) {
493 unsigned int val;
494 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
495 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
496 AMP_OUT_MUTE | val);
497 }
498
499 /* initialize the output path */
500 nid = pin;
501 for (i = 0; i < path->depth; i++) {
502 unmute_and_select(codec, nid, path->idx[i], spec->aa_mix_nid);
503 nid = path->path[i];
504 if (query_amp_caps(codec, nid, HDA_OUTPUT) &
505 (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE))
506 snd_hda_codec_write(codec, nid, 0,
507 AC_VERB_SET_AMP_GAIN_MUTE,
508 AMP_OUT_UNMUTE);
509 }
510}
511
Joseph Chanc577b8a2006-11-29 15:29:40 +0100512
513static void via_auto_init_multi_out(struct hda_codec *codec)
514{
515 struct via_spec *spec = codec->spec;
516 int i;
517
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200518 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwai5d417622011-06-20 11:32:27 +0200519 via_auto_init_output(codec, spec->autocfg.line_out_pins[i],
520 PIN_OUT, &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100521}
522
523static void via_auto_init_hp_out(struct hda_codec *codec)
524{
525 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100526
Takashi Iwai5d417622011-06-20 11:32:27 +0200527 if (spec->hp_dac_nid)
528 via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
529 &spec->hp_path);
530 else
531 via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
532 &spec->hp_dep_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100533}
534
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200535static void via_auto_init_speaker_out(struct hda_codec *codec)
536{
537 struct via_spec *spec = codec->spec;
538
539 if (spec->autocfg.speaker_outs)
540 via_auto_init_output(codec, spec->autocfg.speaker_pins[0],
541 PIN_OUT, &spec->speaker_path);
542}
543
Takashi Iwaif4a78282011-06-17 18:46:48 +0200544static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200545
Joseph Chanc577b8a2006-11-29 15:29:40 +0100546static void via_auto_init_analog_input(struct hda_codec *codec)
547{
548 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200549 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200550 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200551 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200552 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100553
Takashi Iwai096a8852011-06-20 12:09:02 +0200554 /* init ADCs */
555 for (i = 0; i < spec->num_adc_nids; i++) {
556 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
557 AC_VERB_SET_AMP_GAIN_MUTE,
558 AMP_IN_UNMUTE(0));
559 }
560
561 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200562 for (i = 0; i < cfg->num_inputs; i++) {
563 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200564 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200565 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100566 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200567 ctl = PIN_VREF50;
568 else
569 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100570 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200571 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100572 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200573
574 /* init input-src */
575 for (i = 0; i < spec->num_adc_nids; i++) {
576 const struct hda_input_mux *imux = spec->input_mux;
577 if (!imux || !spec->mux_nids[i])
578 continue;
579 snd_hda_codec_write(codec, spec->mux_nids[i], 0,
580 AC_VERB_SET_CONNECT_SEL,
581 imux->items[spec->cur_mux[i]].index);
582 }
583
584 /* init aa-mixer */
585 if (!spec->aa_mix_nid)
586 return;
587 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
588 ARRAY_SIZE(conn));
589 for (i = 0; i < num_conns; i++) {
590 unsigned int caps = get_wcaps(codec, conn[i]);
591 if (get_wcaps_type(caps) == AC_WID_PIN)
592 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
593 AC_VERB_SET_AMP_GAIN_MUTE,
594 AMP_IN_MUTE(i));
595 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100596}
Lydia Wangf5271102009-10-10 19:07:35 +0800597
598static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
599 unsigned int *affected_parm)
600{
601 unsigned parm;
602 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
603 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
604 >> AC_DEFCFG_MISC_SHIFT
605 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800606 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200607 unsigned present = 0;
608
609 no_presence |= spec->no_pin_power_ctl;
610 if (!no_presence)
611 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200612 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800613 || ((no_presence || present)
614 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800615 *affected_parm = AC_PWRST_D0; /* if it's connected */
616 parm = AC_PWRST_D0;
617 } else
618 parm = AC_PWRST_D3;
619
620 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
621}
622
Takashi Iwai24088a52011-06-17 16:59:21 +0200623static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
624 struct snd_ctl_elem_info *uinfo)
625{
626 static const char * const texts[] = {
627 "Disabled", "Enabled"
628 };
629
630 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
631 uinfo->count = 1;
632 uinfo->value.enumerated.items = 2;
633 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
634 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
635 strcpy(uinfo->value.enumerated.name,
636 texts[uinfo->value.enumerated.item]);
637 return 0;
638}
639
640static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
641 struct snd_ctl_elem_value *ucontrol)
642{
643 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
644 struct via_spec *spec = codec->spec;
645 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
646 return 0;
647}
648
649static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
650 struct snd_ctl_elem_value *ucontrol)
651{
652 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
653 struct via_spec *spec = codec->spec;
654 unsigned int val = !ucontrol->value.enumerated.item[0];
655
656 if (val == spec->no_pin_power_ctl)
657 return 0;
658 spec->no_pin_power_ctl = val;
659 set_widgets_power_state(codec);
660 return 1;
661}
662
663static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
664 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
665 .name = "Dynamic Power-Control",
666 .info = via_pin_power_ctl_info,
667 .get = via_pin_power_ctl_get,
668 .put = via_pin_power_ctl_put,
669};
670
671
Joseph Chanc577b8a2006-11-29 15:29:40 +0100672/*
673 * input MUX handling
674 */
675static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
676 struct snd_ctl_elem_info *uinfo)
677{
678 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
679 struct via_spec *spec = codec->spec;
680 return snd_hda_input_mux_info(spec->input_mux, uinfo);
681}
682
683static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
684 struct snd_ctl_elem_value *ucontrol)
685{
686 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
687 struct via_spec *spec = codec->spec;
688 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
689
690 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
691 return 0;
692}
693
694static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
695 struct snd_ctl_elem_value *ucontrol)
696{
697 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
698 struct via_spec *spec = codec->spec;
699 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800700 int ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100701
Takashi Iwai337b9d02009-07-07 18:18:59 +0200702 if (!spec->mux_nids[adc_idx])
703 return -EINVAL;
Lydia Wanga80e6e32009-10-10 19:07:55 +0800704 /* switch to D0 beofre change index */
705 if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
706 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
707 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
708 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800709
710 ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
711 spec->mux_nids[adc_idx],
712 &spec->cur_mux[adc_idx]);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800713 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800714 set_widgets_power_state(codec);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800715
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800716 return ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100717}
718
Harald Welte0aa62ae2008-09-09 15:58:27 +0800719static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
720 struct snd_ctl_elem_info *uinfo)
721{
722 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
723 struct via_spec *spec = codec->spec;
724 return snd_hda_input_mux_info(spec->hp_mux, uinfo);
725}
726
727static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
728 struct snd_ctl_elem_value *ucontrol)
729{
730 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800731 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800732
Takashi Iwaiece8d042011-06-19 16:24:21 +0200733 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800734 return 0;
735}
736
Harald Welte0aa62ae2008-09-09 15:58:27 +0800737static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
738 struct snd_ctl_elem_value *ucontrol)
739{
740 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
741 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100742 hda_nid_t nid = kcontrol->private_value;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800743 unsigned int pinsel = ucontrol->value.enumerated.item[0];
Lydia Wangcdc17842009-10-10 19:07:47 +0800744 /* Get Independent Mode index of headphone pin widget */
745 spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
746 ? 1 : 0;
Takashi Iwaiece8d042011-06-19 16:24:21 +0200747 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800748
Lydia Wangce0e5a92011-03-22 16:22:37 +0800749 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800750 set_widgets_power_state(codec);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800751 return 0;
752}
753
Takashi Iwaiece8d042011-06-19 16:24:21 +0200754static const struct snd_kcontrol_new via_hp_mixer = {
755 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
756 .name = "Independent HP",
757 .info = via_independent_hp_info,
758 .get = via_independent_hp_get,
759 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800760};
761
Takashi Iwai3d83e572010-04-14 14:36:23 +0200762static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100763{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200764 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100765 struct snd_kcontrol_new *knew;
766 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100767
Takashi Iwaiece8d042011-06-19 16:24:21 +0200768 nid = spec->autocfg.hp_pins[0];
769 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200770 if (knew == NULL)
771 return -ENOMEM;
772
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100773 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
774 knew->private_value = nid;
775
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100776 return 0;
777}
778
Lydia Wang1564b282009-10-10 19:07:52 +0800779static void notify_aa_path_ctls(struct hda_codec *codec)
780{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200781 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800782 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800783
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200784 for (i = 0; i < spec->smart51_nums; i++) {
785 struct snd_kcontrol *ctl;
786 struct snd_ctl_elem_id id;
787 memset(&id, 0, sizeof(id));
788 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
789 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800790 ctl = snd_hda_find_mixer_ctl(codec, id.name);
791 if (ctl)
792 snd_ctl_notify(codec->bus->card,
793 SNDRV_CTL_EVENT_MASK_VALUE,
794 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800795 }
796}
797
798static void mute_aa_path(struct hda_codec *codec, int mute)
799{
800 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200801 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800802 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200803
Lydia Wang1564b282009-10-10 19:07:52 +0800804 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200805 for (i = 0; i < spec->smart51_nums; i++) {
806 if (spec->smart51_idxs[i] < 0)
807 continue;
808 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
809 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800810 HDA_AMP_MUTE, val);
811 }
812}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200813
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200814static bool is_smart51_candidate(struct hda_codec *codec, hda_nid_t pin)
Lydia Wang1564b282009-10-10 19:07:52 +0800815{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200816 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200817 const struct auto_pin_cfg *cfg = &spec->autocfg;
818 int i;
819
820 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaif4a78282011-06-17 18:46:48 +0200821 unsigned int defcfg;
822 if (pin != cfg->inputs[i].pin)
823 continue;
824 if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
825 return false;
826 defcfg = snd_hda_codec_get_pincfg(codec, pin);
827 if (snd_hda_get_input_pin_attr(defcfg) < INPUT_PIN_ATTR_NORMAL)
828 return false;
829 return true;
Lydia Wang1564b282009-10-10 19:07:52 +0800830 }
Takashi Iwaif4a78282011-06-17 18:46:48 +0200831 return false;
Lydia Wang1564b282009-10-10 19:07:52 +0800832}
833
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200834static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
835{
836 struct via_spec *spec = codec->spec;
837 int i;
838
839 for (i = 0; i < spec->smart51_nums; i++)
840 if (spec->smart51_pins[i] == pin)
841 return true;
842 return false;
843}
844
Lydia Wang1564b282009-10-10 19:07:52 +0800845static int via_smart51_info(struct snd_kcontrol *kcontrol,
846 struct snd_ctl_elem_info *uinfo)
847{
848 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
849 uinfo->count = 1;
850 uinfo->value.integer.min = 0;
851 uinfo->value.integer.max = 1;
852 return 0;
853}
854
855static int via_smart51_get(struct snd_kcontrol *kcontrol,
856 struct snd_ctl_elem_value *ucontrol)
857{
858 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
859 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800860 int on = 1;
861 int i;
862
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200863 for (i = 0; i < spec->smart51_nums; i++) {
864 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwaif4a78282011-06-17 18:46:48 +0200865 unsigned int ctl;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200866 ctl = snd_hda_codec_read(codec, nid, 0,
867 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200868 if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
869 on = 0;
Lydia Wang1564b282009-10-10 19:07:52 +0800870 }
871 *ucontrol->value.integer.value = on;
872 return 0;
873}
874
875static int via_smart51_put(struct snd_kcontrol *kcontrol,
876 struct snd_ctl_elem_value *ucontrol)
877{
878 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
879 struct via_spec *spec = codec->spec;
880 int out_in = *ucontrol->value.integer.value
881 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800882 int i;
883
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200884 for (i = 0; i < spec->smart51_nums; i++) {
885 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200886 unsigned int parm;
887
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200888 parm = snd_hda_codec_read(codec, nid, 0,
889 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
890 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
891 parm |= out_in;
892 snd_hda_codec_write(codec, nid, 0,
893 AC_VERB_SET_PIN_WIDGET_CONTROL,
894 parm);
895 if (out_in == AC_PINCTL_OUT_EN) {
896 mute_aa_path(codec, 1);
897 notify_aa_path_ctls(codec);
898 }
Lydia Wang1564b282009-10-10 19:07:52 +0800899 }
900 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800901 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800902 return 1;
903}
904
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200905static const struct snd_kcontrol_new via_smart51_mixer = {
906 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
907 .name = "Smart 5.1",
908 .count = 1,
909 .info = via_smart51_info,
910 .get = via_smart51_get,
911 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800912};
913
Takashi Iwaif4a78282011-06-17 18:46:48 +0200914static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100915{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200916 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100917
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200918 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800919 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200920 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100921 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100922 return 0;
923}
924
Lydia Wangf5271102009-10-10 19:07:35 +0800925/* check AA path's mute statue */
926static int is_aa_path_mute(struct hda_codec *codec)
927{
928 int mute = 1;
Lydia Wangf5271102009-10-10 19:07:35 +0800929 int start_idx;
930 int end_idx;
931 int i;
932 struct via_spec *spec = codec->spec;
933 /* get nid of MW0 and start & end index */
934 switch (spec->codec_type) {
935 case VT1708B_8CH:
936 case VT1708B_4CH:
937 case VT1708S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800938 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800939 start_idx = 2;
940 end_idx = 4;
941 break;
942 case VT1702:
Lydia Wangf5271102009-10-10 19:07:35 +0800943 start_idx = 1;
944 end_idx = 3;
945 break;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800946 case VT1718S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800947 start_idx = 1;
948 end_idx = 3;
949 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800950 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800951 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800952 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800953 start_idx = 0;
954 end_idx = 2;
955 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800956 default:
957 return 0;
958 }
959 /* check AA path's mute status */
960 for (i = start_idx; i <= end_idx; i++) {
961 unsigned int con_list = snd_hda_codec_read(
Takashi Iwai620e2b22011-06-17 17:19:19 +0200962 codec, spec->aa_mix_nid, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
Lydia Wangf5271102009-10-10 19:07:35 +0800963 int shift = 8 * (i % 4);
964 hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
965 unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
966 if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
967 /* check mute status while the pin is connected */
Takashi Iwai620e2b22011-06-17 17:19:19 +0200968 int mute_l = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 0,
Lydia Wangf5271102009-10-10 19:07:35 +0800969 HDA_INPUT, i) >> 7;
Takashi Iwai620e2b22011-06-17 17:19:19 +0200970 int mute_r = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 1,
Lydia Wangf5271102009-10-10 19:07:35 +0800971 HDA_INPUT, i) >> 7;
972 if (!mute_l || !mute_r) {
973 mute = 0;
974 break;
975 }
976 }
977 }
978 return mute;
979}
980
981/* enter/exit analog low-current mode */
982static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
983{
984 struct via_spec *spec = codec->spec;
985 static int saved_stream_idle = 1; /* saved stream idle status */
986 int enable = is_aa_path_mute(codec);
987 unsigned int verb = 0;
988 unsigned int parm = 0;
989
990 if (stream_idle == -1) /* stream status did not change */
991 enable = enable && saved_stream_idle;
992 else {
993 enable = enable && stream_idle;
994 saved_stream_idle = stream_idle;
995 }
996
997 /* decide low current mode's verb & parameter */
998 switch (spec->codec_type) {
999 case VT1708B_8CH:
1000 case VT1708B_4CH:
1001 verb = 0xf70;
1002 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1003 break;
1004 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001005 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001006 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001007 verb = 0xf73;
1008 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1009 break;
1010 case VT1702:
1011 verb = 0xf73;
1012 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1013 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001014 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001015 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +08001016 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +08001017 verb = 0xf93;
1018 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1019 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001020 default:
1021 return; /* other codecs are not supported */
1022 }
1023 /* send verb */
1024 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1025}
1026
Joseph Chanc577b8a2006-11-29 15:29:40 +01001027/*
1028 * generic initialization of ADC, input mixers and output mixers
1029 */
Takashi Iwai096a8852011-06-20 12:09:02 +02001030static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001031 /* power down jack detect function */
1032 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001033 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001034};
1035
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001036static void substream_set_idle(struct hda_codec *codec,
1037 struct snd_pcm_substream *substream)
1038{
1039 int idle = substream->pstr->substream_opened == 1
1040 && substream->ref_count == 0;
1041 analog_low_current_mode(codec, idle);
1042}
1043
Takashi Iwaiece8d042011-06-19 16:24:21 +02001044static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001045 struct hda_codec *codec,
1046 struct snd_pcm_substream *substream)
1047{
1048 struct via_spec *spec = codec->spec;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001049
1050 if (!spec->hp_independent_mode)
1051 spec->multiout.hp_nid = spec->hp_dac_nid;
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001052 substream_set_idle(codec, substream);
Takashi Iwai9a081602008-02-12 18:37:26 +01001053 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1054 hinfo);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001055}
1056
Takashi Iwaiece8d042011-06-19 16:24:21 +02001057static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001058 struct hda_codec *codec,
1059 struct snd_pcm_substream *substream)
1060{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001061 struct via_spec *spec = codec->spec;
1062
1063 spec->multiout.hp_nid = 0;
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001064 substream_set_idle(codec, substream);
Takashi Iwai9af74212011-06-18 16:17:45 +02001065 return 0;
1066}
1067
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001068static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1069 struct hda_codec *codec,
1070 struct snd_pcm_substream *substream)
1071{
1072 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001073
Takashi Iwaiece8d042011-06-19 16:24:21 +02001074 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001075 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001076 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1077 return -EBUSY;
1078 substream_set_idle(codec, substream);
1079 return 0;
1080}
1081
1082static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1083 struct hda_codec *codec,
1084 struct snd_pcm_substream *substream)
1085{
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001086 substream_set_idle(codec, substream);
1087 return 0;
1088}
1089
1090static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1091 struct hda_codec *codec,
1092 unsigned int stream_tag,
1093 unsigned int format,
1094 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001095{
1096 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001097
Takashi Iwaiece8d042011-06-19 16:24:21 +02001098 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1099 format, substream);
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001100 vt1708_start_hp_work(spec);
1101 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001102}
1103
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001104static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1105 struct hda_codec *codec,
1106 unsigned int stream_tag,
1107 unsigned int format,
1108 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001109{
1110 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001111
Takashi Iwaiece8d042011-06-19 16:24:21 +02001112 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1113 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001114 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001115 return 0;
1116}
1117
1118static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1119 struct hda_codec *codec,
1120 struct snd_pcm_substream *substream)
1121{
1122 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001123
Takashi Iwaiece8d042011-06-19 16:24:21 +02001124 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001125 vt1708_stop_hp_work(spec);
1126 return 0;
1127}
1128
1129static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1130 struct hda_codec *codec,
1131 struct snd_pcm_substream *substream)
1132{
1133 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001134
Takashi Iwaiece8d042011-06-19 16:24:21 +02001135 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001136 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001137 return 0;
1138}
1139
Joseph Chanc577b8a2006-11-29 15:29:40 +01001140/*
1141 * Digital out
1142 */
1143static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1144 struct hda_codec *codec,
1145 struct snd_pcm_substream *substream)
1146{
1147 struct via_spec *spec = codec->spec;
1148 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1149}
1150
1151static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1152 struct hda_codec *codec,
1153 struct snd_pcm_substream *substream)
1154{
1155 struct via_spec *spec = codec->spec;
1156 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1157}
1158
Harald Welte5691ec72008-09-15 22:42:26 +08001159static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001160 struct hda_codec *codec,
1161 unsigned int stream_tag,
1162 unsigned int format,
1163 struct snd_pcm_substream *substream)
1164{
1165 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001166 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1167 stream_tag, format, substream);
1168}
Harald Welte5691ec72008-09-15 22:42:26 +08001169
Takashi Iwai9da29272009-05-07 16:31:14 +02001170static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1171 struct hda_codec *codec,
1172 struct snd_pcm_substream *substream)
1173{
1174 struct via_spec *spec = codec->spec;
1175 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001176 return 0;
1177}
1178
Joseph Chanc577b8a2006-11-29 15:29:40 +01001179/*
1180 * Analog capture
1181 */
1182static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1183 struct hda_codec *codec,
1184 unsigned int stream_tag,
1185 unsigned int format,
1186 struct snd_pcm_substream *substream)
1187{
1188 struct via_spec *spec = codec->spec;
1189
1190 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1191 stream_tag, 0, format);
1192 return 0;
1193}
1194
1195static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1196 struct hda_codec *codec,
1197 struct snd_pcm_substream *substream)
1198{
1199 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001200 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001201 return 0;
1202}
1203
Takashi Iwai9af74212011-06-18 16:17:45 +02001204static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001205 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001206 .channels_min = 2,
1207 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001208 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001209 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001210 .open = via_playback_multi_pcm_open,
1211 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001212 .prepare = via_playback_multi_pcm_prepare,
1213 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001214 },
1215};
1216
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001217static const struct hda_pcm_stream via_pcm_hp_playback = {
1218 .substreams = 1,
1219 .channels_min = 2,
1220 .channels_max = 2,
1221 /* NID is set in via_build_pcms */
1222 .ops = {
1223 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001224 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001225 .prepare = via_playback_hp_pcm_prepare,
1226 .cleanup = via_playback_hp_pcm_cleanup
1227 },
1228};
1229
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001230static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001231 .substreams = 1,
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001232 .channels_min = 2,
1233 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001234 /* NID is set in via_build_pcms */
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001235 /* We got noisy outputs on the right channel on VT1708 when
1236 * 24bit samples are used. Until any workaround is found,
1237 * disable the 24bit format, so far.
1238 */
1239 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1240 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001241 .open = via_playback_multi_pcm_open,
1242 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001243 .prepare = via_playback_multi_pcm_prepare,
1244 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001245 },
1246};
1247
Takashi Iwai9af74212011-06-18 16:17:45 +02001248static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001249 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001250 .channels_min = 2,
1251 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001252 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001253 .ops = {
1254 .prepare = via_capture_pcm_prepare,
1255 .cleanup = via_capture_pcm_cleanup
1256 },
1257};
1258
Takashi Iwai9af74212011-06-18 16:17:45 +02001259static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001260 .substreams = 1,
1261 .channels_min = 2,
1262 .channels_max = 2,
1263 /* NID is set in via_build_pcms */
1264 .ops = {
1265 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001266 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001267 .prepare = via_dig_playback_pcm_prepare,
1268 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001269 },
1270};
1271
Takashi Iwai9af74212011-06-18 16:17:45 +02001272static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001273 .substreams = 1,
1274 .channels_min = 2,
1275 .channels_max = 2,
1276};
1277
Takashi Iwai370bafb2011-06-20 12:47:45 +02001278/*
1279 * slave controls for virtual master
1280 */
1281static const char * const via_slave_vols[] = {
1282 "Front Playback Volume",
1283 "Surround Playback Volume",
1284 "Center Playback Volume",
1285 "LFE Playback Volume",
1286 "Side Playback Volume",
1287 "Headphone Playback Volume",
1288 "Speaker Playback Volume",
1289 NULL,
1290};
1291
1292static const char * const via_slave_sws[] = {
1293 "Front Playback Switch",
1294 "Surround Playback Switch",
1295 "Center Playback Switch",
1296 "LFE Playback Switch",
1297 "Side Playback Switch",
1298 "Headphone Playback Switch",
1299 "Speaker Playback Switch",
1300 NULL,
1301};
1302
Joseph Chanc577b8a2006-11-29 15:29:40 +01001303static int via_build_controls(struct hda_codec *codec)
1304{
1305 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001306 struct snd_kcontrol *kctl;
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001307 const struct snd_kcontrol_new *knew;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001308 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001309
Takashi Iwai24088a52011-06-17 16:59:21 +02001310 if (spec->set_widgets_power_state)
1311 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1312 return -ENOMEM;
1313
Joseph Chanc577b8a2006-11-29 15:29:40 +01001314 for (i = 0; i < spec->num_mixers; i++) {
1315 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1316 if (err < 0)
1317 return err;
1318 }
1319
1320 if (spec->multiout.dig_out_nid) {
1321 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001322 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001323 spec->multiout.dig_out_nid);
1324 if (err < 0)
1325 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001326 err = snd_hda_create_spdif_share_sw(codec,
1327 &spec->multiout);
1328 if (err < 0)
1329 return err;
1330 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001331 }
1332 if (spec->dig_in_nid) {
1333 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1334 if (err < 0)
1335 return err;
1336 }
Lydia Wang17314372009-10-10 19:07:37 +08001337
Takashi Iwai370bafb2011-06-20 12:47:45 +02001338 /* if we have no master control, let's create it */
1339 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1340 unsigned int vmaster_tlv[4];
1341 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1342 HDA_OUTPUT, vmaster_tlv);
1343 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1344 vmaster_tlv, via_slave_vols);
1345 if (err < 0)
1346 return err;
1347 }
1348 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1349 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1350 NULL, via_slave_sws);
1351 if (err < 0)
1352 return err;
1353 }
1354
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001355 /* assign Capture Source enums to NID */
1356 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1357 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001358 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001359 if (err < 0)
1360 return err;
1361 }
1362
1363 /* other nid->control mapping */
1364 for (i = 0; i < spec->num_mixers; i++) {
1365 for (knew = spec->mixers[i]; knew->name; knew++) {
1366 if (knew->iface != NID_MAPPING)
1367 continue;
1368 kctl = snd_hda_find_mixer_ctl(codec, knew->name);
1369 if (kctl == NULL)
1370 continue;
1371 err = snd_hda_add_nid(codec, kctl, 0,
1372 knew->subdevice);
1373 }
1374 }
1375
Lydia Wang17314372009-10-10 19:07:37 +08001376 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001377 set_widgets_power_state(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001378 analog_low_current_mode(codec, 1);
1379
Takashi Iwai603c4012008-07-30 15:01:44 +02001380 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001381 return 0;
1382}
1383
1384static int via_build_pcms(struct hda_codec *codec)
1385{
1386 struct via_spec *spec = codec->spec;
1387 struct hda_pcm *info = spec->pcm_rec;
1388
1389 codec->num_pcms = 1;
1390 codec->pcm_info = info;
1391
Takashi Iwai82673bc2011-06-17 16:24:21 +02001392 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1393 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001394 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001395
1396 if (!spec->stream_analog_playback)
1397 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001398 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001399 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001400 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1401 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001402 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1403 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001404
1405 if (!spec->stream_analog_capture)
1406 spec->stream_analog_capture = &via_pcm_analog_capture;
1407 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1408 *spec->stream_analog_capture;
1409 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1410 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1411 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001412
1413 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1414 codec->num_pcms++;
1415 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001416 snprintf(spec->stream_name_digital,
1417 sizeof(spec->stream_name_digital),
1418 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001419 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001420 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001421 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001422 if (!spec->stream_digital_playback)
1423 spec->stream_digital_playback =
1424 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001425 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001426 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001427 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1428 spec->multiout.dig_out_nid;
1429 }
1430 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001431 if (!spec->stream_digital_capture)
1432 spec->stream_digital_capture =
1433 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001434 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001435 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001436 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1437 spec->dig_in_nid;
1438 }
1439 }
1440
Takashi Iwaiece8d042011-06-19 16:24:21 +02001441 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001442 codec->num_pcms++;
1443 info++;
1444 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1445 "%s HP", codec->chip_name);
1446 info->name = spec->stream_name_hp;
1447 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1448 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001449 spec->hp_dac_nid;
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001450 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001451 return 0;
1452}
1453
1454static void via_free(struct hda_codec *codec)
1455{
1456 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001457
1458 if (!spec)
1459 return;
1460
Takashi Iwai603c4012008-07-30 15:01:44 +02001461 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001462 vt1708_stop_hp_work(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001463 kfree(codec->spec);
1464}
1465
Takashi Iwai64be2852011-06-17 16:51:39 +02001466/* mute/unmute outputs */
1467static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1468 hda_nid_t *pins, bool mute)
1469{
1470 int i;
1471 for (i = 0; i < num_pins; i++)
1472 snd_hda_codec_write(codec, pins[i], 0,
1473 AC_VERB_SET_PIN_WIDGET_CONTROL,
1474 mute ? 0 : PIN_OUT);
1475}
1476
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001477/* mute internal speaker if line-out is plugged */
1478static void via_line_automute(struct hda_codec *codec, int present)
1479{
1480 struct via_spec *spec = codec->spec;
1481
1482 if (!spec->autocfg.speaker_outs)
1483 return;
1484 if (!present)
1485 present = snd_hda_jack_detect(codec,
1486 spec->autocfg.line_out_pins[0]);
1487 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1488 spec->autocfg.speaker_pins,
1489 present);
1490}
1491
Harald Welte69e52a82008-09-09 15:57:32 +08001492/* mute internal speaker if HP is plugged */
1493static void via_hp_automute(struct hda_codec *codec)
1494{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001495 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001496 struct via_spec *spec = codec->spec;
1497
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001498 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
1499 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai64be2852011-06-17 16:51:39 +02001500 toggle_output_mutes(codec, spec->autocfg.line_outs,
1501 spec->autocfg.line_out_pins,
1502 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001503 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001504 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001505}
1506
Harald Welte69e52a82008-09-09 15:57:32 +08001507static void via_gpio_control(struct hda_codec *codec)
1508{
1509 unsigned int gpio_data;
1510 unsigned int vol_counter;
1511 unsigned int vol;
1512 unsigned int master_vol;
1513
1514 struct via_spec *spec = codec->spec;
1515
1516 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1517 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1518
1519 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1520 0xF84, 0) & 0x3F0000) >> 16;
1521
1522 vol = vol_counter & 0x1F;
1523 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1524 AC_VERB_GET_AMP_GAIN_MUTE,
1525 AC_AMP_GET_INPUT);
1526
1527 if (gpio_data == 0x02) {
1528 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001529 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1530 AC_VERB_SET_PIN_WIDGET_CONTROL,
1531 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001532 if (vol_counter & 0x20) {
1533 /* decrease volume */
1534 if (vol > master_vol)
1535 vol = master_vol;
1536 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1537 0, HDA_AMP_VOLMASK,
1538 master_vol-vol);
1539 } else {
1540 /* increase volume */
1541 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1542 HDA_AMP_VOLMASK,
1543 ((master_vol+vol) > 0x2A) ? 0x2A :
1544 (master_vol+vol));
1545 }
1546 } else if (!(gpio_data & 0x02)) {
1547 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001548 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1549 AC_VERB_SET_PIN_WIDGET_CONTROL,
1550 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001551 }
1552}
1553
1554/* unsolicited event for jack sensing */
1555static void via_unsol_event(struct hda_codec *codec,
1556 unsigned int res)
1557{
1558 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001559
Lydia Wanga34df192009-10-10 19:08:01 +08001560 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001561 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001562
1563 res &= ~VIA_JACK_EVENT;
1564
1565 if (res == VIA_HP_EVENT)
1566 via_hp_automute(codec);
1567 else if (res == VIA_GPIO_EVENT)
1568 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001569 else if (res == VIA_LINE_EVENT)
1570 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001571}
1572
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001573#ifdef SND_HDA_NEEDS_RESUME
1574static int via_suspend(struct hda_codec *codec, pm_message_t state)
1575{
1576 struct via_spec *spec = codec->spec;
1577 vt1708_stop_hp_work(spec);
1578 return 0;
1579}
1580#endif
1581
Takashi Iwaicb53c622007-08-10 17:21:45 +02001582#ifdef CONFIG_SND_HDA_POWER_SAVE
1583static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1584{
1585 struct via_spec *spec = codec->spec;
1586 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1587}
1588#endif
1589
Joseph Chanc577b8a2006-11-29 15:29:40 +01001590/*
1591 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001592
1593static int via_init(struct hda_codec *codec);
1594
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001595static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001596 .build_controls = via_build_controls,
1597 .build_pcms = via_build_pcms,
1598 .init = via_init,
1599 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001600 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001601#ifdef SND_HDA_NEEDS_RESUME
1602 .suspend = via_suspend,
1603#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001604#ifdef CONFIG_SND_HDA_POWER_SAVE
1605 .check_power_status = via_check_power_status,
1606#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001607};
1608
Takashi Iwai4a796162011-06-17 17:53:38 +02001609static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001610{
Takashi Iwai4a796162011-06-17 17:53:38 +02001611 struct via_spec *spec = codec->spec;
1612 int i;
1613
1614 for (i = 0; i < spec->multiout.num_dacs; i++) {
1615 if (spec->multiout.dac_nids[i] == dac)
1616 return false;
1617 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001618 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001619 return false;
1620 return true;
1621}
1622
1623static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1624 hda_nid_t target_dac, struct nid_path *path,
1625 int depth, int wid_type)
1626{
1627 hda_nid_t conn[8];
1628 int i, nums;
1629
1630 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1631 for (i = 0; i < nums; i++) {
1632 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1633 continue;
1634 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1635 path->path[depth] = conn[i];
1636 path->idx[depth] = i;
1637 path->depth = ++depth;
1638 return true;
1639 }
1640 }
1641 if (depth > 4)
1642 return false;
1643 for (i = 0; i < nums; i++) {
1644 unsigned int type;
1645 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1646 if (type == AC_WID_AUD_OUT ||
1647 (wid_type != -1 && type != wid_type))
1648 continue;
1649 if (parse_output_path(codec, conn[i], target_dac,
1650 path, depth + 1, AC_WID_AUD_SEL)) {
1651 path->path[depth] = conn[i];
1652 path->idx[depth] = i;
1653 return true;
1654 }
1655 }
1656 return false;
1657}
1658
1659static int via_auto_fill_dac_nids(struct hda_codec *codec)
1660{
1661 struct via_spec *spec = codec->spec;
1662 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001663 int i;
1664 hda_nid_t nid;
1665
Joseph Chanc577b8a2006-11-29 15:29:40 +01001666 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001667 spec->multiout.num_dacs = cfg->line_outs;
1668 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001669 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001670 if (!nid)
1671 continue;
1672 if (parse_output_path(codec, nid, 0, &spec->out_path[i], 0, -1))
1673 spec->private_dac_nids[i] =
1674 spec->out_path[i].path[spec->out_path[i].depth - 1];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001675 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001676 return 0;
1677}
1678
Takashi Iwai4a796162011-06-17 17:53:38 +02001679static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
1680 hda_nid_t pin, hda_nid_t dac, int chs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001681{
Takashi Iwai4a796162011-06-17 17:53:38 +02001682 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001683 char name[32];
Takashi Iwai4a796162011-06-17 17:53:38 +02001684 hda_nid_t nid;
1685 int err;
1686
1687 if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
1688 nid = dac;
1689 else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
1690 nid = pin;
1691 else
1692 nid = 0;
1693 if (nid) {
1694 sprintf(name, "%s Playback Volume", pfx);
1695 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1696 HDA_COMPOSE_AMP_VAL(dac, chs, 0, HDA_OUTPUT));
1697 if (err < 0)
1698 return err;
1699 }
1700
1701 if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE)
1702 nid = dac;
1703 else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE)
1704 nid = pin;
1705 else
1706 nid = 0;
1707 if (nid) {
1708 sprintf(name, "%s Playback Switch", pfx);
1709 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1710 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1711 if (err < 0)
1712 return err;
1713 }
1714 return 0;
1715}
1716
Takashi Iwaif4a78282011-06-17 18:46:48 +02001717static void mangle_smart51(struct hda_codec *codec)
1718{
1719 struct via_spec *spec = codec->spec;
1720 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001721 int i, nums = 0;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001722
1723 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001724 if (is_smart51_candidate(codec, cfg->inputs[i].pin))
1725 nums++;
1726 }
1727 if (cfg->line_outs + nums < 3)
1728 return;
1729 for (i = 0; i < cfg->num_inputs; i++) {
1730 if (!is_smart51_candidate(codec, cfg->inputs[i].pin))
Takashi Iwaif4a78282011-06-17 18:46:48 +02001731 continue;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001732 spec->smart51_pins[spec->smart51_nums++] = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001733 cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin;
1734 if (cfg->line_outs == 3)
1735 break;
1736 }
1737}
1738
Takashi Iwai4a796162011-06-17 17:53:38 +02001739/* add playback controls from the parsed DAC table */
1740static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1741{
1742 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001743 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001744 static const char * const chname[4] = {
1745 "Front", "Surround", "C/LFE", "Side"
1746 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001747 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001748 int old_line_outs;
1749
1750 /* check smart51 */
1751 old_line_outs = cfg->line_outs;
1752 if (cfg->line_outs == 1)
1753 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001754
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001755 err = via_auto_fill_dac_nids(codec);
1756 if (err < 0)
1757 return err;
1758
Takashi Iwai4a796162011-06-17 17:53:38 +02001759 for (i = 0; i < cfg->line_outs; i++) {
1760 hda_nid_t pin, dac;
1761 pin = cfg->line_out_pins[i];
1762 dac = spec->multiout.dac_nids[i];
1763 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001764 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001765 if (i == HDA_CLFE) {
Takashi Iwai4a796162011-06-17 17:53:38 +02001766 err = create_ch_ctls(codec, "Center", pin, dac, 1);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001767 if (err < 0)
1768 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02001769 err = create_ch_ctls(codec, "LFE", pin, dac, 2);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001770 if (err < 0)
1771 return err;
1772 } else {
Takashi Iwai4a796162011-06-17 17:53:38 +02001773 err = create_ch_ctls(codec, chname[i], pin, dac, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001774 if (err < 0)
1775 return err;
1776 }
1777 }
1778
Takashi Iwai4a796162011-06-17 17:53:38 +02001779 idx = get_connection_index(codec, spec->aa_mix_nid,
1780 spec->multiout.dac_nids[0]);
1781 if (idx >= 0) {
1782 /* add control to mixer */
1783 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1784 "PCM Playback Volume",
1785 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1786 idx, HDA_INPUT));
1787 if (err < 0)
1788 return err;
1789 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1790 "PCM Playback Switch",
1791 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1792 idx, HDA_INPUT));
1793 if (err < 0)
1794 return err;
1795 }
1796
Takashi Iwaif4a78282011-06-17 18:46:48 +02001797 cfg->line_outs = old_line_outs;
1798
Joseph Chanc577b8a2006-11-29 15:29:40 +01001799 return 0;
1800}
1801
Harald Welte0aa62ae2008-09-09 15:58:27 +08001802static void create_hp_imux(struct via_spec *spec)
1803{
1804 int i;
1805 struct hda_input_mux *imux = &spec->private_imux[1];
Takashi Iwaiea734962011-01-17 11:29:34 +01001806 static const char * const texts[] = { "OFF", "ON", NULL};
Harald Welte0aa62ae2008-09-09 15:58:27 +08001807
1808 /* for hp mode select */
Takashi Iwai10a20af2010-09-09 16:28:02 +02001809 for (i = 0; texts[i]; i++)
1810 snd_hda_add_imux_item(imux, texts[i], i, NULL);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001811
1812 spec->hp_mux = &spec->private_imux[1];
1813}
1814
Takashi Iwai4a796162011-06-17 17:53:38 +02001815static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001816{
Takashi Iwai4a796162011-06-17 17:53:38 +02001817 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001818 int err;
1819
1820 if (!pin)
1821 return 0;
1822
Takashi Iwai4a796162011-06-17 17:53:38 +02001823 if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001824 spec->hp_dac_nid = spec->hp_path.path[spec->hp_path.depth - 1];
Takashi Iwai4a796162011-06-17 17:53:38 +02001825 spec->hp_independent_mode_index =
1826 spec->hp_path.idx[spec->hp_path.depth - 1];
1827 create_hp_imux(spec);
1828 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001829
Takashi Iwaiece8d042011-06-19 16:24:21 +02001830 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
1831 &spec->hp_dep_path, 0, -1) &&
1832 !spec->hp_dac_nid)
1833 return 0;
1834
1835
1836 err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001837 if (err < 0)
1838 return err;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001839
Joseph Chanc577b8a2006-11-29 15:29:40 +01001840 return 0;
1841}
1842
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001843static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1844{
1845 struct via_spec *spec = codec->spec;
1846 hda_nid_t pin, dac;
1847
1848 pin = spec->autocfg.speaker_pins[0];
1849 if (!spec->autocfg.speaker_outs || !pin)
1850 return 0;
1851
1852 if (parse_output_path(codec, pin, 0, &spec->speaker_path, 0, -1)) {
1853 dac = spec->speaker_path.path[spec->speaker_path.depth - 1];
1854 spec->multiout.extra_out_nid[0] = dac;
1855 return create_ch_ctls(codec, "Speaker", pin, dac, 3);
1856 }
1857 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
1858 &spec->speaker_path, 0, -1))
1859 return create_ch_ctls(codec, "Headphone", pin, 0, 3);
1860
1861 return 0;
1862}
1863
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001864/* look for ADCs */
1865static int via_fill_adcs(struct hda_codec *codec)
1866{
1867 struct via_spec *spec = codec->spec;
1868 hda_nid_t nid = codec->start_nid;
1869 int i;
1870
1871 for (i = 0; i < codec->num_nodes; i++, nid++) {
1872 unsigned int wcaps = get_wcaps(codec, nid);
1873 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1874 continue;
1875 if (wcaps & AC_WCAP_DIGITAL)
1876 continue;
1877 if (!(wcaps & AC_WCAP_CONN_LIST))
1878 continue;
1879 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1880 return -ENOMEM;
1881 spec->adc_nids[spec->num_adc_nids++] = nid;
1882 }
1883 return 0;
1884}
1885
1886static int get_mux_nids(struct hda_codec *codec);
1887
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001888static const struct snd_kcontrol_new via_input_src_ctl = {
1889 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1890 /* The multiple "Capture Source" controls confuse alsamixer
1891 * So call somewhat different..
1892 */
1893 /* .name = "Capture Source", */
1894 .name = "Input Source",
1895 .info = via_mux_enum_info,
1896 .get = via_mux_enum_get,
1897 .put = via_mux_enum_put,
1898};
1899
Takashi Iwai13af8e72011-06-20 14:05:46 +02001900#ifdef CONFIG_SND_HDA_POWER_SAVE
1901static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
1902{
1903 struct hda_amp_list *list;
1904
1905 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
1906 return;
1907 list = spec->loopback_list + spec->num_loopbacks;
1908 list->nid = mix;
1909 list->dir = HDA_INPUT;
1910 list->idx = idx;
1911 spec->num_loopbacks++;
1912 spec->loopback.amplist = spec->loopback_list;
1913}
1914#else
1915#define add_loopback_list(spec, mix, idx) /* NOP */
1916#endif
1917
Joseph Chanc577b8a2006-11-29 15:29:40 +01001918/* create playback/capture controls for input pins */
Takashi Iwai620e2b22011-06-17 17:19:19 +02001919static int via_auto_create_analog_input_ctls(struct hda_codec *codec,
1920 const struct auto_pin_cfg *cfg)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001921{
Takashi Iwai10a20af2010-09-09 16:28:02 +02001922 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001923 struct hda_input_mux *imux = &spec->private_imux[0];
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001924 int i, j, err, idx, idx2, type, type_idx = 0;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001925 hda_nid_t cap_nid;
1926 hda_nid_t pin_idxs[8];
1927 int num_idxs;
1928
1929 err = via_fill_adcs(codec);
1930 if (err < 0)
1931 return err;
1932 err = get_mux_nids(codec);
1933 if (err < 0)
1934 return err;
1935 cap_nid = spec->mux_nids[0];
1936
1937 num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
1938 ARRAY_SIZE(pin_idxs));
1939 if (num_idxs <= 0)
1940 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001941
1942 /* for internal loopback recording select */
Takashi Iwaif3268512010-08-30 11:00:19 +02001943 for (idx = 0; idx < num_idxs; idx++) {
Takashi Iwai620e2b22011-06-17 17:19:19 +02001944 if (pin_idxs[idx] == spec->aa_mix_nid) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001945 snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
Takashi Iwaif3268512010-08-30 11:00:19 +02001946 break;
1947 }
1948 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001949
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001950 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001951 const char *label;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001952 type = cfg->inputs[i].type;
Takashi Iwaif3268512010-08-30 11:00:19 +02001953 for (idx = 0; idx < num_idxs; idx++)
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001954 if (pin_idxs[idx] == cfg->inputs[i].pin)
Takashi Iwaif3268512010-08-30 11:00:19 +02001955 break;
1956 if (idx >= num_idxs)
1957 continue;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001958 if (i > 0 && type == cfg->inputs[i - 1].type)
1959 type_idx++;
1960 else
1961 type_idx = 0;
Takashi Iwai10a20af2010-09-09 16:28:02 +02001962 label = hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwai620e2b22011-06-17 17:19:19 +02001963 idx2 = get_connection_index(codec, spec->aa_mix_nid,
1964 pin_idxs[idx]);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001965 if (idx2 >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08001966 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwai620e2b22011-06-17 17:19:19 +02001967 idx2, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001968 if (err < 0)
1969 return err;
1970 add_loopback_list(spec, spec->aa_mix_nid, idx2);
1971 }
Takashi Iwai10a20af2010-09-09 16:28:02 +02001972 snd_hda_add_imux_item(imux, label, idx, NULL);
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001973
1974 /* remember the label for smart51 control */
1975 for (j = 0; j < spec->smart51_nums; j++) {
1976 if (spec->smart51_pins[j] == cfg->inputs[i].pin) {
1977 spec->smart51_idxs[j] = idx;
1978 spec->smart51_labels[j] = label;
1979 break;
1980 }
1981 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001982 }
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001983
1984 /* create capture mixer elements */
1985 for (i = 0; i < spec->num_adc_nids; i++) {
1986 hda_nid_t adc = spec->adc_nids[i];
1987 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
1988 "Capture Volume", i,
1989 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1990 HDA_INPUT));
1991 if (err < 0)
1992 return err;
1993 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1994 "Capture Switch", i,
1995 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1996 HDA_INPUT));
1997 if (err < 0)
1998 return err;
1999 }
2000
2001 /* input-source control */
2002 for (i = 0; i < spec->num_adc_nids; i++)
2003 if (!spec->mux_nids[i])
2004 break;
2005 if (i) {
2006 struct snd_kcontrol_new *knew;
2007 knew = via_clone_control(spec, &via_input_src_ctl);
2008 if (!knew)
2009 return -ENOMEM;
2010 knew->count = i;
2011 }
2012
2013 /* mic-boosts */
2014 for (i = 0; i < cfg->num_inputs; i++) {
2015 hda_nid_t pin = cfg->inputs[i].pin;
2016 unsigned int caps;
2017 const char *label;
2018 char name[32];
2019
2020 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2021 continue;
2022 caps = query_amp_caps(codec, pin, HDA_INPUT);
2023 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2024 continue;
2025 label = hda_get_autocfg_input_label(codec, cfg, i);
2026 snprintf(name, sizeof(name), "%s Boost Capture Volume", label);
2027 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2028 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2029 if (err < 0)
2030 return err;
2031 }
2032
Joseph Chanc577b8a2006-11-29 15:29:40 +01002033 return 0;
2034}
2035
Harald Welte76d9b0d2008-09-09 15:50:37 +08002036static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2037{
2038 unsigned int def_conf;
2039 unsigned char seqassoc;
2040
Takashi Iwai2f334f92009-02-20 14:37:42 +01002041 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002042 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2043 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002044 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2045 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2046 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2047 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002048 }
2049
2050 return;
2051}
2052
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002053static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002054 struct snd_ctl_elem_value *ucontrol)
2055{
2056 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2057 struct via_spec *spec = codec->spec;
2058
2059 if (spec->codec_type != VT1708)
2060 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002061 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002062 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002063 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002064 return 0;
2065}
2066
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002067static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002068 struct snd_ctl_elem_value *ucontrol)
2069{
2070 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2071 struct via_spec *spec = codec->spec;
2072 int change;
2073
2074 if (spec->codec_type != VT1708)
2075 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002076 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002077 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002078 == !spec->vt1708_jack_detect;
2079 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002080 mute_aa_path(codec, 1);
2081 notify_aa_path_ctls(codec);
2082 }
2083 return change;
2084}
2085
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002086static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2087 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2088 .name = "Jack Detect",
2089 .count = 1,
2090 .info = snd_ctl_boolean_mono_info,
2091 .get = vt1708_jack_detect_get,
2092 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002093};
2094
Takashi Iwai12daef62011-06-18 17:45:49 +02002095static void fill_dig_outs(struct hda_codec *codec);
2096static void fill_dig_in(struct hda_codec *codec);
2097
2098static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002099{
2100 struct via_spec *spec = codec->spec;
2101 int err;
2102
2103 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2104 if (err < 0)
2105 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002106 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002107 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002108
Takashi Iwai4a796162011-06-17 17:53:38 +02002109 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002110 if (err < 0)
2111 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002112 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002113 if (err < 0)
2114 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002115 err = via_auto_create_speaker_ctls(codec);
2116 if (err < 0)
2117 return err;
Takashi Iwai620e2b22011-06-17 17:19:19 +02002118 err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002119 if (err < 0)
2120 return err;
2121
2122 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2123
Takashi Iwai12daef62011-06-18 17:45:49 +02002124 fill_dig_outs(codec);
2125 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002126
Takashi Iwai603c4012008-07-30 15:01:44 +02002127 if (spec->kctls.list)
2128 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002129
Takashi Iwai096a8852011-06-20 12:09:02 +02002130 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002131
Harald Welte0aa62ae2008-09-09 15:58:27 +08002132 spec->input_mux = &spec->private_imux[0];
2133
Takashi Iwaiece8d042011-06-19 16:24:21 +02002134 if (spec->hp_mux) {
2135 err = via_hp_build(codec);
2136 if (err < 0)
2137 return err;
2138 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002139
Takashi Iwaif4a78282011-06-17 18:46:48 +02002140 err = via_smart51_build(codec);
2141 if (err < 0)
2142 return err;
2143
Takashi Iwai5d417622011-06-20 11:32:27 +02002144 /* assign slave outs */
2145 if (spec->slave_dig_outs[0])
2146 codec->slave_dig_outs = spec->slave_dig_outs;
2147
Joseph Chanc577b8a2006-11-29 15:29:40 +01002148 return 1;
2149}
2150
Takashi Iwai5d417622011-06-20 11:32:27 +02002151static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002152{
Lydia Wang25eaba22009-10-10 19:08:43 +08002153 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002154 if (spec->multiout.dig_out_nid)
2155 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2156 if (spec->slave_dig_outs[0])
2157 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2158}
Lydia Wang25eaba22009-10-10 19:08:43 +08002159
Takashi Iwai5d417622011-06-20 11:32:27 +02002160static void via_auto_init_dig_in(struct hda_codec *codec)
2161{
2162 struct via_spec *spec = codec->spec;
2163 if (!spec->dig_in_nid)
2164 return;
2165 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2166 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2167}
2168
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002169/* initialize the unsolicited events */
2170static void via_auto_init_unsol_event(struct hda_codec *codec)
2171{
2172 struct via_spec *spec = codec->spec;
2173 struct auto_pin_cfg *cfg = &spec->autocfg;
2174 unsigned int ev;
2175 int i;
2176
2177 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2178 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2179 AC_VERB_SET_UNSOLICITED_ENABLE,
2180 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2181
2182 if (cfg->speaker_pins[0])
2183 ev = VIA_LINE_EVENT;
2184 else
2185 ev = 0;
2186 for (i = 0; i < cfg->line_outs; i++) {
2187 if (cfg->line_out_pins[i] &&
2188 is_jack_detectable(codec, cfg->line_out_pins[i]))
2189 snd_hda_codec_write(codec, cfg->line_out_pins[0], 0,
2190 AC_VERB_SET_UNSOLICITED_ENABLE,
2191 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2192 }
2193
2194 for (i = 0; i < cfg->num_inputs; i++) {
2195 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2196 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2197 AC_VERB_SET_UNSOLICITED_ENABLE,
2198 AC_USRSP_EN | VIA_JACK_EVENT);
2199 }
2200}
2201
Takashi Iwai5d417622011-06-20 11:32:27 +02002202static int via_init(struct hda_codec *codec)
2203{
2204 struct via_spec *spec = codec->spec;
2205 int i;
2206
2207 for (i = 0; i < spec->num_iverbs; i++)
2208 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2209
Joseph Chanc577b8a2006-11-29 15:29:40 +01002210 via_auto_init_multi_out(codec);
2211 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002212 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002213 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002214 via_auto_init_dig_outs(codec);
2215 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002216
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002217 via_auto_init_unsol_event(codec);
2218
2219 via_hp_automute(codec);
2220 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002221
Joseph Chanc577b8a2006-11-29 15:29:40 +01002222 return 0;
2223}
2224
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002225static void vt1708_update_hp_jack_state(struct work_struct *work)
2226{
2227 struct via_spec *spec = container_of(work, struct via_spec,
2228 vt1708_hp_work.work);
2229 if (spec->codec_type != VT1708)
2230 return;
2231 /* if jack state toggled */
2232 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002233 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002234 spec->vt1708_hp_present ^= 1;
2235 via_hp_automute(spec->codec);
2236 }
2237 vt1708_start_hp_work(spec);
2238}
2239
Takashi Iwai337b9d02009-07-07 18:18:59 +02002240static int get_mux_nids(struct hda_codec *codec)
2241{
2242 struct via_spec *spec = codec->spec;
2243 hda_nid_t nid, conn[8];
2244 unsigned int type;
2245 int i, n;
2246
2247 for (i = 0; i < spec->num_adc_nids; i++) {
2248 nid = spec->adc_nids[i];
2249 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002250 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002251 if (type == AC_WID_PIN)
2252 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002253 n = snd_hda_get_connections(codec, nid, conn,
2254 ARRAY_SIZE(conn));
2255 if (n <= 0)
2256 break;
2257 if (n > 1) {
2258 spec->mux_nids[i] = nid;
2259 break;
2260 }
2261 nid = conn[0];
2262 }
2263 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002264 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002265}
2266
Joseph Chanc577b8a2006-11-29 15:29:40 +01002267static int patch_vt1708(struct hda_codec *codec)
2268{
2269 struct via_spec *spec;
2270 int err;
2271
2272 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002273 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002274 if (spec == NULL)
2275 return -ENOMEM;
2276
Takashi Iwai620e2b22011-06-17 17:19:19 +02002277 spec->aa_mix_nid = 0x17;
2278
Takashi Iwai12daef62011-06-18 17:45:49 +02002279 /* Add HP and CD pin config connect bit re-config action */
2280 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2281 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2282
Joseph Chanc577b8a2006-11-29 15:29:40 +01002283 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002284 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002285 if (err < 0) {
2286 via_free(codec);
2287 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002288 }
2289
Takashi Iwai12daef62011-06-18 17:45:49 +02002290 /* add jack detect on/off control */
2291 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2292 return -ENOMEM;
2293
Takashi Iwaibc9b562382008-05-23 17:50:27 +02002294 /* disable 32bit format on VT1708 */
2295 if (codec->vendor_id == 0x11061708)
2296 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002297
Joseph Chanc577b8a2006-11-29 15:29:40 +01002298 codec->patch_ops = via_patch_ops;
2299
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002300 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002301 return 0;
2302}
2303
Joseph Chanc577b8a2006-11-29 15:29:40 +01002304static int patch_vt1709_10ch(struct hda_codec *codec)
2305{
2306 struct via_spec *spec;
2307 int err;
2308
2309 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002310 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002311 if (spec == NULL)
2312 return -ENOMEM;
2313
Takashi Iwai620e2b22011-06-17 17:19:19 +02002314 spec->aa_mix_nid = 0x18;
2315
Takashi Iwai12daef62011-06-18 17:45:49 +02002316 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002317 if (err < 0) {
2318 via_free(codec);
2319 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002320 }
2321
Joseph Chanc577b8a2006-11-29 15:29:40 +01002322 codec->patch_ops = via_patch_ops;
2323
Joseph Chanc577b8a2006-11-29 15:29:40 +01002324 return 0;
2325}
2326/*
2327 * generic initialization of ADC, input mixers and output mixers
2328 */
Joseph Chanc577b8a2006-11-29 15:29:40 +01002329static int patch_vt1709_6ch(struct hda_codec *codec)
2330{
2331 struct via_spec *spec;
2332 int err;
2333
2334 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002335 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002336 if (spec == NULL)
2337 return -ENOMEM;
2338
Takashi Iwai620e2b22011-06-17 17:19:19 +02002339 spec->aa_mix_nid = 0x18;
2340
Takashi Iwai12daef62011-06-18 17:45:49 +02002341 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002342 if (err < 0) {
2343 via_free(codec);
2344 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002345 }
2346
Joseph Chanc577b8a2006-11-29 15:29:40 +01002347 codec->patch_ops = via_patch_ops;
2348
Josepch Chanf7278fd2007-12-13 16:40:40 +01002349 return 0;
2350}
2351
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002352static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2353{
2354 struct via_spec *spec = codec->spec;
2355 int imux_is_smixer;
2356 unsigned int parm;
2357 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002358 if ((spec->codec_type != VT1708B_4CH) &&
2359 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002360 is_8ch = 1;
2361
2362 /* SW0 (17h) = stereo mixer */
2363 imux_is_smixer =
2364 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2365 == ((spec->codec_type == VT1708S) ? 5 : 0));
2366 /* inputs */
2367 /* PW 1/2/5 (1ah/1bh/1eh) */
2368 parm = AC_PWRST_D3;
2369 set_pin_power_state(codec, 0x1a, &parm);
2370 set_pin_power_state(codec, 0x1b, &parm);
2371 set_pin_power_state(codec, 0x1e, &parm);
2372 if (imux_is_smixer)
2373 parm = AC_PWRST_D0;
2374 /* SW0 (17h), AIW 0/1 (13h/14h) */
2375 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2376 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2377 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2378
2379 /* outputs */
2380 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2381 parm = AC_PWRST_D3;
2382 set_pin_power_state(codec, 0x19, &parm);
2383 if (spec->smart51_enabled)
2384 set_pin_power_state(codec, 0x1b, &parm);
2385 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2386 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2387
2388 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2389 if (is_8ch) {
2390 parm = AC_PWRST_D3;
2391 set_pin_power_state(codec, 0x22, &parm);
2392 if (spec->smart51_enabled)
2393 set_pin_power_state(codec, 0x1a, &parm);
2394 snd_hda_codec_write(codec, 0x26, 0,
2395 AC_VERB_SET_POWER_STATE, parm);
2396 snd_hda_codec_write(codec, 0x24, 0,
2397 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002398 } else if (codec->vendor_id == 0x11064397) {
2399 /* PW7(23h), SW2(27h), AOW2(25h) */
2400 parm = AC_PWRST_D3;
2401 set_pin_power_state(codec, 0x23, &parm);
2402 if (spec->smart51_enabled)
2403 set_pin_power_state(codec, 0x1a, &parm);
2404 snd_hda_codec_write(codec, 0x27, 0,
2405 AC_VERB_SET_POWER_STATE, parm);
2406 snd_hda_codec_write(codec, 0x25, 0,
2407 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002408 }
2409
2410 /* PW 3/4/7 (1ch/1dh/23h) */
2411 parm = AC_PWRST_D3;
2412 /* force to D0 for internal Speaker */
2413 set_pin_power_state(codec, 0x1c, &parm);
2414 set_pin_power_state(codec, 0x1d, &parm);
2415 if (is_8ch)
2416 set_pin_power_state(codec, 0x23, &parm);
2417
2418 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2419 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2420 imux_is_smixer ? AC_PWRST_D0 : parm);
2421 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2422 if (is_8ch) {
2423 snd_hda_codec_write(codec, 0x25, 0,
2424 AC_VERB_SET_POWER_STATE, parm);
2425 snd_hda_codec_write(codec, 0x27, 0,
2426 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002427 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2428 snd_hda_codec_write(codec, 0x25, 0,
2429 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002430}
2431
Lydia Wang518bf3b2009-10-10 19:07:29 +08002432static int patch_vt1708S(struct hda_codec *codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002433static int patch_vt1708B_8ch(struct hda_codec *codec)
2434{
2435 struct via_spec *spec;
2436 int err;
2437
Lydia Wang518bf3b2009-10-10 19:07:29 +08002438 if (get_codec_type(codec) == VT1708BCE)
2439 return patch_vt1708S(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002440 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002441 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002442 if (spec == NULL)
2443 return -ENOMEM;
2444
Takashi Iwai620e2b22011-06-17 17:19:19 +02002445 spec->aa_mix_nid = 0x16;
2446
Josepch Chanf7278fd2007-12-13 16:40:40 +01002447 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002448 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002449 if (err < 0) {
2450 via_free(codec);
2451 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002452 }
2453
Josepch Chanf7278fd2007-12-13 16:40:40 +01002454 codec->patch_ops = via_patch_ops;
2455
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002456 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2457
Josepch Chanf7278fd2007-12-13 16:40:40 +01002458 return 0;
2459}
2460
2461static int patch_vt1708B_4ch(struct hda_codec *codec)
2462{
2463 struct via_spec *spec;
2464 int err;
2465
2466 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002467 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002468 if (spec == NULL)
2469 return -ENOMEM;
2470
Josepch Chanf7278fd2007-12-13 16:40:40 +01002471 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002472 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002473 if (err < 0) {
2474 via_free(codec);
2475 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002476 }
2477
Josepch Chanf7278fd2007-12-13 16:40:40 +01002478 codec->patch_ops = via_patch_ops;
2479
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002480 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2481
Joseph Chanc577b8a2006-11-29 15:29:40 +01002482 return 0;
2483}
2484
Harald Welted949cac2008-09-09 15:56:01 +08002485/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002486static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002487 /* Enable Mic Boost Volume backdoor */
2488 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002489 /* don't bybass mixer */
2490 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002491 { }
2492};
2493
Takashi Iwai9da29272009-05-07 16:31:14 +02002494/* fill out digital output widgets; one for master and one for slave outputs */
2495static void fill_dig_outs(struct hda_codec *codec)
2496{
2497 struct via_spec *spec = codec->spec;
2498 int i;
2499
2500 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2501 hda_nid_t nid;
2502 int conn;
2503
2504 nid = spec->autocfg.dig_out_pins[i];
2505 if (!nid)
2506 continue;
2507 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2508 if (conn < 1)
2509 continue;
2510 if (!spec->multiout.dig_out_nid)
2511 spec->multiout.dig_out_nid = nid;
2512 else {
2513 spec->slave_dig_outs[0] = nid;
2514 break; /* at most two dig outs */
2515 }
2516 }
2517}
2518
Takashi Iwai12daef62011-06-18 17:45:49 +02002519static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002520{
2521 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002522 hda_nid_t dig_nid;
2523 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002524
Takashi Iwai12daef62011-06-18 17:45:49 +02002525 if (!spec->autocfg.dig_in_pin)
2526 return;
Harald Welted949cac2008-09-09 15:56:01 +08002527
Takashi Iwai12daef62011-06-18 17:45:49 +02002528 dig_nid = codec->start_nid;
2529 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2530 unsigned int wcaps = get_wcaps(codec, dig_nid);
2531 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2532 continue;
2533 if (!(wcaps & AC_WCAP_DIGITAL))
2534 continue;
2535 if (!(wcaps & AC_WCAP_CONN_LIST))
2536 continue;
2537 err = get_connection_index(codec, dig_nid,
2538 spec->autocfg.dig_in_pin);
2539 if (err >= 0) {
2540 spec->dig_in_nid = dig_nid;
2541 break;
2542 }
2543 }
Harald Welted949cac2008-09-09 15:56:01 +08002544}
2545
Lydia Wang6369bcf2009-10-10 19:08:31 +08002546static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2547 int offset, int num_steps, int step_size)
2548{
2549 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2550 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2551 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2552 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2553 (0 << AC_AMPCAP_MUTE_SHIFT));
2554}
2555
Harald Welted949cac2008-09-09 15:56:01 +08002556static int patch_vt1708S(struct hda_codec *codec)
2557{
2558 struct via_spec *spec;
2559 int err;
2560
2561 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002562 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002563 if (spec == NULL)
2564 return -ENOMEM;
2565
Takashi Iwai620e2b22011-06-17 17:19:19 +02002566 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002567 override_mic_boost(codec, 0x1a, 0, 3, 40);
2568 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002569
Harald Welted949cac2008-09-09 15:56:01 +08002570 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002571 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002572 if (err < 0) {
2573 via_free(codec);
2574 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002575 }
2576
Takashi Iwai096a8852011-06-20 12:09:02 +02002577 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002578
Harald Welted949cac2008-09-09 15:56:01 +08002579 codec->patch_ops = via_patch_ops;
2580
Lydia Wang518bf3b2009-10-10 19:07:29 +08002581 /* correct names for VT1708BCE */
2582 if (get_codec_type(codec) == VT1708BCE) {
2583 kfree(codec->chip_name);
2584 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2585 snprintf(codec->bus->card->mixername,
2586 sizeof(codec->bus->card->mixername),
2587 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f630f2011-03-22 16:25:56 +08002588 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002589 /* correct names for VT1705 */
2590 if (codec->vendor_id == 0x11064397) {
2591 kfree(codec->chip_name);
2592 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2593 snprintf(codec->bus->card->mixername,
2594 sizeof(codec->bus->card->mixername),
2595 "%s %s", codec->vendor_name, codec->chip_name);
2596 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002597 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002598 return 0;
2599}
2600
2601/* Patch for VT1702 */
2602
Takashi Iwai096a8852011-06-20 12:09:02 +02002603static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002604 /* mixer enable */
2605 {0x1, 0xF88, 0x3},
2606 /* GPIO 0~2 */
2607 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002608 { }
2609};
2610
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002611static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2612{
2613 int imux_is_smixer =
2614 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2615 unsigned int parm;
2616 /* inputs */
2617 /* PW 1/2/5 (14h/15h/18h) */
2618 parm = AC_PWRST_D3;
2619 set_pin_power_state(codec, 0x14, &parm);
2620 set_pin_power_state(codec, 0x15, &parm);
2621 set_pin_power_state(codec, 0x18, &parm);
2622 if (imux_is_smixer)
2623 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2624 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2625 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2626 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2627 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2628 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2629
2630 /* outputs */
2631 /* PW 3/4 (16h/17h) */
2632 parm = AC_PWRST_D3;
2633 set_pin_power_state(codec, 0x17, &parm);
2634 set_pin_power_state(codec, 0x16, &parm);
2635 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2636 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2637 imux_is_smixer ? AC_PWRST_D0 : parm);
2638 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2639 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2640}
2641
Harald Welted949cac2008-09-09 15:56:01 +08002642static int patch_vt1702(struct hda_codec *codec)
2643{
2644 struct via_spec *spec;
2645 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002646
2647 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002648 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002649 if (spec == NULL)
2650 return -ENOMEM;
2651
Takashi Iwai620e2b22011-06-17 17:19:19 +02002652 spec->aa_mix_nid = 0x1a;
2653
Takashi Iwai12daef62011-06-18 17:45:49 +02002654 /* limit AA path volume to 0 dB */
2655 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2656 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2657 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2658 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2659 (1 << AC_AMPCAP_MUTE_SHIFT));
2660
Harald Welted949cac2008-09-09 15:56:01 +08002661 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002662 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002663 if (err < 0) {
2664 via_free(codec);
2665 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002666 }
2667
Takashi Iwai096a8852011-06-20 12:09:02 +02002668 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002669
Harald Welted949cac2008-09-09 15:56:01 +08002670 codec->patch_ops = via_patch_ops;
2671
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002672 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002673 return 0;
2674}
2675
Lydia Wangeb7188c2009-10-10 19:08:34 +08002676/* Patch for VT1718S */
2677
Takashi Iwai096a8852011-06-20 12:09:02 +02002678static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002679 /* Enable MW0 adjust Gain 5 */
2680 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002681 /* Enable Boost Volume backdoor */
2682 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002683
Lydia Wangeb7188c2009-10-10 19:08:34 +08002684 { }
2685};
2686
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002687static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2688{
2689 struct via_spec *spec = codec->spec;
2690 int imux_is_smixer;
2691 unsigned int parm;
2692 /* MUX6 (1eh) = stereo mixer */
2693 imux_is_smixer =
2694 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2695 /* inputs */
2696 /* PW 5/6/7 (29h/2ah/2bh) */
2697 parm = AC_PWRST_D3;
2698 set_pin_power_state(codec, 0x29, &parm);
2699 set_pin_power_state(codec, 0x2a, &parm);
2700 set_pin_power_state(codec, 0x2b, &parm);
2701 if (imux_is_smixer)
2702 parm = AC_PWRST_D0;
2703 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2704 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2705 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2706 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2707 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2708
2709 /* outputs */
2710 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2711 parm = AC_PWRST_D3;
2712 set_pin_power_state(codec, 0x27, &parm);
2713 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2714 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2715
2716 /* PW2 (26h), AOW2 (ah) */
2717 parm = AC_PWRST_D3;
2718 set_pin_power_state(codec, 0x26, &parm);
2719 if (spec->smart51_enabled)
2720 set_pin_power_state(codec, 0x2b, &parm);
2721 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2722
2723 /* PW0 (24h), AOW0 (8h) */
2724 parm = AC_PWRST_D3;
2725 set_pin_power_state(codec, 0x24, &parm);
2726 if (!spec->hp_independent_mode) /* check for redirected HP */
2727 set_pin_power_state(codec, 0x28, &parm);
2728 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2729 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2730 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2731 imux_is_smixer ? AC_PWRST_D0 : parm);
2732
2733 /* PW1 (25h), AOW1 (9h) */
2734 parm = AC_PWRST_D3;
2735 set_pin_power_state(codec, 0x25, &parm);
2736 if (spec->smart51_enabled)
2737 set_pin_power_state(codec, 0x2a, &parm);
2738 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2739
2740 if (spec->hp_independent_mode) {
2741 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2742 parm = AC_PWRST_D3;
2743 set_pin_power_state(codec, 0x28, &parm);
2744 snd_hda_codec_write(codec, 0x1b, 0,
2745 AC_VERB_SET_POWER_STATE, parm);
2746 snd_hda_codec_write(codec, 0x34, 0,
2747 AC_VERB_SET_POWER_STATE, parm);
2748 snd_hda_codec_write(codec, 0xc, 0,
2749 AC_VERB_SET_POWER_STATE, parm);
2750 }
2751}
2752
Lydia Wangeb7188c2009-10-10 19:08:34 +08002753static int patch_vt1718S(struct hda_codec *codec)
2754{
2755 struct via_spec *spec;
2756 int err;
2757
2758 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002759 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002760 if (spec == NULL)
2761 return -ENOMEM;
2762
Takashi Iwai620e2b22011-06-17 17:19:19 +02002763 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002764 override_mic_boost(codec, 0x2b, 0, 3, 40);
2765 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002766
Lydia Wangeb7188c2009-10-10 19:08:34 +08002767 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002768 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002769 if (err < 0) {
2770 via_free(codec);
2771 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002772 }
2773
Takashi Iwai096a8852011-06-20 12:09:02 +02002774 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002775
Lydia Wangeb7188c2009-10-10 19:08:34 +08002776 codec->patch_ops = via_patch_ops;
2777
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002778 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2779
Lydia Wangeb7188c2009-10-10 19:08:34 +08002780 return 0;
2781}
Lydia Wangf3db4232009-10-10 19:08:41 +08002782
2783/* Patch for VT1716S */
2784
2785static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2786 struct snd_ctl_elem_info *uinfo)
2787{
2788 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2789 uinfo->count = 1;
2790 uinfo->value.integer.min = 0;
2791 uinfo->value.integer.max = 1;
2792 return 0;
2793}
2794
2795static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2796 struct snd_ctl_elem_value *ucontrol)
2797{
2798 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2799 int index = 0;
2800
2801 index = snd_hda_codec_read(codec, 0x26, 0,
2802 AC_VERB_GET_CONNECT_SEL, 0);
2803 if (index != -1)
2804 *ucontrol->value.integer.value = index;
2805
2806 return 0;
2807}
2808
2809static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2810 struct snd_ctl_elem_value *ucontrol)
2811{
2812 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2813 struct via_spec *spec = codec->spec;
2814 int index = *ucontrol->value.integer.value;
2815
2816 snd_hda_codec_write(codec, 0x26, 0,
2817 AC_VERB_SET_CONNECT_SEL, index);
2818 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002819 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002820 return 1;
2821}
2822
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002823static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002824 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2825 {
2826 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2827 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002828 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08002829 .count = 1,
2830 .info = vt1716s_dmic_info,
2831 .get = vt1716s_dmic_get,
2832 .put = vt1716s_dmic_put,
2833 },
2834 {} /* end */
2835};
2836
2837
2838/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002839static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002840 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
2841 { } /* end */
2842};
2843
Takashi Iwai096a8852011-06-20 12:09:02 +02002844static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002845 /* Enable Boost Volume backdoor */
2846 {0x1, 0xf8a, 0x80},
2847 /* don't bybass mixer */
2848 {0x1, 0xf88, 0xc0},
2849 /* Enable mono output */
2850 {0x1, 0xf90, 0x08},
2851 { }
2852};
2853
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002854static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
2855{
2856 struct via_spec *spec = codec->spec;
2857 int imux_is_smixer;
2858 unsigned int parm;
2859 unsigned int mono_out, present;
2860 /* SW0 (17h) = stereo mixer */
2861 imux_is_smixer =
2862 (snd_hda_codec_read(codec, 0x17, 0,
2863 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
2864 /* inputs */
2865 /* PW 1/2/5 (1ah/1bh/1eh) */
2866 parm = AC_PWRST_D3;
2867 set_pin_power_state(codec, 0x1a, &parm);
2868 set_pin_power_state(codec, 0x1b, &parm);
2869 set_pin_power_state(codec, 0x1e, &parm);
2870 if (imux_is_smixer)
2871 parm = AC_PWRST_D0;
2872 /* SW0 (17h), AIW0(13h) */
2873 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2874 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2875
2876 parm = AC_PWRST_D3;
2877 set_pin_power_state(codec, 0x1e, &parm);
2878 /* PW11 (22h) */
2879 if (spec->dmic_enabled)
2880 set_pin_power_state(codec, 0x22, &parm);
2881 else
2882 snd_hda_codec_write(codec, 0x22, 0,
2883 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
2884
2885 /* SW2(26h), AIW1(14h) */
2886 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
2887 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2888
2889 /* outputs */
2890 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2891 parm = AC_PWRST_D3;
2892 set_pin_power_state(codec, 0x19, &parm);
2893 /* Smart 5.1 PW2(1bh) */
2894 if (spec->smart51_enabled)
2895 set_pin_power_state(codec, 0x1b, &parm);
2896 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2897 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2898
2899 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
2900 parm = AC_PWRST_D3;
2901 set_pin_power_state(codec, 0x23, &parm);
2902 /* Smart 5.1 PW1(1ah) */
2903 if (spec->smart51_enabled)
2904 set_pin_power_state(codec, 0x1a, &parm);
2905 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
2906
2907 /* Smart 5.1 PW5(1eh) */
2908 if (spec->smart51_enabled)
2909 set_pin_power_state(codec, 0x1e, &parm);
2910 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
2911
2912 /* Mono out */
2913 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
2914 present = snd_hda_jack_detect(codec, 0x1c);
2915
2916 if (present)
2917 mono_out = 0;
2918 else {
2919 present = snd_hda_jack_detect(codec, 0x1d);
2920 if (!spec->hp_independent_mode && present)
2921 mono_out = 0;
2922 else
2923 mono_out = 1;
2924 }
2925 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
2926 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
2927 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
2928 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
2929
2930 /* PW 3/4 (1ch/1dh) */
2931 parm = AC_PWRST_D3;
2932 set_pin_power_state(codec, 0x1c, &parm);
2933 set_pin_power_state(codec, 0x1d, &parm);
2934 /* HP Independent Mode, power on AOW3 */
2935 if (spec->hp_independent_mode)
2936 snd_hda_codec_write(codec, 0x25, 0,
2937 AC_VERB_SET_POWER_STATE, parm);
2938
2939 /* force to D0 for internal Speaker */
2940 /* MW0 (16h), AOW0 (10h) */
2941 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2942 imux_is_smixer ? AC_PWRST_D0 : parm);
2943 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
2944 mono_out ? AC_PWRST_D0 : parm);
2945}
2946
Lydia Wangf3db4232009-10-10 19:08:41 +08002947static int patch_vt1716S(struct hda_codec *codec)
2948{
2949 struct via_spec *spec;
2950 int err;
2951
2952 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002953 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002954 if (spec == NULL)
2955 return -ENOMEM;
2956
Takashi Iwai620e2b22011-06-17 17:19:19 +02002957 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002958 override_mic_boost(codec, 0x1a, 0, 3, 40);
2959 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002960
Lydia Wangf3db4232009-10-10 19:08:41 +08002961 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002962 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002963 if (err < 0) {
2964 via_free(codec);
2965 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08002966 }
2967
Takashi Iwai096a8852011-06-20 12:09:02 +02002968 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08002969
Lydia Wangf3db4232009-10-10 19:08:41 +08002970 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
2971 spec->num_mixers++;
2972
2973 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
2974
2975 codec->patch_ops = via_patch_ops;
2976
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002977 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08002978 return 0;
2979}
Lydia Wang25eaba22009-10-10 19:08:43 +08002980
2981/* for vt2002P */
2982
Takashi Iwai096a8852011-06-20 12:09:02 +02002983static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08002984 /* Class-D speaker related verbs */
2985 {0x1, 0xfe0, 0x4},
2986 {0x1, 0xfe9, 0x80},
2987 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08002988 /* Enable Boost Volume backdoor */
2989 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08002990 /* Enable AOW0 to MW9 */
2991 {0x1, 0xfb8, 0x88},
2992 { }
2993};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002994
Takashi Iwai096a8852011-06-20 12:09:02 +02002995static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08002996 /* Enable Boost Volume backdoor */
2997 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08002998 /* Enable AOW0 to MW9 */
2999 {0x1, 0xfb8, 0x88},
3000 { }
3001};
Lydia Wang25eaba22009-10-10 19:08:43 +08003002
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003003static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3004{
3005 struct via_spec *spec = codec->spec;
3006 int imux_is_smixer;
3007 unsigned int parm;
3008 unsigned int present;
3009 /* MUX9 (1eh) = stereo mixer */
3010 imux_is_smixer =
3011 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3012 /* inputs */
3013 /* PW 5/6/7 (29h/2ah/2bh) */
3014 parm = AC_PWRST_D3;
3015 set_pin_power_state(codec, 0x29, &parm);
3016 set_pin_power_state(codec, 0x2a, &parm);
3017 set_pin_power_state(codec, 0x2b, &parm);
3018 parm = AC_PWRST_D0;
3019 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3020 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3021 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3022 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3023 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3024
3025 /* outputs */
3026 /* AOW0 (8h)*/
3027 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3028
Lydia Wang118909562011-03-23 17:57:34 +08003029 if (spec->codec_type == VT1802) {
3030 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3031 parm = AC_PWRST_D3;
3032 set_pin_power_state(codec, 0x28, &parm);
3033 snd_hda_codec_write(codec, 0x18, 0,
3034 AC_VERB_SET_POWER_STATE, parm);
3035 snd_hda_codec_write(codec, 0x38, 0,
3036 AC_VERB_SET_POWER_STATE, parm);
3037 } else {
3038 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3039 parm = AC_PWRST_D3;
3040 set_pin_power_state(codec, 0x26, &parm);
3041 snd_hda_codec_write(codec, 0x1c, 0,
3042 AC_VERB_SET_POWER_STATE, parm);
3043 snd_hda_codec_write(codec, 0x37, 0,
3044 AC_VERB_SET_POWER_STATE, parm);
3045 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003046
Lydia Wang118909562011-03-23 17:57:34 +08003047 if (spec->codec_type == VT1802) {
3048 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3049 parm = AC_PWRST_D3;
3050 set_pin_power_state(codec, 0x25, &parm);
3051 snd_hda_codec_write(codec, 0x15, 0,
3052 AC_VERB_SET_POWER_STATE, parm);
3053 snd_hda_codec_write(codec, 0x35, 0,
3054 AC_VERB_SET_POWER_STATE, parm);
3055 } else {
3056 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3057 parm = AC_PWRST_D3;
3058 set_pin_power_state(codec, 0x25, &parm);
3059 snd_hda_codec_write(codec, 0x19, 0,
3060 AC_VERB_SET_POWER_STATE, parm);
3061 snd_hda_codec_write(codec, 0x35, 0,
3062 AC_VERB_SET_POWER_STATE, parm);
3063 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003064
3065 if (spec->hp_independent_mode)
3066 snd_hda_codec_write(codec, 0x9, 0,
3067 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3068
3069 /* Class-D */
3070 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3071 present = snd_hda_jack_detect(codec, 0x25);
3072
3073 parm = AC_PWRST_D3;
3074 set_pin_power_state(codec, 0x24, &parm);
3075 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003076 if (spec->codec_type == VT1802)
3077 snd_hda_codec_write(codec, 0x14, 0,
3078 AC_VERB_SET_POWER_STATE, parm);
3079 else
3080 snd_hda_codec_write(codec, 0x18, 0,
3081 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003082 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3083
3084 /* Mono Out */
3085 present = snd_hda_jack_detect(codec, 0x26);
3086
3087 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003088 if (spec->codec_type == VT1802) {
3089 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3090 snd_hda_codec_write(codec, 0x33, 0,
3091 AC_VERB_SET_POWER_STATE, parm);
3092 snd_hda_codec_write(codec, 0x1c, 0,
3093 AC_VERB_SET_POWER_STATE, parm);
3094 snd_hda_codec_write(codec, 0x3c, 0,
3095 AC_VERB_SET_POWER_STATE, parm);
3096 } else {
3097 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3098 snd_hda_codec_write(codec, 0x31, 0,
3099 AC_VERB_SET_POWER_STATE, parm);
3100 snd_hda_codec_write(codec, 0x17, 0,
3101 AC_VERB_SET_POWER_STATE, parm);
3102 snd_hda_codec_write(codec, 0x3b, 0,
3103 AC_VERB_SET_POWER_STATE, parm);
3104 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003105 /* MW9 (21h) */
3106 if (imux_is_smixer || !is_aa_path_mute(codec))
3107 snd_hda_codec_write(codec, 0x21, 0,
3108 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3109 else
3110 snd_hda_codec_write(codec, 0x21, 0,
3111 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3112}
Lydia Wang25eaba22009-10-10 19:08:43 +08003113
3114/* patch for vt2002P */
3115static int patch_vt2002P(struct hda_codec *codec)
3116{
3117 struct via_spec *spec;
3118 int err;
3119
3120 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003121 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003122 if (spec == NULL)
3123 return -ENOMEM;
3124
Takashi Iwai620e2b22011-06-17 17:19:19 +02003125 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003126 override_mic_boost(codec, 0x2b, 0, 3, 40);
3127 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003128
Lydia Wang25eaba22009-10-10 19:08:43 +08003129 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003130 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003131 if (err < 0) {
3132 via_free(codec);
3133 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003134 }
3135
Lydia Wang118909562011-03-23 17:57:34 +08003136 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003137 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003138 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003139 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003140
Lydia Wang25eaba22009-10-10 19:08:43 +08003141 codec->patch_ops = via_patch_ops;
3142
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003143 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003144 return 0;
3145}
Lydia Wangab6734e2009-10-10 19:08:46 +08003146
3147/* for vt1812 */
3148
Takashi Iwai096a8852011-06-20 12:09:02 +02003149static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003150 /* Enable Boost Volume backdoor */
3151 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003152 /* Enable AOW0 to MW9 */
3153 {0x1, 0xfb8, 0xa8},
3154 { }
3155};
3156
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003157static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3158{
3159 struct via_spec *spec = codec->spec;
3160 int imux_is_smixer =
3161 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3162 unsigned int parm;
3163 unsigned int present;
3164 /* MUX10 (1eh) = stereo mixer */
3165 imux_is_smixer =
3166 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3167 /* inputs */
3168 /* PW 5/6/7 (29h/2ah/2bh) */
3169 parm = AC_PWRST_D3;
3170 set_pin_power_state(codec, 0x29, &parm);
3171 set_pin_power_state(codec, 0x2a, &parm);
3172 set_pin_power_state(codec, 0x2b, &parm);
3173 parm = AC_PWRST_D0;
3174 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3175 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3176 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3177 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3178 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3179
3180 /* outputs */
3181 /* AOW0 (8h)*/
3182 snd_hda_codec_write(codec, 0x8, 0,
3183 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3184
3185 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3186 parm = AC_PWRST_D3;
3187 set_pin_power_state(codec, 0x28, &parm);
3188 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3189 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3190
3191 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3192 parm = AC_PWRST_D3;
3193 set_pin_power_state(codec, 0x25, &parm);
3194 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3195 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3196 if (spec->hp_independent_mode)
3197 snd_hda_codec_write(codec, 0x9, 0,
3198 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3199
3200 /* Internal Speaker */
3201 /* PW0 (24h), MW0(14h), MUX0(34h) */
3202 present = snd_hda_jack_detect(codec, 0x25);
3203
3204 parm = AC_PWRST_D3;
3205 set_pin_power_state(codec, 0x24, &parm);
3206 if (present) {
3207 snd_hda_codec_write(codec, 0x14, 0,
3208 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3209 snd_hda_codec_write(codec, 0x34, 0,
3210 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3211 } else {
3212 snd_hda_codec_write(codec, 0x14, 0,
3213 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3214 snd_hda_codec_write(codec, 0x34, 0,
3215 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3216 }
3217
3218
3219 /* Mono Out */
3220 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3221 present = snd_hda_jack_detect(codec, 0x28);
3222
3223 parm = AC_PWRST_D3;
3224 set_pin_power_state(codec, 0x31, &parm);
3225 if (present) {
3226 snd_hda_codec_write(codec, 0x1c, 0,
3227 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3228 snd_hda_codec_write(codec, 0x3c, 0,
3229 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3230 snd_hda_codec_write(codec, 0x3e, 0,
3231 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3232 } else {
3233 snd_hda_codec_write(codec, 0x1c, 0,
3234 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3235 snd_hda_codec_write(codec, 0x3c, 0,
3236 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3237 snd_hda_codec_write(codec, 0x3e, 0,
3238 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3239 }
3240
3241 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3242 parm = AC_PWRST_D3;
3243 set_pin_power_state(codec, 0x33, &parm);
3244 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3245 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3246
3247}
Lydia Wangab6734e2009-10-10 19:08:46 +08003248
3249/* patch for vt1812 */
3250static int patch_vt1812(struct hda_codec *codec)
3251{
3252 struct via_spec *spec;
3253 int err;
3254
3255 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003256 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003257 if (spec == NULL)
3258 return -ENOMEM;
3259
Takashi Iwai620e2b22011-06-17 17:19:19 +02003260 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003261 override_mic_boost(codec, 0x2b, 0, 3, 40);
3262 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003263
Lydia Wangab6734e2009-10-10 19:08:46 +08003264 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003265 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003266 if (err < 0) {
3267 via_free(codec);
3268 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003269 }
3270
Takashi Iwai096a8852011-06-20 12:09:02 +02003271 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003272
Lydia Wangab6734e2009-10-10 19:08:46 +08003273 codec->patch_ops = via_patch_ops;
3274
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003275 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003276 return 0;
3277}
3278
Joseph Chanc577b8a2006-11-29 15:29:40 +01003279/*
3280 * patch entries
3281 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003282static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003283 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3284 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3285 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3286 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3287 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003288 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003289 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003290 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003291 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003292 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003293 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003294 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003295 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003296 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003297 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003298 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003299 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003300 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003301 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003302 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003303 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003304 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003305 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003306 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003307 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003308 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003309 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003310 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003311 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003312 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003313 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003314 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003315 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003316 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003317 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003318 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003319 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003320 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003321 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003322 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003323 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003324 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003325 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003326 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003327 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003328 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003329 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003330 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003331 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003332 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003333 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003334 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003335 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003336 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003337 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003338 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003339 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003340 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003341 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003342 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003343 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003344 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003345 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003346 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003347 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003348 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003349 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003350 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003351 { .id = 0x11060428, .name = "VT1718S",
3352 .patch = patch_vt1718S},
3353 { .id = 0x11064428, .name = "VT1718S",
3354 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003355 { .id = 0x11060441, .name = "VT2020",
3356 .patch = patch_vt1718S},
3357 { .id = 0x11064441, .name = "VT1828S",
3358 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003359 { .id = 0x11060433, .name = "VT1716S",
3360 .patch = patch_vt1716S},
3361 { .id = 0x1106a721, .name = "VT1716S",
3362 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003363 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3364 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003365 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003366 { .id = 0x11060440, .name = "VT1818S",
3367 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003368 { .id = 0x11060446, .name = "VT1802",
3369 .patch = patch_vt2002P},
3370 { .id = 0x11068446, .name = "VT1802",
3371 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003372 {} /* terminator */
3373};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003374
3375MODULE_ALIAS("snd-hda-codec-id:1106*");
3376
3377static struct hda_codec_preset_list via_list = {
3378 .preset = snd_hda_preset_via,
3379 .owner = THIS_MODULE,
3380};
3381
3382MODULE_LICENSE("GPL");
3383MODULE_DESCRIPTION("VIA HD-audio codec");
3384
3385static int __init patch_via_init(void)
3386{
3387 return snd_hda_add_codec_preset(&via_list);
3388}
3389
3390static void __exit patch_via_exit(void)
3391{
3392 snd_hda_delete_codec_preset(&via_list);
3393}
3394
3395module_init(patch_via_init)
3396module_exit(patch_via_exit)