blob: 6e621b7c984ec1f7423a14bbe76cfea26221c885 [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 Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-06-18 16:40:14 +02001064 substream_set_idle(codec, substream);
Takashi Iwai9af74212011-06-18 16:17:45 +02001065 return 0;
1066}
1067
Takashi Iwai7eb56e82011-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 Iwai7eb56e82011-06-18 16:40:14 +02001073
Takashi Iwaiece8d042011-06-19 16:24:21 +02001074 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-06-18 16:40:14 +02001100 vt1708_start_hp_work(spec);
1101 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001102}
1103
Takashi Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai7eb56e82011-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 Iwai6aadf412011-06-20 14:09:02 +02001773 const char *pfx = chname[i];
1774 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1775 cfg->line_outs == 1)
1776 pfx = "Speaker";
1777 err = create_ch_ctls(codec, pfx, pin, dac, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001778 if (err < 0)
1779 return err;
1780 }
1781 }
1782
Takashi Iwai4a796162011-06-17 17:53:38 +02001783 idx = get_connection_index(codec, spec->aa_mix_nid,
1784 spec->multiout.dac_nids[0]);
1785 if (idx >= 0) {
1786 /* add control to mixer */
1787 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1788 "PCM Playback Volume",
1789 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1790 idx, HDA_INPUT));
1791 if (err < 0)
1792 return err;
1793 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1794 "PCM Playback Switch",
1795 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1796 idx, HDA_INPUT));
1797 if (err < 0)
1798 return err;
1799 }
1800
Takashi Iwaif4a78282011-06-17 18:46:48 +02001801 cfg->line_outs = old_line_outs;
1802
Joseph Chanc577b8a2006-11-29 15:29:40 +01001803 return 0;
1804}
1805
Harald Welte0aa62ae2008-09-09 15:58:27 +08001806static void create_hp_imux(struct via_spec *spec)
1807{
1808 int i;
1809 struct hda_input_mux *imux = &spec->private_imux[1];
Takashi Iwaiea734962011-01-17 11:29:34 +01001810 static const char * const texts[] = { "OFF", "ON", NULL};
Harald Welte0aa62ae2008-09-09 15:58:27 +08001811
1812 /* for hp mode select */
Takashi Iwai10a20af2010-09-09 16:28:02 +02001813 for (i = 0; texts[i]; i++)
1814 snd_hda_add_imux_item(imux, texts[i], i, NULL);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001815
1816 spec->hp_mux = &spec->private_imux[1];
1817}
1818
Takashi Iwai4a796162011-06-17 17:53:38 +02001819static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001820{
Takashi Iwai4a796162011-06-17 17:53:38 +02001821 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001822 int err;
1823
1824 if (!pin)
1825 return 0;
1826
Takashi Iwai4a796162011-06-17 17:53:38 +02001827 if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001828 spec->hp_dac_nid = spec->hp_path.path[spec->hp_path.depth - 1];
Takashi Iwai4a796162011-06-17 17:53:38 +02001829 spec->hp_independent_mode_index =
1830 spec->hp_path.idx[spec->hp_path.depth - 1];
1831 create_hp_imux(spec);
1832 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001833
Takashi Iwaiece8d042011-06-19 16:24:21 +02001834 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
1835 &spec->hp_dep_path, 0, -1) &&
1836 !spec->hp_dac_nid)
1837 return 0;
1838
1839
1840 err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001841 if (err < 0)
1842 return err;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001843
Joseph Chanc577b8a2006-11-29 15:29:40 +01001844 return 0;
1845}
1846
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001847static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1848{
1849 struct via_spec *spec = codec->spec;
1850 hda_nid_t pin, dac;
1851
1852 pin = spec->autocfg.speaker_pins[0];
1853 if (!spec->autocfg.speaker_outs || !pin)
1854 return 0;
1855
1856 if (parse_output_path(codec, pin, 0, &spec->speaker_path, 0, -1)) {
1857 dac = spec->speaker_path.path[spec->speaker_path.depth - 1];
1858 spec->multiout.extra_out_nid[0] = dac;
1859 return create_ch_ctls(codec, "Speaker", pin, dac, 3);
1860 }
1861 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
1862 &spec->speaker_path, 0, -1))
1863 return create_ch_ctls(codec, "Headphone", pin, 0, 3);
1864
1865 return 0;
1866}
1867
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001868/* look for ADCs */
1869static int via_fill_adcs(struct hda_codec *codec)
1870{
1871 struct via_spec *spec = codec->spec;
1872 hda_nid_t nid = codec->start_nid;
1873 int i;
1874
1875 for (i = 0; i < codec->num_nodes; i++, nid++) {
1876 unsigned int wcaps = get_wcaps(codec, nid);
1877 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1878 continue;
1879 if (wcaps & AC_WCAP_DIGITAL)
1880 continue;
1881 if (!(wcaps & AC_WCAP_CONN_LIST))
1882 continue;
1883 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1884 return -ENOMEM;
1885 spec->adc_nids[spec->num_adc_nids++] = nid;
1886 }
1887 return 0;
1888}
1889
1890static int get_mux_nids(struct hda_codec *codec);
1891
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001892static const struct snd_kcontrol_new via_input_src_ctl = {
1893 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1894 /* The multiple "Capture Source" controls confuse alsamixer
1895 * So call somewhat different..
1896 */
1897 /* .name = "Capture Source", */
1898 .name = "Input Source",
1899 .info = via_mux_enum_info,
1900 .get = via_mux_enum_get,
1901 .put = via_mux_enum_put,
1902};
1903
Takashi Iwai13af8e72011-06-20 14:05:46 +02001904#ifdef CONFIG_SND_HDA_POWER_SAVE
1905static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
1906{
1907 struct hda_amp_list *list;
1908
1909 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
1910 return;
1911 list = spec->loopback_list + spec->num_loopbacks;
1912 list->nid = mix;
1913 list->dir = HDA_INPUT;
1914 list->idx = idx;
1915 spec->num_loopbacks++;
1916 spec->loopback.amplist = spec->loopback_list;
1917}
1918#else
1919#define add_loopback_list(spec, mix, idx) /* NOP */
1920#endif
1921
Joseph Chanc577b8a2006-11-29 15:29:40 +01001922/* create playback/capture controls for input pins */
Takashi Iwai620e2b22011-06-17 17:19:19 +02001923static int via_auto_create_analog_input_ctls(struct hda_codec *codec,
1924 const struct auto_pin_cfg *cfg)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001925{
Takashi Iwai10a20af2010-09-09 16:28:02 +02001926 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001927 struct hda_input_mux *imux = &spec->private_imux[0];
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001928 int i, j, err, idx, idx2, type, type_idx = 0;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001929 hda_nid_t cap_nid;
1930 hda_nid_t pin_idxs[8];
1931 int num_idxs;
1932
1933 err = via_fill_adcs(codec);
1934 if (err < 0)
1935 return err;
1936 err = get_mux_nids(codec);
1937 if (err < 0)
1938 return err;
1939 cap_nid = spec->mux_nids[0];
1940
1941 num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
1942 ARRAY_SIZE(pin_idxs));
1943 if (num_idxs <= 0)
1944 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001945
1946 /* for internal loopback recording select */
Takashi Iwaif3268512010-08-30 11:00:19 +02001947 for (idx = 0; idx < num_idxs; idx++) {
Takashi Iwai620e2b22011-06-17 17:19:19 +02001948 if (pin_idxs[idx] == spec->aa_mix_nid) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001949 snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
Takashi Iwaif3268512010-08-30 11:00:19 +02001950 break;
1951 }
1952 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001953
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001954 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001955 const char *label;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001956 type = cfg->inputs[i].type;
Takashi Iwaif3268512010-08-30 11:00:19 +02001957 for (idx = 0; idx < num_idxs; idx++)
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001958 if (pin_idxs[idx] == cfg->inputs[i].pin)
Takashi Iwaif3268512010-08-30 11:00:19 +02001959 break;
1960 if (idx >= num_idxs)
1961 continue;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001962 if (i > 0 && type == cfg->inputs[i - 1].type)
1963 type_idx++;
1964 else
1965 type_idx = 0;
Takashi Iwai10a20af2010-09-09 16:28:02 +02001966 label = hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwai620e2b22011-06-17 17:19:19 +02001967 idx2 = get_connection_index(codec, spec->aa_mix_nid,
1968 pin_idxs[idx]);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001969 if (idx2 >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08001970 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwai620e2b22011-06-17 17:19:19 +02001971 idx2, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001972 if (err < 0)
1973 return err;
1974 add_loopback_list(spec, spec->aa_mix_nid, idx2);
1975 }
Takashi Iwai10a20af2010-09-09 16:28:02 +02001976 snd_hda_add_imux_item(imux, label, idx, NULL);
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001977
1978 /* remember the label for smart51 control */
1979 for (j = 0; j < spec->smart51_nums; j++) {
1980 if (spec->smart51_pins[j] == cfg->inputs[i].pin) {
1981 spec->smart51_idxs[j] = idx;
1982 spec->smart51_labels[j] = label;
1983 break;
1984 }
1985 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001986 }
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001987
1988 /* create capture mixer elements */
1989 for (i = 0; i < spec->num_adc_nids; i++) {
1990 hda_nid_t adc = spec->adc_nids[i];
1991 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
1992 "Capture Volume", i,
1993 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1994 HDA_INPUT));
1995 if (err < 0)
1996 return err;
1997 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1998 "Capture Switch", i,
1999 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2000 HDA_INPUT));
2001 if (err < 0)
2002 return err;
2003 }
2004
2005 /* input-source control */
2006 for (i = 0; i < spec->num_adc_nids; i++)
2007 if (!spec->mux_nids[i])
2008 break;
2009 if (i) {
2010 struct snd_kcontrol_new *knew;
2011 knew = via_clone_control(spec, &via_input_src_ctl);
2012 if (!knew)
2013 return -ENOMEM;
2014 knew->count = i;
2015 }
2016
2017 /* mic-boosts */
2018 for (i = 0; i < cfg->num_inputs; i++) {
2019 hda_nid_t pin = cfg->inputs[i].pin;
2020 unsigned int caps;
2021 const char *label;
2022 char name[32];
2023
2024 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2025 continue;
2026 caps = query_amp_caps(codec, pin, HDA_INPUT);
2027 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2028 continue;
2029 label = hda_get_autocfg_input_label(codec, cfg, i);
2030 snprintf(name, sizeof(name), "%s Boost Capture Volume", label);
2031 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2032 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2033 if (err < 0)
2034 return err;
2035 }
2036
Joseph Chanc577b8a2006-11-29 15:29:40 +01002037 return 0;
2038}
2039
Harald Welte76d9b0d2008-09-09 15:50:37 +08002040static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2041{
2042 unsigned int def_conf;
2043 unsigned char seqassoc;
2044
Takashi Iwai2f334f92009-02-20 14:37:42 +01002045 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002046 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2047 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002048 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2049 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2050 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2051 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002052 }
2053
2054 return;
2055}
2056
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002057static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002058 struct snd_ctl_elem_value *ucontrol)
2059{
2060 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2061 struct via_spec *spec = codec->spec;
2062
2063 if (spec->codec_type != VT1708)
2064 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002065 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002066 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002067 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002068 return 0;
2069}
2070
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002071static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002072 struct snd_ctl_elem_value *ucontrol)
2073{
2074 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2075 struct via_spec *spec = codec->spec;
2076 int change;
2077
2078 if (spec->codec_type != VT1708)
2079 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002080 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002081 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002082 == !spec->vt1708_jack_detect;
2083 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002084 mute_aa_path(codec, 1);
2085 notify_aa_path_ctls(codec);
2086 }
2087 return change;
2088}
2089
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002090static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2091 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2092 .name = "Jack Detect",
2093 .count = 1,
2094 .info = snd_ctl_boolean_mono_info,
2095 .get = vt1708_jack_detect_get,
2096 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002097};
2098
Takashi Iwai12daef62011-06-18 17:45:49 +02002099static void fill_dig_outs(struct hda_codec *codec);
2100static void fill_dig_in(struct hda_codec *codec);
2101
2102static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002103{
2104 struct via_spec *spec = codec->spec;
2105 int err;
2106
2107 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2108 if (err < 0)
2109 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002110 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002111 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002112
Takashi Iwai4a796162011-06-17 17:53:38 +02002113 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002114 if (err < 0)
2115 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002116 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002117 if (err < 0)
2118 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002119 err = via_auto_create_speaker_ctls(codec);
2120 if (err < 0)
2121 return err;
Takashi Iwai620e2b22011-06-17 17:19:19 +02002122 err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002123 if (err < 0)
2124 return err;
2125
2126 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2127
Takashi Iwai12daef62011-06-18 17:45:49 +02002128 fill_dig_outs(codec);
2129 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002130
Takashi Iwai603c4012008-07-30 15:01:44 +02002131 if (spec->kctls.list)
2132 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002133
Takashi Iwai096a8852011-06-20 12:09:02 +02002134 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002135
Harald Welte0aa62ae2008-09-09 15:58:27 +08002136 spec->input_mux = &spec->private_imux[0];
2137
Takashi Iwaiece8d042011-06-19 16:24:21 +02002138 if (spec->hp_mux) {
2139 err = via_hp_build(codec);
2140 if (err < 0)
2141 return err;
2142 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002143
Takashi Iwaif4a78282011-06-17 18:46:48 +02002144 err = via_smart51_build(codec);
2145 if (err < 0)
2146 return err;
2147
Takashi Iwai5d417622011-06-20 11:32:27 +02002148 /* assign slave outs */
2149 if (spec->slave_dig_outs[0])
2150 codec->slave_dig_outs = spec->slave_dig_outs;
2151
Joseph Chanc577b8a2006-11-29 15:29:40 +01002152 return 1;
2153}
2154
Takashi Iwai5d417622011-06-20 11:32:27 +02002155static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002156{
Lydia Wang25eaba22009-10-10 19:08:43 +08002157 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002158 if (spec->multiout.dig_out_nid)
2159 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2160 if (spec->slave_dig_outs[0])
2161 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2162}
Lydia Wang25eaba22009-10-10 19:08:43 +08002163
Takashi Iwai5d417622011-06-20 11:32:27 +02002164static void via_auto_init_dig_in(struct hda_codec *codec)
2165{
2166 struct via_spec *spec = codec->spec;
2167 if (!spec->dig_in_nid)
2168 return;
2169 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2170 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2171}
2172
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002173/* initialize the unsolicited events */
2174static void via_auto_init_unsol_event(struct hda_codec *codec)
2175{
2176 struct via_spec *spec = codec->spec;
2177 struct auto_pin_cfg *cfg = &spec->autocfg;
2178 unsigned int ev;
2179 int i;
2180
2181 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2182 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2183 AC_VERB_SET_UNSOLICITED_ENABLE,
2184 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2185
2186 if (cfg->speaker_pins[0])
2187 ev = VIA_LINE_EVENT;
2188 else
2189 ev = 0;
2190 for (i = 0; i < cfg->line_outs; i++) {
2191 if (cfg->line_out_pins[i] &&
2192 is_jack_detectable(codec, cfg->line_out_pins[i]))
2193 snd_hda_codec_write(codec, cfg->line_out_pins[0], 0,
2194 AC_VERB_SET_UNSOLICITED_ENABLE,
2195 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2196 }
2197
2198 for (i = 0; i < cfg->num_inputs; i++) {
2199 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2200 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2201 AC_VERB_SET_UNSOLICITED_ENABLE,
2202 AC_USRSP_EN | VIA_JACK_EVENT);
2203 }
2204}
2205
Takashi Iwai5d417622011-06-20 11:32:27 +02002206static int via_init(struct hda_codec *codec)
2207{
2208 struct via_spec *spec = codec->spec;
2209 int i;
2210
2211 for (i = 0; i < spec->num_iverbs; i++)
2212 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2213
Joseph Chanc577b8a2006-11-29 15:29:40 +01002214 via_auto_init_multi_out(codec);
2215 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002216 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002217 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002218 via_auto_init_dig_outs(codec);
2219 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002220
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002221 via_auto_init_unsol_event(codec);
2222
2223 via_hp_automute(codec);
2224 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002225
Joseph Chanc577b8a2006-11-29 15:29:40 +01002226 return 0;
2227}
2228
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002229static void vt1708_update_hp_jack_state(struct work_struct *work)
2230{
2231 struct via_spec *spec = container_of(work, struct via_spec,
2232 vt1708_hp_work.work);
2233 if (spec->codec_type != VT1708)
2234 return;
2235 /* if jack state toggled */
2236 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002237 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002238 spec->vt1708_hp_present ^= 1;
2239 via_hp_automute(spec->codec);
2240 }
2241 vt1708_start_hp_work(spec);
2242}
2243
Takashi Iwai337b9d02009-07-07 18:18:59 +02002244static int get_mux_nids(struct hda_codec *codec)
2245{
2246 struct via_spec *spec = codec->spec;
2247 hda_nid_t nid, conn[8];
2248 unsigned int type;
2249 int i, n;
2250
2251 for (i = 0; i < spec->num_adc_nids; i++) {
2252 nid = spec->adc_nids[i];
2253 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002254 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002255 if (type == AC_WID_PIN)
2256 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002257 n = snd_hda_get_connections(codec, nid, conn,
2258 ARRAY_SIZE(conn));
2259 if (n <= 0)
2260 break;
2261 if (n > 1) {
2262 spec->mux_nids[i] = nid;
2263 break;
2264 }
2265 nid = conn[0];
2266 }
2267 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002268 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002269}
2270
Joseph Chanc577b8a2006-11-29 15:29:40 +01002271static int patch_vt1708(struct hda_codec *codec)
2272{
2273 struct via_spec *spec;
2274 int err;
2275
2276 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002277 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002278 if (spec == NULL)
2279 return -ENOMEM;
2280
Takashi Iwai620e2b22011-06-17 17:19:19 +02002281 spec->aa_mix_nid = 0x17;
2282
Takashi Iwai12daef62011-06-18 17:45:49 +02002283 /* Add HP and CD pin config connect bit re-config action */
2284 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2285 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2286
Joseph Chanc577b8a2006-11-29 15:29:40 +01002287 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002288 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002289 if (err < 0) {
2290 via_free(codec);
2291 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002292 }
2293
Takashi Iwai12daef62011-06-18 17:45:49 +02002294 /* add jack detect on/off control */
2295 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2296 return -ENOMEM;
2297
Takashi Iwaibc9b562382008-05-23 17:50:27 +02002298 /* disable 32bit format on VT1708 */
2299 if (codec->vendor_id == 0x11061708)
2300 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002301
Joseph Chanc577b8a2006-11-29 15:29:40 +01002302 codec->patch_ops = via_patch_ops;
2303
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002304 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002305 return 0;
2306}
2307
Joseph Chanc577b8a2006-11-29 15:29:40 +01002308static int patch_vt1709_10ch(struct hda_codec *codec)
2309{
2310 struct via_spec *spec;
2311 int err;
2312
2313 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002314 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002315 if (spec == NULL)
2316 return -ENOMEM;
2317
Takashi Iwai620e2b22011-06-17 17:19:19 +02002318 spec->aa_mix_nid = 0x18;
2319
Takashi Iwai12daef62011-06-18 17:45:49 +02002320 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002321 if (err < 0) {
2322 via_free(codec);
2323 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002324 }
2325
Joseph Chanc577b8a2006-11-29 15:29:40 +01002326 codec->patch_ops = via_patch_ops;
2327
Joseph Chanc577b8a2006-11-29 15:29:40 +01002328 return 0;
2329}
2330/*
2331 * generic initialization of ADC, input mixers and output mixers
2332 */
Joseph Chanc577b8a2006-11-29 15:29:40 +01002333static int patch_vt1709_6ch(struct hda_codec *codec)
2334{
2335 struct via_spec *spec;
2336 int err;
2337
2338 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002339 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002340 if (spec == NULL)
2341 return -ENOMEM;
2342
Takashi Iwai620e2b22011-06-17 17:19:19 +02002343 spec->aa_mix_nid = 0x18;
2344
Takashi Iwai12daef62011-06-18 17:45:49 +02002345 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002346 if (err < 0) {
2347 via_free(codec);
2348 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002349 }
2350
Joseph Chanc577b8a2006-11-29 15:29:40 +01002351 codec->patch_ops = via_patch_ops;
2352
Josepch Chanf7278fd2007-12-13 16:40:40 +01002353 return 0;
2354}
2355
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002356static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2357{
2358 struct via_spec *spec = codec->spec;
2359 int imux_is_smixer;
2360 unsigned int parm;
2361 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002362 if ((spec->codec_type != VT1708B_4CH) &&
2363 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002364 is_8ch = 1;
2365
2366 /* SW0 (17h) = stereo mixer */
2367 imux_is_smixer =
2368 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2369 == ((spec->codec_type == VT1708S) ? 5 : 0));
2370 /* inputs */
2371 /* PW 1/2/5 (1ah/1bh/1eh) */
2372 parm = AC_PWRST_D3;
2373 set_pin_power_state(codec, 0x1a, &parm);
2374 set_pin_power_state(codec, 0x1b, &parm);
2375 set_pin_power_state(codec, 0x1e, &parm);
2376 if (imux_is_smixer)
2377 parm = AC_PWRST_D0;
2378 /* SW0 (17h), AIW 0/1 (13h/14h) */
2379 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2380 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2381 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2382
2383 /* outputs */
2384 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2385 parm = AC_PWRST_D3;
2386 set_pin_power_state(codec, 0x19, &parm);
2387 if (spec->smart51_enabled)
2388 set_pin_power_state(codec, 0x1b, &parm);
2389 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2390 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2391
2392 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2393 if (is_8ch) {
2394 parm = AC_PWRST_D3;
2395 set_pin_power_state(codec, 0x22, &parm);
2396 if (spec->smart51_enabled)
2397 set_pin_power_state(codec, 0x1a, &parm);
2398 snd_hda_codec_write(codec, 0x26, 0,
2399 AC_VERB_SET_POWER_STATE, parm);
2400 snd_hda_codec_write(codec, 0x24, 0,
2401 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002402 } else if (codec->vendor_id == 0x11064397) {
2403 /* PW7(23h), SW2(27h), AOW2(25h) */
2404 parm = AC_PWRST_D3;
2405 set_pin_power_state(codec, 0x23, &parm);
2406 if (spec->smart51_enabled)
2407 set_pin_power_state(codec, 0x1a, &parm);
2408 snd_hda_codec_write(codec, 0x27, 0,
2409 AC_VERB_SET_POWER_STATE, parm);
2410 snd_hda_codec_write(codec, 0x25, 0,
2411 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002412 }
2413
2414 /* PW 3/4/7 (1ch/1dh/23h) */
2415 parm = AC_PWRST_D3;
2416 /* force to D0 for internal Speaker */
2417 set_pin_power_state(codec, 0x1c, &parm);
2418 set_pin_power_state(codec, 0x1d, &parm);
2419 if (is_8ch)
2420 set_pin_power_state(codec, 0x23, &parm);
2421
2422 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2423 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2424 imux_is_smixer ? AC_PWRST_D0 : parm);
2425 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2426 if (is_8ch) {
2427 snd_hda_codec_write(codec, 0x25, 0,
2428 AC_VERB_SET_POWER_STATE, parm);
2429 snd_hda_codec_write(codec, 0x27, 0,
2430 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002431 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2432 snd_hda_codec_write(codec, 0x25, 0,
2433 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002434}
2435
Lydia Wang518bf3b2009-10-10 19:07:29 +08002436static int patch_vt1708S(struct hda_codec *codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002437static int patch_vt1708B_8ch(struct hda_codec *codec)
2438{
2439 struct via_spec *spec;
2440 int err;
2441
Lydia Wang518bf3b2009-10-10 19:07:29 +08002442 if (get_codec_type(codec) == VT1708BCE)
2443 return patch_vt1708S(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002444 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002445 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002446 if (spec == NULL)
2447 return -ENOMEM;
2448
Takashi Iwai620e2b22011-06-17 17:19:19 +02002449 spec->aa_mix_nid = 0x16;
2450
Josepch Chanf7278fd2007-12-13 16:40:40 +01002451 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002452 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002453 if (err < 0) {
2454 via_free(codec);
2455 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002456 }
2457
Josepch Chanf7278fd2007-12-13 16:40:40 +01002458 codec->patch_ops = via_patch_ops;
2459
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002460 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2461
Josepch Chanf7278fd2007-12-13 16:40:40 +01002462 return 0;
2463}
2464
2465static int patch_vt1708B_4ch(struct hda_codec *codec)
2466{
2467 struct via_spec *spec;
2468 int err;
2469
2470 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002471 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002472 if (spec == NULL)
2473 return -ENOMEM;
2474
Josepch Chanf7278fd2007-12-13 16:40:40 +01002475 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002476 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002477 if (err < 0) {
2478 via_free(codec);
2479 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002480 }
2481
Josepch Chanf7278fd2007-12-13 16:40:40 +01002482 codec->patch_ops = via_patch_ops;
2483
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002484 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2485
Joseph Chanc577b8a2006-11-29 15:29:40 +01002486 return 0;
2487}
2488
Harald Welted949cac2008-09-09 15:56:01 +08002489/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002490static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002491 /* Enable Mic Boost Volume backdoor */
2492 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002493 /* don't bybass mixer */
2494 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002495 { }
2496};
2497
Takashi Iwai9da29272009-05-07 16:31:14 +02002498/* fill out digital output widgets; one for master and one for slave outputs */
2499static void fill_dig_outs(struct hda_codec *codec)
2500{
2501 struct via_spec *spec = codec->spec;
2502 int i;
2503
2504 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2505 hda_nid_t nid;
2506 int conn;
2507
2508 nid = spec->autocfg.dig_out_pins[i];
2509 if (!nid)
2510 continue;
2511 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2512 if (conn < 1)
2513 continue;
2514 if (!spec->multiout.dig_out_nid)
2515 spec->multiout.dig_out_nid = nid;
2516 else {
2517 spec->slave_dig_outs[0] = nid;
2518 break; /* at most two dig outs */
2519 }
2520 }
2521}
2522
Takashi Iwai12daef62011-06-18 17:45:49 +02002523static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002524{
2525 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002526 hda_nid_t dig_nid;
2527 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002528
Takashi Iwai12daef62011-06-18 17:45:49 +02002529 if (!spec->autocfg.dig_in_pin)
2530 return;
Harald Welted949cac2008-09-09 15:56:01 +08002531
Takashi Iwai12daef62011-06-18 17:45:49 +02002532 dig_nid = codec->start_nid;
2533 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2534 unsigned int wcaps = get_wcaps(codec, dig_nid);
2535 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2536 continue;
2537 if (!(wcaps & AC_WCAP_DIGITAL))
2538 continue;
2539 if (!(wcaps & AC_WCAP_CONN_LIST))
2540 continue;
2541 err = get_connection_index(codec, dig_nid,
2542 spec->autocfg.dig_in_pin);
2543 if (err >= 0) {
2544 spec->dig_in_nid = dig_nid;
2545 break;
2546 }
2547 }
Harald Welted949cac2008-09-09 15:56:01 +08002548}
2549
Lydia Wang6369bcf2009-10-10 19:08:31 +08002550static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2551 int offset, int num_steps, int step_size)
2552{
2553 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2554 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2555 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2556 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2557 (0 << AC_AMPCAP_MUTE_SHIFT));
2558}
2559
Harald Welted949cac2008-09-09 15:56:01 +08002560static int patch_vt1708S(struct hda_codec *codec)
2561{
2562 struct via_spec *spec;
2563 int err;
2564
2565 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002566 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002567 if (spec == NULL)
2568 return -ENOMEM;
2569
Takashi Iwai620e2b22011-06-17 17:19:19 +02002570 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002571 override_mic_boost(codec, 0x1a, 0, 3, 40);
2572 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002573
Harald Welted949cac2008-09-09 15:56:01 +08002574 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002575 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002576 if (err < 0) {
2577 via_free(codec);
2578 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002579 }
2580
Takashi Iwai096a8852011-06-20 12:09:02 +02002581 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002582
Harald Welted949cac2008-09-09 15:56:01 +08002583 codec->patch_ops = via_patch_ops;
2584
Lydia Wang518bf3b2009-10-10 19:07:29 +08002585 /* correct names for VT1708BCE */
2586 if (get_codec_type(codec) == VT1708BCE) {
2587 kfree(codec->chip_name);
2588 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2589 snprintf(codec->bus->card->mixername,
2590 sizeof(codec->bus->card->mixername),
2591 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f630f2011-03-22 16:25:56 +08002592 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002593 /* correct names for VT1705 */
2594 if (codec->vendor_id == 0x11064397) {
2595 kfree(codec->chip_name);
2596 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2597 snprintf(codec->bus->card->mixername,
2598 sizeof(codec->bus->card->mixername),
2599 "%s %s", codec->vendor_name, codec->chip_name);
2600 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002601 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002602 return 0;
2603}
2604
2605/* Patch for VT1702 */
2606
Takashi Iwai096a8852011-06-20 12:09:02 +02002607static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002608 /* mixer enable */
2609 {0x1, 0xF88, 0x3},
2610 /* GPIO 0~2 */
2611 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002612 { }
2613};
2614
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002615static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2616{
2617 int imux_is_smixer =
2618 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2619 unsigned int parm;
2620 /* inputs */
2621 /* PW 1/2/5 (14h/15h/18h) */
2622 parm = AC_PWRST_D3;
2623 set_pin_power_state(codec, 0x14, &parm);
2624 set_pin_power_state(codec, 0x15, &parm);
2625 set_pin_power_state(codec, 0x18, &parm);
2626 if (imux_is_smixer)
2627 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2628 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2629 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2630 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2631 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2632 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2633
2634 /* outputs */
2635 /* PW 3/4 (16h/17h) */
2636 parm = AC_PWRST_D3;
2637 set_pin_power_state(codec, 0x17, &parm);
2638 set_pin_power_state(codec, 0x16, &parm);
2639 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2640 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2641 imux_is_smixer ? AC_PWRST_D0 : parm);
2642 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2643 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2644}
2645
Harald Welted949cac2008-09-09 15:56:01 +08002646static int patch_vt1702(struct hda_codec *codec)
2647{
2648 struct via_spec *spec;
2649 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002650
2651 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002652 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002653 if (spec == NULL)
2654 return -ENOMEM;
2655
Takashi Iwai620e2b22011-06-17 17:19:19 +02002656 spec->aa_mix_nid = 0x1a;
2657
Takashi Iwai12daef62011-06-18 17:45:49 +02002658 /* limit AA path volume to 0 dB */
2659 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2660 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2661 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2662 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2663 (1 << AC_AMPCAP_MUTE_SHIFT));
2664
Harald Welted949cac2008-09-09 15:56:01 +08002665 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002666 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002667 if (err < 0) {
2668 via_free(codec);
2669 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002670 }
2671
Takashi Iwai096a8852011-06-20 12:09:02 +02002672 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002673
Harald Welted949cac2008-09-09 15:56:01 +08002674 codec->patch_ops = via_patch_ops;
2675
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002676 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002677 return 0;
2678}
2679
Lydia Wangeb7188c2009-10-10 19:08:34 +08002680/* Patch for VT1718S */
2681
Takashi Iwai096a8852011-06-20 12:09:02 +02002682static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002683 /* Enable MW0 adjust Gain 5 */
2684 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002685 /* Enable Boost Volume backdoor */
2686 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002687
Lydia Wangeb7188c2009-10-10 19:08:34 +08002688 { }
2689};
2690
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002691static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2692{
2693 struct via_spec *spec = codec->spec;
2694 int imux_is_smixer;
2695 unsigned int parm;
2696 /* MUX6 (1eh) = stereo mixer */
2697 imux_is_smixer =
2698 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2699 /* inputs */
2700 /* PW 5/6/7 (29h/2ah/2bh) */
2701 parm = AC_PWRST_D3;
2702 set_pin_power_state(codec, 0x29, &parm);
2703 set_pin_power_state(codec, 0x2a, &parm);
2704 set_pin_power_state(codec, 0x2b, &parm);
2705 if (imux_is_smixer)
2706 parm = AC_PWRST_D0;
2707 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2708 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2709 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2710 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2711 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2712
2713 /* outputs */
2714 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2715 parm = AC_PWRST_D3;
2716 set_pin_power_state(codec, 0x27, &parm);
2717 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2718 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2719
2720 /* PW2 (26h), AOW2 (ah) */
2721 parm = AC_PWRST_D3;
2722 set_pin_power_state(codec, 0x26, &parm);
2723 if (spec->smart51_enabled)
2724 set_pin_power_state(codec, 0x2b, &parm);
2725 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2726
2727 /* PW0 (24h), AOW0 (8h) */
2728 parm = AC_PWRST_D3;
2729 set_pin_power_state(codec, 0x24, &parm);
2730 if (!spec->hp_independent_mode) /* check for redirected HP */
2731 set_pin_power_state(codec, 0x28, &parm);
2732 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2733 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2734 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2735 imux_is_smixer ? AC_PWRST_D0 : parm);
2736
2737 /* PW1 (25h), AOW1 (9h) */
2738 parm = AC_PWRST_D3;
2739 set_pin_power_state(codec, 0x25, &parm);
2740 if (spec->smart51_enabled)
2741 set_pin_power_state(codec, 0x2a, &parm);
2742 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2743
2744 if (spec->hp_independent_mode) {
2745 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2746 parm = AC_PWRST_D3;
2747 set_pin_power_state(codec, 0x28, &parm);
2748 snd_hda_codec_write(codec, 0x1b, 0,
2749 AC_VERB_SET_POWER_STATE, parm);
2750 snd_hda_codec_write(codec, 0x34, 0,
2751 AC_VERB_SET_POWER_STATE, parm);
2752 snd_hda_codec_write(codec, 0xc, 0,
2753 AC_VERB_SET_POWER_STATE, parm);
2754 }
2755}
2756
Lydia Wangeb7188c2009-10-10 19:08:34 +08002757static int patch_vt1718S(struct hda_codec *codec)
2758{
2759 struct via_spec *spec;
2760 int err;
2761
2762 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002763 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002764 if (spec == NULL)
2765 return -ENOMEM;
2766
Takashi Iwai620e2b22011-06-17 17:19:19 +02002767 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002768 override_mic_boost(codec, 0x2b, 0, 3, 40);
2769 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002770
Lydia Wangeb7188c2009-10-10 19:08:34 +08002771 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002772 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002773 if (err < 0) {
2774 via_free(codec);
2775 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002776 }
2777
Takashi Iwai096a8852011-06-20 12:09:02 +02002778 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002779
Lydia Wangeb7188c2009-10-10 19:08:34 +08002780 codec->patch_ops = via_patch_ops;
2781
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002782 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2783
Lydia Wangeb7188c2009-10-10 19:08:34 +08002784 return 0;
2785}
Lydia Wangf3db4232009-10-10 19:08:41 +08002786
2787/* Patch for VT1716S */
2788
2789static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2790 struct snd_ctl_elem_info *uinfo)
2791{
2792 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2793 uinfo->count = 1;
2794 uinfo->value.integer.min = 0;
2795 uinfo->value.integer.max = 1;
2796 return 0;
2797}
2798
2799static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2800 struct snd_ctl_elem_value *ucontrol)
2801{
2802 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2803 int index = 0;
2804
2805 index = snd_hda_codec_read(codec, 0x26, 0,
2806 AC_VERB_GET_CONNECT_SEL, 0);
2807 if (index != -1)
2808 *ucontrol->value.integer.value = index;
2809
2810 return 0;
2811}
2812
2813static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2814 struct snd_ctl_elem_value *ucontrol)
2815{
2816 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2817 struct via_spec *spec = codec->spec;
2818 int index = *ucontrol->value.integer.value;
2819
2820 snd_hda_codec_write(codec, 0x26, 0,
2821 AC_VERB_SET_CONNECT_SEL, index);
2822 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002823 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002824 return 1;
2825}
2826
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002827static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002828 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2829 {
2830 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2831 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002832 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08002833 .count = 1,
2834 .info = vt1716s_dmic_info,
2835 .get = vt1716s_dmic_get,
2836 .put = vt1716s_dmic_put,
2837 },
2838 {} /* end */
2839};
2840
2841
2842/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002843static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002844 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
2845 { } /* end */
2846};
2847
Takashi Iwai096a8852011-06-20 12:09:02 +02002848static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002849 /* Enable Boost Volume backdoor */
2850 {0x1, 0xf8a, 0x80},
2851 /* don't bybass mixer */
2852 {0x1, 0xf88, 0xc0},
2853 /* Enable mono output */
2854 {0x1, 0xf90, 0x08},
2855 { }
2856};
2857
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002858static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
2859{
2860 struct via_spec *spec = codec->spec;
2861 int imux_is_smixer;
2862 unsigned int parm;
2863 unsigned int mono_out, present;
2864 /* SW0 (17h) = stereo mixer */
2865 imux_is_smixer =
2866 (snd_hda_codec_read(codec, 0x17, 0,
2867 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
2868 /* inputs */
2869 /* PW 1/2/5 (1ah/1bh/1eh) */
2870 parm = AC_PWRST_D3;
2871 set_pin_power_state(codec, 0x1a, &parm);
2872 set_pin_power_state(codec, 0x1b, &parm);
2873 set_pin_power_state(codec, 0x1e, &parm);
2874 if (imux_is_smixer)
2875 parm = AC_PWRST_D0;
2876 /* SW0 (17h), AIW0(13h) */
2877 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2878 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2879
2880 parm = AC_PWRST_D3;
2881 set_pin_power_state(codec, 0x1e, &parm);
2882 /* PW11 (22h) */
2883 if (spec->dmic_enabled)
2884 set_pin_power_state(codec, 0x22, &parm);
2885 else
2886 snd_hda_codec_write(codec, 0x22, 0,
2887 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
2888
2889 /* SW2(26h), AIW1(14h) */
2890 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
2891 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2892
2893 /* outputs */
2894 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2895 parm = AC_PWRST_D3;
2896 set_pin_power_state(codec, 0x19, &parm);
2897 /* Smart 5.1 PW2(1bh) */
2898 if (spec->smart51_enabled)
2899 set_pin_power_state(codec, 0x1b, &parm);
2900 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2901 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2902
2903 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
2904 parm = AC_PWRST_D3;
2905 set_pin_power_state(codec, 0x23, &parm);
2906 /* Smart 5.1 PW1(1ah) */
2907 if (spec->smart51_enabled)
2908 set_pin_power_state(codec, 0x1a, &parm);
2909 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
2910
2911 /* Smart 5.1 PW5(1eh) */
2912 if (spec->smart51_enabled)
2913 set_pin_power_state(codec, 0x1e, &parm);
2914 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
2915
2916 /* Mono out */
2917 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
2918 present = snd_hda_jack_detect(codec, 0x1c);
2919
2920 if (present)
2921 mono_out = 0;
2922 else {
2923 present = snd_hda_jack_detect(codec, 0x1d);
2924 if (!spec->hp_independent_mode && present)
2925 mono_out = 0;
2926 else
2927 mono_out = 1;
2928 }
2929 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
2930 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
2931 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
2932 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
2933
2934 /* PW 3/4 (1ch/1dh) */
2935 parm = AC_PWRST_D3;
2936 set_pin_power_state(codec, 0x1c, &parm);
2937 set_pin_power_state(codec, 0x1d, &parm);
2938 /* HP Independent Mode, power on AOW3 */
2939 if (spec->hp_independent_mode)
2940 snd_hda_codec_write(codec, 0x25, 0,
2941 AC_VERB_SET_POWER_STATE, parm);
2942
2943 /* force to D0 for internal Speaker */
2944 /* MW0 (16h), AOW0 (10h) */
2945 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2946 imux_is_smixer ? AC_PWRST_D0 : parm);
2947 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
2948 mono_out ? AC_PWRST_D0 : parm);
2949}
2950
Lydia Wangf3db4232009-10-10 19:08:41 +08002951static int patch_vt1716S(struct hda_codec *codec)
2952{
2953 struct via_spec *spec;
2954 int err;
2955
2956 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002957 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002958 if (spec == NULL)
2959 return -ENOMEM;
2960
Takashi Iwai620e2b22011-06-17 17:19:19 +02002961 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002962 override_mic_boost(codec, 0x1a, 0, 3, 40);
2963 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002964
Lydia Wangf3db4232009-10-10 19:08:41 +08002965 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002966 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002967 if (err < 0) {
2968 via_free(codec);
2969 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08002970 }
2971
Takashi Iwai096a8852011-06-20 12:09:02 +02002972 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08002973
Lydia Wangf3db4232009-10-10 19:08:41 +08002974 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
2975 spec->num_mixers++;
2976
2977 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
2978
2979 codec->patch_ops = via_patch_ops;
2980
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002981 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08002982 return 0;
2983}
Lydia Wang25eaba22009-10-10 19:08:43 +08002984
2985/* for vt2002P */
2986
Takashi Iwai096a8852011-06-20 12:09:02 +02002987static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08002988 /* Class-D speaker related verbs */
2989 {0x1, 0xfe0, 0x4},
2990 {0x1, 0xfe9, 0x80},
2991 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08002992 /* Enable Boost Volume backdoor */
2993 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08002994 /* Enable AOW0 to MW9 */
2995 {0x1, 0xfb8, 0x88},
2996 { }
2997};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002998
Takashi Iwai096a8852011-06-20 12:09:02 +02002999static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003000 /* Enable Boost Volume backdoor */
3001 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003002 /* Enable AOW0 to MW9 */
3003 {0x1, 0xfb8, 0x88},
3004 { }
3005};
Lydia Wang25eaba22009-10-10 19:08:43 +08003006
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003007static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3008{
3009 struct via_spec *spec = codec->spec;
3010 int imux_is_smixer;
3011 unsigned int parm;
3012 unsigned int present;
3013 /* MUX9 (1eh) = stereo mixer */
3014 imux_is_smixer =
3015 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3016 /* inputs */
3017 /* PW 5/6/7 (29h/2ah/2bh) */
3018 parm = AC_PWRST_D3;
3019 set_pin_power_state(codec, 0x29, &parm);
3020 set_pin_power_state(codec, 0x2a, &parm);
3021 set_pin_power_state(codec, 0x2b, &parm);
3022 parm = AC_PWRST_D0;
3023 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3024 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3025 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3026 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3027 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3028
3029 /* outputs */
3030 /* AOW0 (8h)*/
3031 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3032
Lydia Wang118909562011-03-23 17:57:34 +08003033 if (spec->codec_type == VT1802) {
3034 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3035 parm = AC_PWRST_D3;
3036 set_pin_power_state(codec, 0x28, &parm);
3037 snd_hda_codec_write(codec, 0x18, 0,
3038 AC_VERB_SET_POWER_STATE, parm);
3039 snd_hda_codec_write(codec, 0x38, 0,
3040 AC_VERB_SET_POWER_STATE, parm);
3041 } else {
3042 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3043 parm = AC_PWRST_D3;
3044 set_pin_power_state(codec, 0x26, &parm);
3045 snd_hda_codec_write(codec, 0x1c, 0,
3046 AC_VERB_SET_POWER_STATE, parm);
3047 snd_hda_codec_write(codec, 0x37, 0,
3048 AC_VERB_SET_POWER_STATE, parm);
3049 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003050
Lydia Wang118909562011-03-23 17:57:34 +08003051 if (spec->codec_type == VT1802) {
3052 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3053 parm = AC_PWRST_D3;
3054 set_pin_power_state(codec, 0x25, &parm);
3055 snd_hda_codec_write(codec, 0x15, 0,
3056 AC_VERB_SET_POWER_STATE, parm);
3057 snd_hda_codec_write(codec, 0x35, 0,
3058 AC_VERB_SET_POWER_STATE, parm);
3059 } else {
3060 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3061 parm = AC_PWRST_D3;
3062 set_pin_power_state(codec, 0x25, &parm);
3063 snd_hda_codec_write(codec, 0x19, 0,
3064 AC_VERB_SET_POWER_STATE, parm);
3065 snd_hda_codec_write(codec, 0x35, 0,
3066 AC_VERB_SET_POWER_STATE, parm);
3067 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003068
3069 if (spec->hp_independent_mode)
3070 snd_hda_codec_write(codec, 0x9, 0,
3071 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3072
3073 /* Class-D */
3074 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3075 present = snd_hda_jack_detect(codec, 0x25);
3076
3077 parm = AC_PWRST_D3;
3078 set_pin_power_state(codec, 0x24, &parm);
3079 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003080 if (spec->codec_type == VT1802)
3081 snd_hda_codec_write(codec, 0x14, 0,
3082 AC_VERB_SET_POWER_STATE, parm);
3083 else
3084 snd_hda_codec_write(codec, 0x18, 0,
3085 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003086 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3087
3088 /* Mono Out */
3089 present = snd_hda_jack_detect(codec, 0x26);
3090
3091 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003092 if (spec->codec_type == VT1802) {
3093 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3094 snd_hda_codec_write(codec, 0x33, 0,
3095 AC_VERB_SET_POWER_STATE, parm);
3096 snd_hda_codec_write(codec, 0x1c, 0,
3097 AC_VERB_SET_POWER_STATE, parm);
3098 snd_hda_codec_write(codec, 0x3c, 0,
3099 AC_VERB_SET_POWER_STATE, parm);
3100 } else {
3101 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3102 snd_hda_codec_write(codec, 0x31, 0,
3103 AC_VERB_SET_POWER_STATE, parm);
3104 snd_hda_codec_write(codec, 0x17, 0,
3105 AC_VERB_SET_POWER_STATE, parm);
3106 snd_hda_codec_write(codec, 0x3b, 0,
3107 AC_VERB_SET_POWER_STATE, parm);
3108 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003109 /* MW9 (21h) */
3110 if (imux_is_smixer || !is_aa_path_mute(codec))
3111 snd_hda_codec_write(codec, 0x21, 0,
3112 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3113 else
3114 snd_hda_codec_write(codec, 0x21, 0,
3115 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3116}
Lydia Wang25eaba22009-10-10 19:08:43 +08003117
3118/* patch for vt2002P */
3119static int patch_vt2002P(struct hda_codec *codec)
3120{
3121 struct via_spec *spec;
3122 int err;
3123
3124 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003125 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003126 if (spec == NULL)
3127 return -ENOMEM;
3128
Takashi Iwai620e2b22011-06-17 17:19:19 +02003129 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003130 override_mic_boost(codec, 0x2b, 0, 3, 40);
3131 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003132
Lydia Wang25eaba22009-10-10 19:08:43 +08003133 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003134 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003135 if (err < 0) {
3136 via_free(codec);
3137 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003138 }
3139
Lydia Wang118909562011-03-23 17:57:34 +08003140 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003141 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003142 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003143 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003144
Lydia Wang25eaba22009-10-10 19:08:43 +08003145 codec->patch_ops = via_patch_ops;
3146
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003147 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003148 return 0;
3149}
Lydia Wangab6734e2009-10-10 19:08:46 +08003150
3151/* for vt1812 */
3152
Takashi Iwai096a8852011-06-20 12:09:02 +02003153static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003154 /* Enable Boost Volume backdoor */
3155 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003156 /* Enable AOW0 to MW9 */
3157 {0x1, 0xfb8, 0xa8},
3158 { }
3159};
3160
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003161static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3162{
3163 struct via_spec *spec = codec->spec;
3164 int imux_is_smixer =
3165 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3166 unsigned int parm;
3167 unsigned int present;
3168 /* MUX10 (1eh) = stereo mixer */
3169 imux_is_smixer =
3170 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3171 /* inputs */
3172 /* PW 5/6/7 (29h/2ah/2bh) */
3173 parm = AC_PWRST_D3;
3174 set_pin_power_state(codec, 0x29, &parm);
3175 set_pin_power_state(codec, 0x2a, &parm);
3176 set_pin_power_state(codec, 0x2b, &parm);
3177 parm = AC_PWRST_D0;
3178 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3179 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3180 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3181 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3182 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3183
3184 /* outputs */
3185 /* AOW0 (8h)*/
3186 snd_hda_codec_write(codec, 0x8, 0,
3187 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3188
3189 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3190 parm = AC_PWRST_D3;
3191 set_pin_power_state(codec, 0x28, &parm);
3192 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3193 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3194
3195 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3196 parm = AC_PWRST_D3;
3197 set_pin_power_state(codec, 0x25, &parm);
3198 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3199 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3200 if (spec->hp_independent_mode)
3201 snd_hda_codec_write(codec, 0x9, 0,
3202 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3203
3204 /* Internal Speaker */
3205 /* PW0 (24h), MW0(14h), MUX0(34h) */
3206 present = snd_hda_jack_detect(codec, 0x25);
3207
3208 parm = AC_PWRST_D3;
3209 set_pin_power_state(codec, 0x24, &parm);
3210 if (present) {
3211 snd_hda_codec_write(codec, 0x14, 0,
3212 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3213 snd_hda_codec_write(codec, 0x34, 0,
3214 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3215 } else {
3216 snd_hda_codec_write(codec, 0x14, 0,
3217 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3218 snd_hda_codec_write(codec, 0x34, 0,
3219 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3220 }
3221
3222
3223 /* Mono Out */
3224 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3225 present = snd_hda_jack_detect(codec, 0x28);
3226
3227 parm = AC_PWRST_D3;
3228 set_pin_power_state(codec, 0x31, &parm);
3229 if (present) {
3230 snd_hda_codec_write(codec, 0x1c, 0,
3231 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3232 snd_hda_codec_write(codec, 0x3c, 0,
3233 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3234 snd_hda_codec_write(codec, 0x3e, 0,
3235 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3236 } else {
3237 snd_hda_codec_write(codec, 0x1c, 0,
3238 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3239 snd_hda_codec_write(codec, 0x3c, 0,
3240 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3241 snd_hda_codec_write(codec, 0x3e, 0,
3242 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3243 }
3244
3245 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3246 parm = AC_PWRST_D3;
3247 set_pin_power_state(codec, 0x33, &parm);
3248 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3249 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3250
3251}
Lydia Wangab6734e2009-10-10 19:08:46 +08003252
3253/* patch for vt1812 */
3254static int patch_vt1812(struct hda_codec *codec)
3255{
3256 struct via_spec *spec;
3257 int err;
3258
3259 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003260 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003261 if (spec == NULL)
3262 return -ENOMEM;
3263
Takashi Iwai620e2b22011-06-17 17:19:19 +02003264 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003265 override_mic_boost(codec, 0x2b, 0, 3, 40);
3266 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003267
Lydia Wangab6734e2009-10-10 19:08:46 +08003268 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003269 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003270 if (err < 0) {
3271 via_free(codec);
3272 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003273 }
3274
Takashi Iwai096a8852011-06-20 12:09:02 +02003275 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003276
Lydia Wangab6734e2009-10-10 19:08:46 +08003277 codec->patch_ops = via_patch_ops;
3278
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003279 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003280 return 0;
3281}
3282
Joseph Chanc577b8a2006-11-29 15:29:40 +01003283/*
3284 * patch entries
3285 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003286static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003287 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3288 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3289 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3290 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3291 { .id = 0x1106e710, .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 = 0x1106e711, .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 = 0x1106e712, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003296 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003297 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003298 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003299 { .id = 0x1106e714, .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 = 0x1106e715, .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 = 0x1106e716, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003304 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003305 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003306 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003307 { .id = 0x1106e720, .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 = 0x1106e721, .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 = 0x1106e722, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003312 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003313 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003314 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003315 { .id = 0x1106e724, .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 = 0x1106e725, .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 = 0x1106e726, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003320 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003321 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003322 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003323 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003324 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003325 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003326 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003327 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003328 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003329 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003330 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003331 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003332 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003333 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003334 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003335 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003336 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003337 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003338 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003339 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003340 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003341 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003342 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003343 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003344 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003345 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003346 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003347 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003348 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003349 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003350 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003351 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003352 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003353 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003354 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003355 { .id = 0x11060428, .name = "VT1718S",
3356 .patch = patch_vt1718S},
3357 { .id = 0x11064428, .name = "VT1718S",
3358 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003359 { .id = 0x11060441, .name = "VT2020",
3360 .patch = patch_vt1718S},
3361 { .id = 0x11064441, .name = "VT1828S",
3362 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003363 { .id = 0x11060433, .name = "VT1716S",
3364 .patch = patch_vt1716S},
3365 { .id = 0x1106a721, .name = "VT1716S",
3366 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003367 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3368 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003369 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003370 { .id = 0x11060440, .name = "VT1818S",
3371 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003372 { .id = 0x11060446, .name = "VT1802",
3373 .patch = patch_vt2002P},
3374 { .id = 0x11068446, .name = "VT1802",
3375 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003376 {} /* terminator */
3377};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003378
3379MODULE_ALIAS("snd-hda-codec-id:1106*");
3380
3381static struct hda_codec_preset_list via_list = {
3382 .preset = snd_hda_preset_via,
3383 .owner = THIS_MODULE,
3384};
3385
3386MODULE_LICENSE("GPL");
3387MODULE_DESCRIPTION("VIA HD-audio codec");
3388
3389static int __init patch_via_init(void)
3390{
3391 return snd_hda_add_codec_preset(&via_list);
3392}
3393
3394static void __exit patch_via_exit(void)
3395{
3396 snd_hda_delete_codec_preset(&via_list);
3397}
3398
3399module_init(patch_via_init)
3400module_exit(patch_via_exit)