blob: c3be9f124b6898e4e6209519271c54a464586c9b [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;
189#endif
190};
191
Lydia Wang0341ccd2011-03-22 16:25:03 +0800192static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100193static struct via_spec * via_new_spec(struct hda_codec *codec)
194{
195 struct via_spec *spec;
196
197 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
198 if (spec == NULL)
199 return NULL;
200
201 codec->spec = spec;
202 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800203 spec->codec_type = get_codec_type(codec);
204 /* VT1708BCE & VT1708S are almost same */
205 if (spec->codec_type == VT1708BCE)
206 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100207 return spec;
208}
209
Lydia Wang744ff5f2009-10-10 19:07:26 +0800210static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800211{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800212 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800213 u16 ven_id = vendor_id >> 16;
214 u16 dev_id = vendor_id & 0xffff;
215 enum VIA_HDA_CODEC codec_type;
216
217 /* get codec type */
218 if (ven_id != 0x1106)
219 codec_type = UNKNOWN;
220 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
221 codec_type = VT1708;
222 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
223 codec_type = VT1709_10CH;
224 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
225 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800226 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800227 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800228 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
229 codec_type = VT1708BCE;
230 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800231 codec_type = VT1708B_4CH;
232 else if ((dev_id & 0xfff) == 0x397
233 && (dev_id >> 12) < 8)
234 codec_type = VT1708S;
235 else if ((dev_id & 0xfff) == 0x398
236 && (dev_id >> 12) < 8)
237 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800238 else if ((dev_id & 0xfff) == 0x428
239 && (dev_id >> 12) < 8)
240 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800241 else if (dev_id == 0x0433 || dev_id == 0xa721)
242 codec_type = VT1716S;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +0800243 else if (dev_id == 0x0441 || dev_id == 0x4441)
244 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800245 else if (dev_id == 0x0438 || dev_id == 0x4438)
246 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800247 else if (dev_id == 0x0448)
248 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800249 else if (dev_id == 0x0440)
250 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800251 else if ((dev_id & 0xfff) == 0x446)
252 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800253 else
254 codec_type = UNKNOWN;
255 return codec_type;
256};
257
Lydia Wangec7e7e42011-03-24 12:43:44 +0800258#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800259#define VIA_HP_EVENT 0x01
260#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200261#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800262
Joseph Chanc577b8a2006-11-29 15:29:40 +0100263enum {
264 VIA_CTL_WIDGET_VOL,
265 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800266 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100267};
268
Lydia Wangf5271102009-10-10 19:07:35 +0800269static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800270static int is_aa_path_mute(struct hda_codec *codec);
271
272static void vt1708_start_hp_work(struct via_spec *spec)
273{
274 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
275 return;
276 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200277 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800278 if (!delayed_work_pending(&spec->vt1708_hp_work))
279 schedule_delayed_work(&spec->vt1708_hp_work,
280 msecs_to_jiffies(100));
281}
282
283static void vt1708_stop_hp_work(struct via_spec *spec)
284{
285 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
286 return;
287 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
288 && !is_aa_path_mute(spec->codec))
289 return;
290 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200291 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100292 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800293}
Lydia Wangf5271102009-10-10 19:07:35 +0800294
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800295static void set_widgets_power_state(struct hda_codec *codec)
296{
297 struct via_spec *spec = codec->spec;
298 if (spec->set_widgets_power_state)
299 spec->set_widgets_power_state(codec);
300}
Lydia Wang25eaba22009-10-10 19:08:43 +0800301
Lydia Wangf5271102009-10-10 19:07:35 +0800302static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
303 struct snd_ctl_elem_value *ucontrol)
304{
305 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
306 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
307
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800308 set_widgets_power_state(codec);
Lydia Wangf5271102009-10-10 19:07:35 +0800309 analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800310 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
311 if (is_aa_path_mute(codec))
312 vt1708_start_hp_work(codec->spec);
313 else
314 vt1708_stop_hp_work(codec->spec);
315 }
Lydia Wangf5271102009-10-10 19:07:35 +0800316 return change;
317}
318
319/* modify .put = snd_hda_mixer_amp_switch_put */
320#define ANALOG_INPUT_MUTE \
321 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
322 .name = NULL, \
323 .index = 0, \
324 .info = snd_hda_mixer_amp_switch_info, \
325 .get = snd_hda_mixer_amp_switch_get, \
326 .put = analog_input_switch_put, \
327 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
328
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200329static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100330 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
331 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800332 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100333};
334
Lydia Wangab6734e2009-10-10 19:08:46 +0800335
Joseph Chanc577b8a2006-11-29 15:29:40 +0100336/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200337static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
338 const struct snd_kcontrol_new *tmpl,
339 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100340{
341 struct snd_kcontrol_new *knew;
342
Takashi Iwai603c4012008-07-30 15:01:44 +0200343 snd_array_init(&spec->kctls, sizeof(*knew), 32);
344 knew = snd_array_new(&spec->kctls);
345 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200346 return NULL;
347 *knew = *tmpl;
348 if (!name)
349 name = tmpl->name;
350 if (name) {
351 knew->name = kstrdup(name, GFP_KERNEL);
352 if (!knew->name)
353 return NULL;
354 }
355 return knew;
356}
357
358static int __via_add_control(struct via_spec *spec, int type, const char *name,
359 int idx, unsigned long val)
360{
361 struct snd_kcontrol_new *knew;
362
363 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
364 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100365 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200366 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100367 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100368 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100369 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100370 return 0;
371}
372
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200373#define via_add_control(spec, type, name, val) \
374 __via_add_control(spec, type, name, 0, val)
375
Takashi Iwai291c9e32011-06-17 16:15:26 +0200376#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100377
Takashi Iwai603c4012008-07-30 15:01:44 +0200378static void via_free_kctls(struct hda_codec *codec)
379{
380 struct via_spec *spec = codec->spec;
381
382 if (spec->kctls.list) {
383 struct snd_kcontrol_new *kctl = spec->kctls.list;
384 int i;
385 for (i = 0; i < spec->kctls.used; i++)
386 kfree(kctl[i].name);
387 }
388 snd_array_free(&spec->kctls);
389}
390
Joseph Chanc577b8a2006-11-29 15:29:40 +0100391/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800392static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200393 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100394{
395 char name[32];
396 int err;
397
398 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200399 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100400 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
401 if (err < 0)
402 return err;
403 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200404 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100405 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
406 if (err < 0)
407 return err;
408 return 0;
409}
410
Takashi Iwai5d417622011-06-20 11:32:27 +0200411/* return the index of the given widget nid as the source of mux;
412 * return -1 if not found;
413 * if num_conns is non-NULL, set the total number of connections
414 */
415static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux,
416 hda_nid_t nid, int *num_conns)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100417{
Takashi Iwai5d417622011-06-20 11:32:27 +0200418 hda_nid_t conn[HDA_MAX_NUM_INPUTS];
419 int i, nums;
420
421 nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
422 if (num_conns)
423 *num_conns = nums;
424 for (i = 0; i < nums; i++)
425 if (conn[i] == nid)
426 return i;
427 return -1;
428}
429
430#define get_connection_index(codec, mux, nid) \
431 __get_connection_index(codec, mux, nid, NULL)
432
433/* unmute input amp and select the specificed source */
434static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid,
435 hda_nid_t src, hda_nid_t mix)
436{
437 int idx, num_conns;
438
439 idx = __get_connection_index(codec, nid, src, &num_conns);
440 if (idx < 0)
441 return;
442
443 /* select the route explicitly when multiple connections exist */
444 if (num_conns > 1)
Lydia Wang377ff312009-10-10 19:08:55 +0800445 snd_hda_codec_write(codec, nid, 0,
Takashi Iwai5d417622011-06-20 11:32:27 +0200446 AC_VERB_SET_CONNECT_SEL, idx);
447 /* unmute if the input amp is present */
448 if (!(query_amp_caps(codec, nid, HDA_INPUT) &
449 (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)))
450 return;
451 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
452 AMP_IN_UNMUTE(idx));
453
454 /* unmute AA-path if present */
455 if (!mix)
456 return;
457 idx = __get_connection_index(codec, nid, mix, NULL);
458 if (idx >= 0)
459 snd_hda_codec_write(codec, nid, 0,
460 AC_VERB_SET_AMP_GAIN_MUTE,
461 AMP_IN_UNMUTE(idx));
462}
463
464/* set the given pin as output */
465static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
466 int pin_type)
467{
468 if (!pin)
469 return;
470 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
471 pin_type);
472 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
473 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200474 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100475}
476
Takashi Iwai5d417622011-06-20 11:32:27 +0200477static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin,
478 int pin_type, struct nid_path *path)
479{
480 struct via_spec *spec = codec->spec;
481 unsigned int caps;
482 hda_nid_t nid;
483 int i;
484
485 if (!pin)
486 return;
487
488 init_output_pin(codec, pin, pin_type);
489 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
490 if (caps & AC_AMPCAP_MUTE) {
491 unsigned int val;
492 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
493 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
494 AMP_OUT_MUTE | val);
495 }
496
497 /* initialize the output path */
498 nid = pin;
499 for (i = 0; i < path->depth; i++) {
500 unmute_and_select(codec, nid, path->idx[i], spec->aa_mix_nid);
501 nid = path->path[i];
502 if (query_amp_caps(codec, nid, HDA_OUTPUT) &
503 (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE))
504 snd_hda_codec_write(codec, nid, 0,
505 AC_VERB_SET_AMP_GAIN_MUTE,
506 AMP_OUT_UNMUTE);
507 }
508}
509
Joseph Chanc577b8a2006-11-29 15:29:40 +0100510
511static void via_auto_init_multi_out(struct hda_codec *codec)
512{
513 struct via_spec *spec = codec->spec;
514 int i;
515
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200516 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwai5d417622011-06-20 11:32:27 +0200517 via_auto_init_output(codec, spec->autocfg.line_out_pins[i],
518 PIN_OUT, &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100519}
520
521static void via_auto_init_hp_out(struct hda_codec *codec)
522{
523 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100524
Takashi Iwai5d417622011-06-20 11:32:27 +0200525 if (spec->hp_dac_nid)
526 via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
527 &spec->hp_path);
528 else
529 via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
530 &spec->hp_dep_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100531}
532
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200533static void via_auto_init_speaker_out(struct hda_codec *codec)
534{
535 struct via_spec *spec = codec->spec;
536
537 if (spec->autocfg.speaker_outs)
538 via_auto_init_output(codec, spec->autocfg.speaker_pins[0],
539 PIN_OUT, &spec->speaker_path);
540}
541
Takashi Iwaif4a78282011-06-17 18:46:48 +0200542static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200543
Joseph Chanc577b8a2006-11-29 15:29:40 +0100544static void via_auto_init_analog_input(struct hda_codec *codec)
545{
546 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200547 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200548 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200549 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200550 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100551
Takashi Iwai096a8852011-06-20 12:09:02 +0200552 /* init ADCs */
553 for (i = 0; i < spec->num_adc_nids; i++) {
554 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
555 AC_VERB_SET_AMP_GAIN_MUTE,
556 AMP_IN_UNMUTE(0));
557 }
558
559 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200560 for (i = 0; i < cfg->num_inputs; i++) {
561 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200562 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200563 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100564 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200565 ctl = PIN_VREF50;
566 else
567 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100568 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200569 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100570 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200571
572 /* init input-src */
573 for (i = 0; i < spec->num_adc_nids; i++) {
574 const struct hda_input_mux *imux = spec->input_mux;
575 if (!imux || !spec->mux_nids[i])
576 continue;
577 snd_hda_codec_write(codec, spec->mux_nids[i], 0,
578 AC_VERB_SET_CONNECT_SEL,
579 imux->items[spec->cur_mux[i]].index);
580 }
581
582 /* init aa-mixer */
583 if (!spec->aa_mix_nid)
584 return;
585 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
586 ARRAY_SIZE(conn));
587 for (i = 0; i < num_conns; i++) {
588 unsigned int caps = get_wcaps(codec, conn[i]);
589 if (get_wcaps_type(caps) == AC_WID_PIN)
590 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
591 AC_VERB_SET_AMP_GAIN_MUTE,
592 AMP_IN_MUTE(i));
593 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100594}
Lydia Wangf5271102009-10-10 19:07:35 +0800595
596static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
597 unsigned int *affected_parm)
598{
599 unsigned parm;
600 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
601 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
602 >> AC_DEFCFG_MISC_SHIFT
603 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800604 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200605 unsigned present = 0;
606
607 no_presence |= spec->no_pin_power_ctl;
608 if (!no_presence)
609 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200610 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800611 || ((no_presence || present)
612 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800613 *affected_parm = AC_PWRST_D0; /* if it's connected */
614 parm = AC_PWRST_D0;
615 } else
616 parm = AC_PWRST_D3;
617
618 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
619}
620
Takashi Iwai24088a52011-06-17 16:59:21 +0200621static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
622 struct snd_ctl_elem_info *uinfo)
623{
624 static const char * const texts[] = {
625 "Disabled", "Enabled"
626 };
627
628 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
629 uinfo->count = 1;
630 uinfo->value.enumerated.items = 2;
631 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
632 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
633 strcpy(uinfo->value.enumerated.name,
634 texts[uinfo->value.enumerated.item]);
635 return 0;
636}
637
638static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
639 struct snd_ctl_elem_value *ucontrol)
640{
641 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
642 struct via_spec *spec = codec->spec;
643 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
644 return 0;
645}
646
647static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
648 struct snd_ctl_elem_value *ucontrol)
649{
650 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
651 struct via_spec *spec = codec->spec;
652 unsigned int val = !ucontrol->value.enumerated.item[0];
653
654 if (val == spec->no_pin_power_ctl)
655 return 0;
656 spec->no_pin_power_ctl = val;
657 set_widgets_power_state(codec);
658 return 1;
659}
660
661static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
662 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
663 .name = "Dynamic Power-Control",
664 .info = via_pin_power_ctl_info,
665 .get = via_pin_power_ctl_get,
666 .put = via_pin_power_ctl_put,
667};
668
669
Joseph Chanc577b8a2006-11-29 15:29:40 +0100670/*
671 * input MUX handling
672 */
673static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
674 struct snd_ctl_elem_info *uinfo)
675{
676 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
677 struct via_spec *spec = codec->spec;
678 return snd_hda_input_mux_info(spec->input_mux, uinfo);
679}
680
681static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
682 struct snd_ctl_elem_value *ucontrol)
683{
684 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
685 struct via_spec *spec = codec->spec;
686 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
687
688 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
689 return 0;
690}
691
692static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
693 struct snd_ctl_elem_value *ucontrol)
694{
695 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
696 struct via_spec *spec = codec->spec;
697 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800698 int ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100699
Takashi Iwai337b9d02009-07-07 18:18:59 +0200700 if (!spec->mux_nids[adc_idx])
701 return -EINVAL;
Lydia Wanga80e6e32009-10-10 19:07:55 +0800702 /* switch to D0 beofre change index */
703 if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
704 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
705 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
706 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800707
708 ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
709 spec->mux_nids[adc_idx],
710 &spec->cur_mux[adc_idx]);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800711 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800712 set_widgets_power_state(codec);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800713
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800714 return ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100715}
716
Harald Welte0aa62ae2008-09-09 15:58:27 +0800717static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
718 struct snd_ctl_elem_info *uinfo)
719{
720 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
721 struct via_spec *spec = codec->spec;
722 return snd_hda_input_mux_info(spec->hp_mux, uinfo);
723}
724
725static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
726 struct snd_ctl_elem_value *ucontrol)
727{
728 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800729 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800730
Takashi Iwaiece8d042011-06-19 16:24:21 +0200731 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800732 return 0;
733}
734
Harald Welte0aa62ae2008-09-09 15:58:27 +0800735static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
736 struct snd_ctl_elem_value *ucontrol)
737{
738 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
739 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100740 hda_nid_t nid = kcontrol->private_value;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800741 unsigned int pinsel = ucontrol->value.enumerated.item[0];
Lydia Wangcdc17842009-10-10 19:07:47 +0800742 /* Get Independent Mode index of headphone pin widget */
743 spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
744 ? 1 : 0;
Takashi Iwaiece8d042011-06-19 16:24:21 +0200745 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800746
Lydia Wangce0e5a92011-03-22 16:22:37 +0800747 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800748 set_widgets_power_state(codec);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800749 return 0;
750}
751
Takashi Iwaiece8d042011-06-19 16:24:21 +0200752static const struct snd_kcontrol_new via_hp_mixer = {
753 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
754 .name = "Independent HP",
755 .info = via_independent_hp_info,
756 .get = via_independent_hp_get,
757 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800758};
759
Takashi Iwai3d83e572010-04-14 14:36:23 +0200760static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100761{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200762 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100763 struct snd_kcontrol_new *knew;
764 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100765
Takashi Iwaiece8d042011-06-19 16:24:21 +0200766 nid = spec->autocfg.hp_pins[0];
767 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200768 if (knew == NULL)
769 return -ENOMEM;
770
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100771 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
772 knew->private_value = nid;
773
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100774 return 0;
775}
776
Lydia Wang1564b282009-10-10 19:07:52 +0800777static void notify_aa_path_ctls(struct hda_codec *codec)
778{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200779 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800780 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800781
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200782 for (i = 0; i < spec->smart51_nums; i++) {
783 struct snd_kcontrol *ctl;
784 struct snd_ctl_elem_id id;
785 memset(&id, 0, sizeof(id));
786 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
787 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800788 ctl = snd_hda_find_mixer_ctl(codec, id.name);
789 if (ctl)
790 snd_ctl_notify(codec->bus->card,
791 SNDRV_CTL_EVENT_MASK_VALUE,
792 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800793 }
794}
795
796static void mute_aa_path(struct hda_codec *codec, int mute)
797{
798 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200799 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800800 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200801
Lydia Wang1564b282009-10-10 19:07:52 +0800802 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200803 for (i = 0; i < spec->smart51_nums; i++) {
804 if (spec->smart51_idxs[i] < 0)
805 continue;
806 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
807 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800808 HDA_AMP_MUTE, val);
809 }
810}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200811
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200812static bool is_smart51_candidate(struct hda_codec *codec, hda_nid_t pin)
Lydia Wang1564b282009-10-10 19:07:52 +0800813{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200814 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200815 const struct auto_pin_cfg *cfg = &spec->autocfg;
816 int i;
817
818 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaif4a78282011-06-17 18:46:48 +0200819 unsigned int defcfg;
820 if (pin != cfg->inputs[i].pin)
821 continue;
822 if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
823 return false;
824 defcfg = snd_hda_codec_get_pincfg(codec, pin);
825 if (snd_hda_get_input_pin_attr(defcfg) < INPUT_PIN_ATTR_NORMAL)
826 return false;
827 return true;
Lydia Wang1564b282009-10-10 19:07:52 +0800828 }
Takashi Iwaif4a78282011-06-17 18:46:48 +0200829 return false;
Lydia Wang1564b282009-10-10 19:07:52 +0800830}
831
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200832static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
833{
834 struct via_spec *spec = codec->spec;
835 int i;
836
837 for (i = 0; i < spec->smart51_nums; i++)
838 if (spec->smart51_pins[i] == pin)
839 return true;
840 return false;
841}
842
Lydia Wang1564b282009-10-10 19:07:52 +0800843static int via_smart51_info(struct snd_kcontrol *kcontrol,
844 struct snd_ctl_elem_info *uinfo)
845{
846 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
847 uinfo->count = 1;
848 uinfo->value.integer.min = 0;
849 uinfo->value.integer.max = 1;
850 return 0;
851}
852
853static int via_smart51_get(struct snd_kcontrol *kcontrol,
854 struct snd_ctl_elem_value *ucontrol)
855{
856 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
857 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800858 int on = 1;
859 int i;
860
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200861 for (i = 0; i < spec->smart51_nums; i++) {
862 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwaif4a78282011-06-17 18:46:48 +0200863 unsigned int ctl;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200864 ctl = snd_hda_codec_read(codec, nid, 0,
865 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200866 if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
867 on = 0;
Lydia Wang1564b282009-10-10 19:07:52 +0800868 }
869 *ucontrol->value.integer.value = on;
870 return 0;
871}
872
873static int via_smart51_put(struct snd_kcontrol *kcontrol,
874 struct snd_ctl_elem_value *ucontrol)
875{
876 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
877 struct via_spec *spec = codec->spec;
878 int out_in = *ucontrol->value.integer.value
879 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800880 int i;
881
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200882 for (i = 0; i < spec->smart51_nums; i++) {
883 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200884 unsigned int parm;
885
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200886 parm = snd_hda_codec_read(codec, nid, 0,
887 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
888 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
889 parm |= out_in;
890 snd_hda_codec_write(codec, nid, 0,
891 AC_VERB_SET_PIN_WIDGET_CONTROL,
892 parm);
893 if (out_in == AC_PINCTL_OUT_EN) {
894 mute_aa_path(codec, 1);
895 notify_aa_path_ctls(codec);
896 }
Lydia Wang1564b282009-10-10 19:07:52 +0800897 }
898 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800899 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800900 return 1;
901}
902
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200903static const struct snd_kcontrol_new via_smart51_mixer = {
904 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
905 .name = "Smart 5.1",
906 .count = 1,
907 .info = via_smart51_info,
908 .get = via_smart51_get,
909 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800910};
911
Takashi Iwaif4a78282011-06-17 18:46:48 +0200912static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100913{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200914 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100915
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200916 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800917 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200918 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100919 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100920 return 0;
921}
922
Lydia Wangf5271102009-10-10 19:07:35 +0800923/* check AA path's mute statue */
924static int is_aa_path_mute(struct hda_codec *codec)
925{
926 int mute = 1;
Lydia Wangf5271102009-10-10 19:07:35 +0800927 int start_idx;
928 int end_idx;
929 int i;
930 struct via_spec *spec = codec->spec;
931 /* get nid of MW0 and start & end index */
932 switch (spec->codec_type) {
933 case VT1708B_8CH:
934 case VT1708B_4CH:
935 case VT1708S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800936 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800937 start_idx = 2;
938 end_idx = 4;
939 break;
940 case VT1702:
Lydia Wangf5271102009-10-10 19:07:35 +0800941 start_idx = 1;
942 end_idx = 3;
943 break;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800944 case VT1718S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800945 start_idx = 1;
946 end_idx = 3;
947 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800948 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800949 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800950 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800951 start_idx = 0;
952 end_idx = 2;
953 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800954 default:
955 return 0;
956 }
957 /* check AA path's mute status */
958 for (i = start_idx; i <= end_idx; i++) {
959 unsigned int con_list = snd_hda_codec_read(
Takashi Iwai620e2b22011-06-17 17:19:19 +0200960 codec, spec->aa_mix_nid, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
Lydia Wangf5271102009-10-10 19:07:35 +0800961 int shift = 8 * (i % 4);
962 hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
963 unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
964 if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
965 /* check mute status while the pin is connected */
Takashi Iwai620e2b22011-06-17 17:19:19 +0200966 int mute_l = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 0,
Lydia Wangf5271102009-10-10 19:07:35 +0800967 HDA_INPUT, i) >> 7;
Takashi Iwai620e2b22011-06-17 17:19:19 +0200968 int mute_r = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 1,
Lydia Wangf5271102009-10-10 19:07:35 +0800969 HDA_INPUT, i) >> 7;
970 if (!mute_l || !mute_r) {
971 mute = 0;
972 break;
973 }
974 }
975 }
976 return mute;
977}
978
979/* enter/exit analog low-current mode */
980static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
981{
982 struct via_spec *spec = codec->spec;
983 static int saved_stream_idle = 1; /* saved stream idle status */
984 int enable = is_aa_path_mute(codec);
985 unsigned int verb = 0;
986 unsigned int parm = 0;
987
988 if (stream_idle == -1) /* stream status did not change */
989 enable = enable && saved_stream_idle;
990 else {
991 enable = enable && stream_idle;
992 saved_stream_idle = stream_idle;
993 }
994
995 /* decide low current mode's verb & parameter */
996 switch (spec->codec_type) {
997 case VT1708B_8CH:
998 case VT1708B_4CH:
999 verb = 0xf70;
1000 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1001 break;
1002 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001003 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001004 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001005 verb = 0xf73;
1006 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1007 break;
1008 case VT1702:
1009 verb = 0xf73;
1010 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1011 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001012 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001013 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +08001014 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +08001015 verb = 0xf93;
1016 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1017 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001018 default:
1019 return; /* other codecs are not supported */
1020 }
1021 /* send verb */
1022 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1023}
1024
Joseph Chanc577b8a2006-11-29 15:29:40 +01001025/*
1026 * generic initialization of ADC, input mixers and output mixers
1027 */
Takashi Iwai096a8852011-06-20 12:09:02 +02001028static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001029 /* power down jack detect function */
1030 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001031 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001032};
1033
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001034static void substream_set_idle(struct hda_codec *codec,
1035 struct snd_pcm_substream *substream)
1036{
1037 int idle = substream->pstr->substream_opened == 1
1038 && substream->ref_count == 0;
1039 analog_low_current_mode(codec, idle);
1040}
1041
Takashi Iwaiece8d042011-06-19 16:24:21 +02001042static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001043 struct hda_codec *codec,
1044 struct snd_pcm_substream *substream)
1045{
1046 struct via_spec *spec = codec->spec;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001047
1048 if (!spec->hp_independent_mode)
1049 spec->multiout.hp_nid = spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001050 substream_set_idle(codec, substream);
Takashi Iwai9a081602008-02-12 18:37:26 +01001051 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1052 hinfo);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001053}
1054
Takashi Iwaiece8d042011-06-19 16:24:21 +02001055static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001056 struct hda_codec *codec,
1057 struct snd_pcm_substream *substream)
1058{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001059 struct via_spec *spec = codec->spec;
1060
1061 spec->multiout.hp_nid = 0;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001062 substream_set_idle(codec, substream);
Takashi Iwai9af74212011-06-18 16:17:45 +02001063 return 0;
1064}
1065
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001066static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1067 struct hda_codec *codec,
1068 struct snd_pcm_substream *substream)
1069{
1070 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001071
Takashi Iwaiece8d042011-06-19 16:24:21 +02001072 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001073 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001074 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1075 return -EBUSY;
1076 substream_set_idle(codec, substream);
1077 return 0;
1078}
1079
1080static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1081 struct hda_codec *codec,
1082 struct snd_pcm_substream *substream)
1083{
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001084 substream_set_idle(codec, substream);
1085 return 0;
1086}
1087
1088static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1089 struct hda_codec *codec,
1090 unsigned int stream_tag,
1091 unsigned int format,
1092 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001093{
1094 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001095
Takashi Iwaiece8d042011-06-19 16:24:21 +02001096 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1097 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001098 vt1708_start_hp_work(spec);
1099 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001100}
1101
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001102static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1103 struct hda_codec *codec,
1104 unsigned int stream_tag,
1105 unsigned int format,
1106 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001107{
1108 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001109
Takashi Iwaiece8d042011-06-19 16:24:21 +02001110 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1111 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001112 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001113 return 0;
1114}
1115
1116static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1117 struct hda_codec *codec,
1118 struct snd_pcm_substream *substream)
1119{
1120 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001121
Takashi Iwaiece8d042011-06-19 16:24:21 +02001122 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001123 vt1708_stop_hp_work(spec);
1124 return 0;
1125}
1126
1127static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1128 struct hda_codec *codec,
1129 struct snd_pcm_substream *substream)
1130{
1131 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001132
Takashi Iwaiece8d042011-06-19 16:24:21 +02001133 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001134 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001135 return 0;
1136}
1137
Joseph Chanc577b8a2006-11-29 15:29:40 +01001138/*
1139 * Digital out
1140 */
1141static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1142 struct hda_codec *codec,
1143 struct snd_pcm_substream *substream)
1144{
1145 struct via_spec *spec = codec->spec;
1146 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1147}
1148
1149static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1150 struct hda_codec *codec,
1151 struct snd_pcm_substream *substream)
1152{
1153 struct via_spec *spec = codec->spec;
1154 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1155}
1156
Harald Welte5691ec72008-09-15 22:42:26 +08001157static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001158 struct hda_codec *codec,
1159 unsigned int stream_tag,
1160 unsigned int format,
1161 struct snd_pcm_substream *substream)
1162{
1163 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001164 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1165 stream_tag, format, substream);
1166}
Harald Welte5691ec72008-09-15 22:42:26 +08001167
Takashi Iwai9da29272009-05-07 16:31:14 +02001168static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1169 struct hda_codec *codec,
1170 struct snd_pcm_substream *substream)
1171{
1172 struct via_spec *spec = codec->spec;
1173 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001174 return 0;
1175}
1176
Joseph Chanc577b8a2006-11-29 15:29:40 +01001177/*
1178 * Analog capture
1179 */
1180static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1181 struct hda_codec *codec,
1182 unsigned int stream_tag,
1183 unsigned int format,
1184 struct snd_pcm_substream *substream)
1185{
1186 struct via_spec *spec = codec->spec;
1187
1188 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1189 stream_tag, 0, format);
1190 return 0;
1191}
1192
1193static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1194 struct hda_codec *codec,
1195 struct snd_pcm_substream *substream)
1196{
1197 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001198 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001199 return 0;
1200}
1201
Takashi Iwai9af74212011-06-18 16:17:45 +02001202static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001203 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001204 .channels_min = 2,
1205 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001206 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001207 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001208 .open = via_playback_multi_pcm_open,
1209 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001210 .prepare = via_playback_multi_pcm_prepare,
1211 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001212 },
1213};
1214
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001215static const struct hda_pcm_stream via_pcm_hp_playback = {
1216 .substreams = 1,
1217 .channels_min = 2,
1218 .channels_max = 2,
1219 /* NID is set in via_build_pcms */
1220 .ops = {
1221 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001222 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001223 .prepare = via_playback_hp_pcm_prepare,
1224 .cleanup = via_playback_hp_pcm_cleanup
1225 },
1226};
1227
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001228static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001229 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001230 .channels_min = 2,
1231 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001232 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001233 /* We got noisy outputs on the right channel on VT1708 when
1234 * 24bit samples are used. Until any workaround is found,
1235 * disable the 24bit format, so far.
1236 */
1237 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1238 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001239 .open = via_playback_multi_pcm_open,
1240 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001241 .prepare = via_playback_multi_pcm_prepare,
1242 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001243 },
1244};
1245
Takashi Iwai9af74212011-06-18 16:17:45 +02001246static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001247 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001248 .channels_min = 2,
1249 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001250 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001251 .ops = {
1252 .prepare = via_capture_pcm_prepare,
1253 .cleanup = via_capture_pcm_cleanup
1254 },
1255};
1256
Takashi Iwai9af74212011-06-18 16:17:45 +02001257static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001258 .substreams = 1,
1259 .channels_min = 2,
1260 .channels_max = 2,
1261 /* NID is set in via_build_pcms */
1262 .ops = {
1263 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001264 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001265 .prepare = via_dig_playback_pcm_prepare,
1266 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001267 },
1268};
1269
Takashi Iwai9af74212011-06-18 16:17:45 +02001270static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001271 .substreams = 1,
1272 .channels_min = 2,
1273 .channels_max = 2,
1274};
1275
Takashi Iwai370bafb2011-06-20 12:47:45 +02001276/*
1277 * slave controls for virtual master
1278 */
1279static const char * const via_slave_vols[] = {
1280 "Front Playback Volume",
1281 "Surround Playback Volume",
1282 "Center Playback Volume",
1283 "LFE Playback Volume",
1284 "Side Playback Volume",
1285 "Headphone Playback Volume",
1286 "Speaker Playback Volume",
1287 NULL,
1288};
1289
1290static const char * const via_slave_sws[] = {
1291 "Front Playback Switch",
1292 "Surround Playback Switch",
1293 "Center Playback Switch",
1294 "LFE Playback Switch",
1295 "Side Playback Switch",
1296 "Headphone Playback Switch",
1297 "Speaker Playback Switch",
1298 NULL,
1299};
1300
Joseph Chanc577b8a2006-11-29 15:29:40 +01001301static int via_build_controls(struct hda_codec *codec)
1302{
1303 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001304 struct snd_kcontrol *kctl;
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001305 const struct snd_kcontrol_new *knew;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001306 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001307
Takashi Iwai24088a52011-06-17 16:59:21 +02001308 if (spec->set_widgets_power_state)
1309 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1310 return -ENOMEM;
1311
Joseph Chanc577b8a2006-11-29 15:29:40 +01001312 for (i = 0; i < spec->num_mixers; i++) {
1313 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1314 if (err < 0)
1315 return err;
1316 }
1317
1318 if (spec->multiout.dig_out_nid) {
1319 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001320 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001321 spec->multiout.dig_out_nid);
1322 if (err < 0)
1323 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001324 err = snd_hda_create_spdif_share_sw(codec,
1325 &spec->multiout);
1326 if (err < 0)
1327 return err;
1328 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001329 }
1330 if (spec->dig_in_nid) {
1331 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1332 if (err < 0)
1333 return err;
1334 }
Lydia Wang17314372009-10-10 19:07:37 +08001335
Takashi Iwai370bafb2011-06-20 12:47:45 +02001336 /* if we have no master control, let's create it */
1337 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1338 unsigned int vmaster_tlv[4];
1339 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1340 HDA_OUTPUT, vmaster_tlv);
1341 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1342 vmaster_tlv, via_slave_vols);
1343 if (err < 0)
1344 return err;
1345 }
1346 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1347 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1348 NULL, via_slave_sws);
1349 if (err < 0)
1350 return err;
1351 }
1352
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001353 /* assign Capture Source enums to NID */
1354 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1355 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001356 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001357 if (err < 0)
1358 return err;
1359 }
1360
1361 /* other nid->control mapping */
1362 for (i = 0; i < spec->num_mixers; i++) {
1363 for (knew = spec->mixers[i]; knew->name; knew++) {
1364 if (knew->iface != NID_MAPPING)
1365 continue;
1366 kctl = snd_hda_find_mixer_ctl(codec, knew->name);
1367 if (kctl == NULL)
1368 continue;
1369 err = snd_hda_add_nid(codec, kctl, 0,
1370 knew->subdevice);
1371 }
1372 }
1373
Lydia Wang17314372009-10-10 19:07:37 +08001374 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001375 set_widgets_power_state(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001376 analog_low_current_mode(codec, 1);
1377
Takashi Iwai603c4012008-07-30 15:01:44 +02001378 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001379 return 0;
1380}
1381
1382static int via_build_pcms(struct hda_codec *codec)
1383{
1384 struct via_spec *spec = codec->spec;
1385 struct hda_pcm *info = spec->pcm_rec;
1386
1387 codec->num_pcms = 1;
1388 codec->pcm_info = info;
1389
Takashi Iwai82673bc2011-06-17 16:24:21 +02001390 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1391 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001392 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001393
1394 if (!spec->stream_analog_playback)
1395 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001396 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001397 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001398 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1399 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001400 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1401 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001402
1403 if (!spec->stream_analog_capture)
1404 spec->stream_analog_capture = &via_pcm_analog_capture;
1405 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1406 *spec->stream_analog_capture;
1407 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1408 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1409 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001410
1411 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1412 codec->num_pcms++;
1413 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001414 snprintf(spec->stream_name_digital,
1415 sizeof(spec->stream_name_digital),
1416 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001417 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001418 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001419 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001420 if (!spec->stream_digital_playback)
1421 spec->stream_digital_playback =
1422 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001423 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001424 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001425 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1426 spec->multiout.dig_out_nid;
1427 }
1428 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001429 if (!spec->stream_digital_capture)
1430 spec->stream_digital_capture =
1431 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001432 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001433 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001434 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1435 spec->dig_in_nid;
1436 }
1437 }
1438
Takashi Iwaiece8d042011-06-19 16:24:21 +02001439 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001440 codec->num_pcms++;
1441 info++;
1442 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1443 "%s HP", codec->chip_name);
1444 info->name = spec->stream_name_hp;
1445 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1446 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001447 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001448 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001449 return 0;
1450}
1451
1452static void via_free(struct hda_codec *codec)
1453{
1454 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001455
1456 if (!spec)
1457 return;
1458
Takashi Iwai603c4012008-07-30 15:01:44 +02001459 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001460 vt1708_stop_hp_work(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001461 kfree(codec->spec);
1462}
1463
Takashi Iwai64be2852011-06-17 16:51:39 +02001464/* mute/unmute outputs */
1465static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1466 hda_nid_t *pins, bool mute)
1467{
1468 int i;
1469 for (i = 0; i < num_pins; i++)
1470 snd_hda_codec_write(codec, pins[i], 0,
1471 AC_VERB_SET_PIN_WIDGET_CONTROL,
1472 mute ? 0 : PIN_OUT);
1473}
1474
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001475/* mute internal speaker if line-out is plugged */
1476static void via_line_automute(struct hda_codec *codec, int present)
1477{
1478 struct via_spec *spec = codec->spec;
1479
1480 if (!spec->autocfg.speaker_outs)
1481 return;
1482 if (!present)
1483 present = snd_hda_jack_detect(codec,
1484 spec->autocfg.line_out_pins[0]);
1485 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1486 spec->autocfg.speaker_pins,
1487 present);
1488}
1489
Harald Welte69e52a82008-09-09 15:57:32 +08001490/* mute internal speaker if HP is plugged */
1491static void via_hp_automute(struct hda_codec *codec)
1492{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001493 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001494 struct via_spec *spec = codec->spec;
1495
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001496 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
1497 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai64be2852011-06-17 16:51:39 +02001498 toggle_output_mutes(codec, spec->autocfg.line_outs,
1499 spec->autocfg.line_out_pins,
1500 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001501 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001502 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001503}
1504
Harald Welte69e52a82008-09-09 15:57:32 +08001505static void via_gpio_control(struct hda_codec *codec)
1506{
1507 unsigned int gpio_data;
1508 unsigned int vol_counter;
1509 unsigned int vol;
1510 unsigned int master_vol;
1511
1512 struct via_spec *spec = codec->spec;
1513
1514 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1515 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1516
1517 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1518 0xF84, 0) & 0x3F0000) >> 16;
1519
1520 vol = vol_counter & 0x1F;
1521 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1522 AC_VERB_GET_AMP_GAIN_MUTE,
1523 AC_AMP_GET_INPUT);
1524
1525 if (gpio_data == 0x02) {
1526 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001527 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1528 AC_VERB_SET_PIN_WIDGET_CONTROL,
1529 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001530 if (vol_counter & 0x20) {
1531 /* decrease volume */
1532 if (vol > master_vol)
1533 vol = master_vol;
1534 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1535 0, HDA_AMP_VOLMASK,
1536 master_vol-vol);
1537 } else {
1538 /* increase volume */
1539 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1540 HDA_AMP_VOLMASK,
1541 ((master_vol+vol) > 0x2A) ? 0x2A :
1542 (master_vol+vol));
1543 }
1544 } else if (!(gpio_data & 0x02)) {
1545 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001546 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1547 AC_VERB_SET_PIN_WIDGET_CONTROL,
1548 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001549 }
1550}
1551
1552/* unsolicited event for jack sensing */
1553static void via_unsol_event(struct hda_codec *codec,
1554 unsigned int res)
1555{
1556 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001557
Lydia Wanga34df192009-10-10 19:08:01 +08001558 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001559 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001560
1561 res &= ~VIA_JACK_EVENT;
1562
1563 if (res == VIA_HP_EVENT)
1564 via_hp_automute(codec);
1565 else if (res == VIA_GPIO_EVENT)
1566 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001567 else if (res == VIA_LINE_EVENT)
1568 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001569}
1570
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001571#ifdef SND_HDA_NEEDS_RESUME
1572static int via_suspend(struct hda_codec *codec, pm_message_t state)
1573{
1574 struct via_spec *spec = codec->spec;
1575 vt1708_stop_hp_work(spec);
1576 return 0;
1577}
1578#endif
1579
Takashi Iwaicb53c622007-08-10 17:21:45 +02001580#ifdef CONFIG_SND_HDA_POWER_SAVE
1581static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1582{
1583 struct via_spec *spec = codec->spec;
1584 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1585}
1586#endif
1587
Joseph Chanc577b8a2006-11-29 15:29:40 +01001588/*
1589 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001590
1591static int via_init(struct hda_codec *codec);
1592
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001593static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001594 .build_controls = via_build_controls,
1595 .build_pcms = via_build_pcms,
1596 .init = via_init,
1597 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001598 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001599#ifdef SND_HDA_NEEDS_RESUME
1600 .suspend = via_suspend,
1601#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001602#ifdef CONFIG_SND_HDA_POWER_SAVE
1603 .check_power_status = via_check_power_status,
1604#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001605};
1606
Takashi Iwai4a796162011-06-17 17:53:38 +02001607static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001608{
Takashi Iwai4a796162011-06-17 17:53:38 +02001609 struct via_spec *spec = codec->spec;
1610 int i;
1611
1612 for (i = 0; i < spec->multiout.num_dacs; i++) {
1613 if (spec->multiout.dac_nids[i] == dac)
1614 return false;
1615 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001616 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001617 return false;
1618 return true;
1619}
1620
1621static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1622 hda_nid_t target_dac, struct nid_path *path,
1623 int depth, int wid_type)
1624{
1625 hda_nid_t conn[8];
1626 int i, nums;
1627
1628 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1629 for (i = 0; i < nums; i++) {
1630 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1631 continue;
1632 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1633 path->path[depth] = conn[i];
1634 path->idx[depth] = i;
1635 path->depth = ++depth;
1636 return true;
1637 }
1638 }
1639 if (depth > 4)
1640 return false;
1641 for (i = 0; i < nums; i++) {
1642 unsigned int type;
1643 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1644 if (type == AC_WID_AUD_OUT ||
1645 (wid_type != -1 && type != wid_type))
1646 continue;
1647 if (parse_output_path(codec, conn[i], target_dac,
1648 path, depth + 1, AC_WID_AUD_SEL)) {
1649 path->path[depth] = conn[i];
1650 path->idx[depth] = i;
1651 return true;
1652 }
1653 }
1654 return false;
1655}
1656
1657static int via_auto_fill_dac_nids(struct hda_codec *codec)
1658{
1659 struct via_spec *spec = codec->spec;
1660 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001661 int i;
1662 hda_nid_t nid;
1663
Joseph Chanc577b8a2006-11-29 15:29:40 +01001664 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001665 spec->multiout.num_dacs = cfg->line_outs;
1666 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001667 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001668 if (!nid)
1669 continue;
1670 if (parse_output_path(codec, nid, 0, &spec->out_path[i], 0, -1))
1671 spec->private_dac_nids[i] =
1672 spec->out_path[i].path[spec->out_path[i].depth - 1];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001673 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001674 return 0;
1675}
1676
Takashi Iwai4a796162011-06-17 17:53:38 +02001677static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
1678 hda_nid_t pin, hda_nid_t dac, int chs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001679{
Takashi Iwai4a796162011-06-17 17:53:38 +02001680 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001681 char name[32];
Takashi Iwai4a796162011-06-17 17:53:38 +02001682 hda_nid_t nid;
1683 int err;
1684
1685 if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
1686 nid = dac;
1687 else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
1688 nid = pin;
1689 else
1690 nid = 0;
1691 if (nid) {
1692 sprintf(name, "%s Playback Volume", pfx);
1693 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1694 HDA_COMPOSE_AMP_VAL(dac, chs, 0, HDA_OUTPUT));
1695 if (err < 0)
1696 return err;
1697 }
1698
1699 if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE)
1700 nid = dac;
1701 else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE)
1702 nid = pin;
1703 else
1704 nid = 0;
1705 if (nid) {
1706 sprintf(name, "%s Playback Switch", pfx);
1707 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1708 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1709 if (err < 0)
1710 return err;
1711 }
1712 return 0;
1713}
1714
Takashi Iwaif4a78282011-06-17 18:46:48 +02001715static void mangle_smart51(struct hda_codec *codec)
1716{
1717 struct via_spec *spec = codec->spec;
1718 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001719 int i, nums = 0;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001720
1721 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001722 if (is_smart51_candidate(codec, cfg->inputs[i].pin))
1723 nums++;
1724 }
1725 if (cfg->line_outs + nums < 3)
1726 return;
1727 for (i = 0; i < cfg->num_inputs; i++) {
1728 if (!is_smart51_candidate(codec, cfg->inputs[i].pin))
Takashi Iwaif4a78282011-06-17 18:46:48 +02001729 continue;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001730 spec->smart51_pins[spec->smart51_nums++] = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001731 cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin;
1732 if (cfg->line_outs == 3)
1733 break;
1734 }
1735}
1736
Takashi Iwai4a796162011-06-17 17:53:38 +02001737/* add playback controls from the parsed DAC table */
1738static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1739{
1740 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001741 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001742 static const char * const chname[4] = {
1743 "Front", "Surround", "C/LFE", "Side"
1744 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001745 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001746 int old_line_outs;
1747
1748 /* check smart51 */
1749 old_line_outs = cfg->line_outs;
1750 if (cfg->line_outs == 1)
1751 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001752
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001753 err = via_auto_fill_dac_nids(codec);
1754 if (err < 0)
1755 return err;
1756
Takashi Iwai4a796162011-06-17 17:53:38 +02001757 for (i = 0; i < cfg->line_outs; i++) {
1758 hda_nid_t pin, dac;
1759 pin = cfg->line_out_pins[i];
1760 dac = spec->multiout.dac_nids[i];
1761 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001762 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001763 if (i == HDA_CLFE) {
Takashi Iwai4a796162011-06-17 17:53:38 +02001764 err = create_ch_ctls(codec, "Center", pin, dac, 1);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001765 if (err < 0)
1766 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02001767 err = create_ch_ctls(codec, "LFE", pin, dac, 2);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001768 if (err < 0)
1769 return err;
1770 } else {
Takashi Iwai4a796162011-06-17 17:53:38 +02001771 err = create_ch_ctls(codec, chname[i], pin, dac, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001772 if (err < 0)
1773 return err;
1774 }
1775 }
1776
Takashi Iwai4a796162011-06-17 17:53:38 +02001777 idx = get_connection_index(codec, spec->aa_mix_nid,
1778 spec->multiout.dac_nids[0]);
1779 if (idx >= 0) {
1780 /* add control to mixer */
1781 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1782 "PCM Playback Volume",
1783 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1784 idx, HDA_INPUT));
1785 if (err < 0)
1786 return err;
1787 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1788 "PCM Playback Switch",
1789 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1790 idx, HDA_INPUT));
1791 if (err < 0)
1792 return err;
1793 }
1794
Takashi Iwaif4a78282011-06-17 18:46:48 +02001795 cfg->line_outs = old_line_outs;
1796
Joseph Chanc577b8a2006-11-29 15:29:40 +01001797 return 0;
1798}
1799
Harald Welte0aa62ae2008-09-09 15:58:27 +08001800static void create_hp_imux(struct via_spec *spec)
1801{
1802 int i;
1803 struct hda_input_mux *imux = &spec->private_imux[1];
Takashi Iwaiea734962011-01-17 11:29:34 +01001804 static const char * const texts[] = { "OFF", "ON", NULL};
Harald Welte0aa62ae2008-09-09 15:58:27 +08001805
1806 /* for hp mode select */
Takashi Iwai10a20af2010-09-09 16:28:02 +02001807 for (i = 0; texts[i]; i++)
1808 snd_hda_add_imux_item(imux, texts[i], i, NULL);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001809
1810 spec->hp_mux = &spec->private_imux[1];
1811}
1812
Takashi Iwai4a796162011-06-17 17:53:38 +02001813static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001814{
Takashi Iwai4a796162011-06-17 17:53:38 +02001815 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001816 int err;
1817
1818 if (!pin)
1819 return 0;
1820
Takashi Iwai4a796162011-06-17 17:53:38 +02001821 if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001822 spec->hp_dac_nid = spec->hp_path.path[spec->hp_path.depth - 1];
Takashi Iwai4a796162011-06-17 17:53:38 +02001823 spec->hp_independent_mode_index =
1824 spec->hp_path.idx[spec->hp_path.depth - 1];
1825 create_hp_imux(spec);
1826 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001827
Takashi Iwaiece8d042011-06-19 16:24:21 +02001828 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
1829 &spec->hp_dep_path, 0, -1) &&
1830 !spec->hp_dac_nid)
1831 return 0;
1832
1833
1834 err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001835 if (err < 0)
1836 return err;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001837
Joseph Chanc577b8a2006-11-29 15:29:40 +01001838 return 0;
1839}
1840
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001841static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1842{
1843 struct via_spec *spec = codec->spec;
1844 hda_nid_t pin, dac;
1845
1846 pin = spec->autocfg.speaker_pins[0];
1847 if (!spec->autocfg.speaker_outs || !pin)
1848 return 0;
1849
1850 if (parse_output_path(codec, pin, 0, &spec->speaker_path, 0, -1)) {
1851 dac = spec->speaker_path.path[spec->speaker_path.depth - 1];
1852 spec->multiout.extra_out_nid[0] = dac;
1853 return create_ch_ctls(codec, "Speaker", pin, dac, 3);
1854 }
1855 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
1856 &spec->speaker_path, 0, -1))
1857 return create_ch_ctls(codec, "Headphone", pin, 0, 3);
1858
1859 return 0;
1860}
1861
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001862/* look for ADCs */
1863static int via_fill_adcs(struct hda_codec *codec)
1864{
1865 struct via_spec *spec = codec->spec;
1866 hda_nid_t nid = codec->start_nid;
1867 int i;
1868
1869 for (i = 0; i < codec->num_nodes; i++, nid++) {
1870 unsigned int wcaps = get_wcaps(codec, nid);
1871 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1872 continue;
1873 if (wcaps & AC_WCAP_DIGITAL)
1874 continue;
1875 if (!(wcaps & AC_WCAP_CONN_LIST))
1876 continue;
1877 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1878 return -ENOMEM;
1879 spec->adc_nids[spec->num_adc_nids++] = nid;
1880 }
1881 return 0;
1882}
1883
1884static int get_mux_nids(struct hda_codec *codec);
1885
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001886static const struct snd_kcontrol_new via_input_src_ctl = {
1887 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1888 /* The multiple "Capture Source" controls confuse alsamixer
1889 * So call somewhat different..
1890 */
1891 /* .name = "Capture Source", */
1892 .name = "Input Source",
1893 .info = via_mux_enum_info,
1894 .get = via_mux_enum_get,
1895 .put = via_mux_enum_put,
1896};
1897
Joseph Chanc577b8a2006-11-29 15:29:40 +01001898/* create playback/capture controls for input pins */
Takashi Iwai620e2b22011-06-17 17:19:19 +02001899static int via_auto_create_analog_input_ctls(struct hda_codec *codec,
1900 const struct auto_pin_cfg *cfg)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001901{
Takashi Iwai10a20af2010-09-09 16:28:02 +02001902 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001903 struct hda_input_mux *imux = &spec->private_imux[0];
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001904 int i, j, err, idx, idx2, type, type_idx = 0;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001905 hda_nid_t cap_nid;
1906 hda_nid_t pin_idxs[8];
1907 int num_idxs;
1908
1909 err = via_fill_adcs(codec);
1910 if (err < 0)
1911 return err;
1912 err = get_mux_nids(codec);
1913 if (err < 0)
1914 return err;
1915 cap_nid = spec->mux_nids[0];
1916
1917 num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
1918 ARRAY_SIZE(pin_idxs));
1919 if (num_idxs <= 0)
1920 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001921
1922 /* for internal loopback recording select */
Takashi Iwaif3268512010-08-30 11:00:19 +02001923 for (idx = 0; idx < num_idxs; idx++) {
Takashi Iwai620e2b22011-06-17 17:19:19 +02001924 if (pin_idxs[idx] == spec->aa_mix_nid) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001925 snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
Takashi Iwaif3268512010-08-30 11:00:19 +02001926 break;
1927 }
1928 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001929
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001930 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001931 const char *label;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001932 type = cfg->inputs[i].type;
Takashi Iwaif3268512010-08-30 11:00:19 +02001933 for (idx = 0; idx < num_idxs; idx++)
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001934 if (pin_idxs[idx] == cfg->inputs[i].pin)
Takashi Iwaif3268512010-08-30 11:00:19 +02001935 break;
1936 if (idx >= num_idxs)
1937 continue;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001938 if (i > 0 && type == cfg->inputs[i - 1].type)
1939 type_idx++;
1940 else
1941 type_idx = 0;
Takashi Iwai10a20af2010-09-09 16:28:02 +02001942 label = hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwai620e2b22011-06-17 17:19:19 +02001943 idx2 = get_connection_index(codec, spec->aa_mix_nid,
1944 pin_idxs[idx]);
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001945 if (idx2 >= 0)
Lydia Wang16922282011-03-22 16:24:10 +08001946 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwai620e2b22011-06-17 17:19:19 +02001947 idx2, spec->aa_mix_nid);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001948 if (err < 0)
1949 return err;
Takashi Iwai10a20af2010-09-09 16:28:02 +02001950 snd_hda_add_imux_item(imux, label, idx, NULL);
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001951
1952 /* remember the label for smart51 control */
1953 for (j = 0; j < spec->smart51_nums; j++) {
1954 if (spec->smart51_pins[j] == cfg->inputs[i].pin) {
1955 spec->smart51_idxs[j] = idx;
1956 spec->smart51_labels[j] = label;
1957 break;
1958 }
1959 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001960 }
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001961
1962 /* create capture mixer elements */
1963 for (i = 0; i < spec->num_adc_nids; i++) {
1964 hda_nid_t adc = spec->adc_nids[i];
1965 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
1966 "Capture Volume", i,
1967 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1968 HDA_INPUT));
1969 if (err < 0)
1970 return err;
1971 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1972 "Capture Switch", i,
1973 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1974 HDA_INPUT));
1975 if (err < 0)
1976 return err;
1977 }
1978
1979 /* input-source control */
1980 for (i = 0; i < spec->num_adc_nids; i++)
1981 if (!spec->mux_nids[i])
1982 break;
1983 if (i) {
1984 struct snd_kcontrol_new *knew;
1985 knew = via_clone_control(spec, &via_input_src_ctl);
1986 if (!knew)
1987 return -ENOMEM;
1988 knew->count = i;
1989 }
1990
1991 /* mic-boosts */
1992 for (i = 0; i < cfg->num_inputs; i++) {
1993 hda_nid_t pin = cfg->inputs[i].pin;
1994 unsigned int caps;
1995 const char *label;
1996 char name[32];
1997
1998 if (cfg->inputs[i].type != AUTO_PIN_MIC)
1999 continue;
2000 caps = query_amp_caps(codec, pin, HDA_INPUT);
2001 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2002 continue;
2003 label = hda_get_autocfg_input_label(codec, cfg, i);
2004 snprintf(name, sizeof(name), "%s Boost Capture Volume", label);
2005 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2006 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2007 if (err < 0)
2008 return err;
2009 }
2010
Joseph Chanc577b8a2006-11-29 15:29:40 +01002011 return 0;
2012}
2013
Takashi Iwaicb53c622007-08-10 17:21:45 +02002014#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002015static const struct hda_amp_list vt1708_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02002016 { 0x17, HDA_INPUT, 1 },
2017 { 0x17, HDA_INPUT, 2 },
2018 { 0x17, HDA_INPUT, 3 },
2019 { 0x17, HDA_INPUT, 4 },
2020 { } /* end */
2021};
2022#endif
2023
Harald Welte76d9b0d2008-09-09 15:50:37 +08002024static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2025{
2026 unsigned int def_conf;
2027 unsigned char seqassoc;
2028
Takashi Iwai2f334f92009-02-20 14:37:42 +01002029 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002030 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2031 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002032 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2033 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2034 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2035 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002036 }
2037
2038 return;
2039}
2040
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002041static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002042 struct snd_ctl_elem_value *ucontrol)
2043{
2044 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2045 struct via_spec *spec = codec->spec;
2046
2047 if (spec->codec_type != VT1708)
2048 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002049 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002050 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002051 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002052 return 0;
2053}
2054
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002055static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002056 struct snd_ctl_elem_value *ucontrol)
2057{
2058 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2059 struct via_spec *spec = codec->spec;
2060 int change;
2061
2062 if (spec->codec_type != VT1708)
2063 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002064 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002065 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002066 == !spec->vt1708_jack_detect;
2067 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002068 mute_aa_path(codec, 1);
2069 notify_aa_path_ctls(codec);
2070 }
2071 return change;
2072}
2073
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002074static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2075 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2076 .name = "Jack Detect",
2077 .count = 1,
2078 .info = snd_ctl_boolean_mono_info,
2079 .get = vt1708_jack_detect_get,
2080 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002081};
2082
Takashi Iwai12daef62011-06-18 17:45:49 +02002083static void fill_dig_outs(struct hda_codec *codec);
2084static void fill_dig_in(struct hda_codec *codec);
2085
2086static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002087{
2088 struct via_spec *spec = codec->spec;
2089 int err;
2090
2091 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2092 if (err < 0)
2093 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002094 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002095 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002096
Takashi Iwai4a796162011-06-17 17:53:38 +02002097 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002098 if (err < 0)
2099 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002100 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002101 if (err < 0)
2102 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002103 err = via_auto_create_speaker_ctls(codec);
2104 if (err < 0)
2105 return err;
Takashi Iwai620e2b22011-06-17 17:19:19 +02002106 err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002107 if (err < 0)
2108 return err;
2109
2110 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2111
Takashi Iwai12daef62011-06-18 17:45:49 +02002112 fill_dig_outs(codec);
2113 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002114
Takashi Iwai603c4012008-07-30 15:01:44 +02002115 if (spec->kctls.list)
2116 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002117
Takashi Iwai096a8852011-06-20 12:09:02 +02002118 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002119
Harald Welte0aa62ae2008-09-09 15:58:27 +08002120 spec->input_mux = &spec->private_imux[0];
2121
Takashi Iwaiece8d042011-06-19 16:24:21 +02002122 if (spec->hp_mux) {
2123 err = via_hp_build(codec);
2124 if (err < 0)
2125 return err;
2126 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002127
Takashi Iwaif4a78282011-06-17 18:46:48 +02002128 err = via_smart51_build(codec);
2129 if (err < 0)
2130 return err;
2131
Takashi Iwai5d417622011-06-20 11:32:27 +02002132 /* assign slave outs */
2133 if (spec->slave_dig_outs[0])
2134 codec->slave_dig_outs = spec->slave_dig_outs;
2135
Joseph Chanc577b8a2006-11-29 15:29:40 +01002136 return 1;
2137}
2138
Takashi Iwai5d417622011-06-20 11:32:27 +02002139static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002140{
Lydia Wang25eaba22009-10-10 19:08:43 +08002141 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002142 if (spec->multiout.dig_out_nid)
2143 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2144 if (spec->slave_dig_outs[0])
2145 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2146}
Lydia Wang25eaba22009-10-10 19:08:43 +08002147
Takashi Iwai5d417622011-06-20 11:32:27 +02002148static void via_auto_init_dig_in(struct hda_codec *codec)
2149{
2150 struct via_spec *spec = codec->spec;
2151 if (!spec->dig_in_nid)
2152 return;
2153 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2154 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2155}
2156
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002157/* initialize the unsolicited events */
2158static void via_auto_init_unsol_event(struct hda_codec *codec)
2159{
2160 struct via_spec *spec = codec->spec;
2161 struct auto_pin_cfg *cfg = &spec->autocfg;
2162 unsigned int ev;
2163 int i;
2164
2165 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2166 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2167 AC_VERB_SET_UNSOLICITED_ENABLE,
2168 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2169
2170 if (cfg->speaker_pins[0])
2171 ev = VIA_LINE_EVENT;
2172 else
2173 ev = 0;
2174 for (i = 0; i < cfg->line_outs; i++) {
2175 if (cfg->line_out_pins[i] &&
2176 is_jack_detectable(codec, cfg->line_out_pins[i]))
2177 snd_hda_codec_write(codec, cfg->line_out_pins[0], 0,
2178 AC_VERB_SET_UNSOLICITED_ENABLE,
2179 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2180 }
2181
2182 for (i = 0; i < cfg->num_inputs; i++) {
2183 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2184 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2185 AC_VERB_SET_UNSOLICITED_ENABLE,
2186 AC_USRSP_EN | VIA_JACK_EVENT);
2187 }
2188}
2189
Takashi Iwai5d417622011-06-20 11:32:27 +02002190static int via_init(struct hda_codec *codec)
2191{
2192 struct via_spec *spec = codec->spec;
2193 int i;
2194
2195 for (i = 0; i < spec->num_iverbs; i++)
2196 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2197
Joseph Chanc577b8a2006-11-29 15:29:40 +01002198 via_auto_init_multi_out(codec);
2199 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002200 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002201 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002202 via_auto_init_dig_outs(codec);
2203 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002204
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002205 via_auto_init_unsol_event(codec);
2206
2207 via_hp_automute(codec);
2208 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002209
Joseph Chanc577b8a2006-11-29 15:29:40 +01002210 return 0;
2211}
2212
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002213static void vt1708_update_hp_jack_state(struct work_struct *work)
2214{
2215 struct via_spec *spec = container_of(work, struct via_spec,
2216 vt1708_hp_work.work);
2217 if (spec->codec_type != VT1708)
2218 return;
2219 /* if jack state toggled */
2220 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002221 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002222 spec->vt1708_hp_present ^= 1;
2223 via_hp_automute(spec->codec);
2224 }
2225 vt1708_start_hp_work(spec);
2226}
2227
Takashi Iwai337b9d02009-07-07 18:18:59 +02002228static int get_mux_nids(struct hda_codec *codec)
2229{
2230 struct via_spec *spec = codec->spec;
2231 hda_nid_t nid, conn[8];
2232 unsigned int type;
2233 int i, n;
2234
2235 for (i = 0; i < spec->num_adc_nids; i++) {
2236 nid = spec->adc_nids[i];
2237 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002238 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002239 if (type == AC_WID_PIN)
2240 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002241 n = snd_hda_get_connections(codec, nid, conn,
2242 ARRAY_SIZE(conn));
2243 if (n <= 0)
2244 break;
2245 if (n > 1) {
2246 spec->mux_nids[i] = nid;
2247 break;
2248 }
2249 nid = conn[0];
2250 }
2251 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002252 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002253}
2254
Joseph Chanc577b8a2006-11-29 15:29:40 +01002255static int patch_vt1708(struct hda_codec *codec)
2256{
2257 struct via_spec *spec;
2258 int err;
2259
2260 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002261 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002262 if (spec == NULL)
2263 return -ENOMEM;
2264
Takashi Iwai620e2b22011-06-17 17:19:19 +02002265 spec->aa_mix_nid = 0x17;
2266
Takashi Iwai12daef62011-06-18 17:45:49 +02002267 /* Add HP and CD pin config connect bit re-config action */
2268 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2269 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2270
Joseph Chanc577b8a2006-11-29 15:29:40 +01002271 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002272 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002273 if (err < 0) {
2274 via_free(codec);
2275 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002276 }
2277
Takashi Iwai12daef62011-06-18 17:45:49 +02002278 /* add jack detect on/off control */
2279 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2280 return -ENOMEM;
2281
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002282 /* disable 32bit format on VT1708 */
2283 if (codec->vendor_id == 0x11061708)
2284 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002285
Joseph Chanc577b8a2006-11-29 15:29:40 +01002286 codec->patch_ops = via_patch_ops;
2287
Takashi Iwaicb53c622007-08-10 17:21:45 +02002288#ifdef CONFIG_SND_HDA_POWER_SAVE
2289 spec->loopback.amplist = vt1708_loopbacks;
2290#endif
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002291 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002292 return 0;
2293}
2294
Joseph Chanc577b8a2006-11-29 15:29:40 +01002295/*
2296 * generic initialization of ADC, input mixers and output mixers
2297 */
Takashi Iwaicb53c622007-08-10 17:21:45 +02002298#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002299static const struct hda_amp_list vt1709_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02002300 { 0x18, HDA_INPUT, 1 },
2301 { 0x18, HDA_INPUT, 2 },
2302 { 0x18, HDA_INPUT, 3 },
2303 { 0x18, HDA_INPUT, 4 },
2304 { } /* end */
2305};
2306#endif
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
Takashi Iwaicb53c622007-08-10 17:21:45 +02002328#ifdef CONFIG_SND_HDA_POWER_SAVE
2329 spec->loopback.amplist = vt1709_loopbacks;
2330#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01002331
2332 return 0;
2333}
2334/*
2335 * generic initialization of ADC, input mixers and output mixers
2336 */
Joseph Chanc577b8a2006-11-29 15:29:40 +01002337static int patch_vt1709_6ch(struct hda_codec *codec)
2338{
2339 struct via_spec *spec;
2340 int err;
2341
2342 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002343 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002344 if (spec == NULL)
2345 return -ENOMEM;
2346
Takashi Iwai620e2b22011-06-17 17:19:19 +02002347 spec->aa_mix_nid = 0x18;
2348
Takashi Iwai12daef62011-06-18 17:45:49 +02002349 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002350 if (err < 0) {
2351 via_free(codec);
2352 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002353 }
2354
Joseph Chanc577b8a2006-11-29 15:29:40 +01002355 codec->patch_ops = via_patch_ops;
2356
Takashi Iwaicb53c622007-08-10 17:21:45 +02002357#ifdef CONFIG_SND_HDA_POWER_SAVE
2358 spec->loopback.amplist = vt1709_loopbacks;
2359#endif
Josepch Chanf7278fd2007-12-13 16:40:40 +01002360 return 0;
2361}
2362
Josepch Chanf7278fd2007-12-13 16:40:40 +01002363/*
2364 * generic initialization of ADC, input mixers and output mixers
2365 */
Josepch Chanf7278fd2007-12-13 16:40:40 +01002366#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002367static const struct hda_amp_list vt1708B_loopbacks[] = {
Josepch Chanf7278fd2007-12-13 16:40:40 +01002368 { 0x16, HDA_INPUT, 1 },
2369 { 0x16, HDA_INPUT, 2 },
2370 { 0x16, HDA_INPUT, 3 },
2371 { 0x16, HDA_INPUT, 4 },
2372 { } /* end */
2373};
2374#endif
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002375
2376static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2377{
2378 struct via_spec *spec = codec->spec;
2379 int imux_is_smixer;
2380 unsigned int parm;
2381 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002382 if ((spec->codec_type != VT1708B_4CH) &&
2383 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002384 is_8ch = 1;
2385
2386 /* SW0 (17h) = stereo mixer */
2387 imux_is_smixer =
2388 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2389 == ((spec->codec_type == VT1708S) ? 5 : 0));
2390 /* inputs */
2391 /* PW 1/2/5 (1ah/1bh/1eh) */
2392 parm = AC_PWRST_D3;
2393 set_pin_power_state(codec, 0x1a, &parm);
2394 set_pin_power_state(codec, 0x1b, &parm);
2395 set_pin_power_state(codec, 0x1e, &parm);
2396 if (imux_is_smixer)
2397 parm = AC_PWRST_D0;
2398 /* SW0 (17h), AIW 0/1 (13h/14h) */
2399 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2400 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2401 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2402
2403 /* outputs */
2404 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2405 parm = AC_PWRST_D3;
2406 set_pin_power_state(codec, 0x19, &parm);
2407 if (spec->smart51_enabled)
2408 set_pin_power_state(codec, 0x1b, &parm);
2409 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2410 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2411
2412 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2413 if (is_8ch) {
2414 parm = AC_PWRST_D3;
2415 set_pin_power_state(codec, 0x22, &parm);
2416 if (spec->smart51_enabled)
2417 set_pin_power_state(codec, 0x1a, &parm);
2418 snd_hda_codec_write(codec, 0x26, 0,
2419 AC_VERB_SET_POWER_STATE, parm);
2420 snd_hda_codec_write(codec, 0x24, 0,
2421 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002422 } else if (codec->vendor_id == 0x11064397) {
2423 /* PW7(23h), SW2(27h), AOW2(25h) */
2424 parm = AC_PWRST_D3;
2425 set_pin_power_state(codec, 0x23, &parm);
2426 if (spec->smart51_enabled)
2427 set_pin_power_state(codec, 0x1a, &parm);
2428 snd_hda_codec_write(codec, 0x27, 0,
2429 AC_VERB_SET_POWER_STATE, parm);
2430 snd_hda_codec_write(codec, 0x25, 0,
2431 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002432 }
2433
2434 /* PW 3/4/7 (1ch/1dh/23h) */
2435 parm = AC_PWRST_D3;
2436 /* force to D0 for internal Speaker */
2437 set_pin_power_state(codec, 0x1c, &parm);
2438 set_pin_power_state(codec, 0x1d, &parm);
2439 if (is_8ch)
2440 set_pin_power_state(codec, 0x23, &parm);
2441
2442 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2443 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2444 imux_is_smixer ? AC_PWRST_D0 : parm);
2445 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2446 if (is_8ch) {
2447 snd_hda_codec_write(codec, 0x25, 0,
2448 AC_VERB_SET_POWER_STATE, parm);
2449 snd_hda_codec_write(codec, 0x27, 0,
2450 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002451 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2452 snd_hda_codec_write(codec, 0x25, 0,
2453 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002454}
2455
Lydia Wang518bf3b2009-10-10 19:07:29 +08002456static int patch_vt1708S(struct hda_codec *codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002457static int patch_vt1708B_8ch(struct hda_codec *codec)
2458{
2459 struct via_spec *spec;
2460 int err;
2461
Lydia Wang518bf3b2009-10-10 19:07:29 +08002462 if (get_codec_type(codec) == VT1708BCE)
2463 return patch_vt1708S(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002464 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002465 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002466 if (spec == NULL)
2467 return -ENOMEM;
2468
Takashi Iwai620e2b22011-06-17 17:19:19 +02002469 spec->aa_mix_nid = 0x16;
2470
Josepch Chanf7278fd2007-12-13 16:40:40 +01002471 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002472 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002473 if (err < 0) {
2474 via_free(codec);
2475 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002476 }
2477
Josepch Chanf7278fd2007-12-13 16:40:40 +01002478 codec->patch_ops = via_patch_ops;
2479
Josepch Chanf7278fd2007-12-13 16:40:40 +01002480#ifdef CONFIG_SND_HDA_POWER_SAVE
2481 spec->loopback.amplist = vt1708B_loopbacks;
2482#endif
2483
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002484 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2485
Josepch Chanf7278fd2007-12-13 16:40:40 +01002486 return 0;
2487}
2488
2489static int patch_vt1708B_4ch(struct hda_codec *codec)
2490{
2491 struct via_spec *spec;
2492 int err;
2493
2494 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002495 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002496 if (spec == NULL)
2497 return -ENOMEM;
2498
Josepch Chanf7278fd2007-12-13 16:40:40 +01002499 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002500 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002501 if (err < 0) {
2502 via_free(codec);
2503 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002504 }
2505
Josepch Chanf7278fd2007-12-13 16:40:40 +01002506 codec->patch_ops = via_patch_ops;
2507
Josepch Chanf7278fd2007-12-13 16:40:40 +01002508#ifdef CONFIG_SND_HDA_POWER_SAVE
2509 spec->loopback.amplist = vt1708B_loopbacks;
2510#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01002511
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002512 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2513
Joseph Chanc577b8a2006-11-29 15:29:40 +01002514 return 0;
2515}
2516
Harald Welted949cac2008-09-09 15:56:01 +08002517/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002518static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002519 /* Enable Mic Boost Volume backdoor */
2520 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002521 /* don't bybass mixer */
2522 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002523 { }
2524};
2525
Takashi Iwai9da29272009-05-07 16:31:14 +02002526/* fill out digital output widgets; one for master and one for slave outputs */
2527static void fill_dig_outs(struct hda_codec *codec)
2528{
2529 struct via_spec *spec = codec->spec;
2530 int i;
2531
2532 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2533 hda_nid_t nid;
2534 int conn;
2535
2536 nid = spec->autocfg.dig_out_pins[i];
2537 if (!nid)
2538 continue;
2539 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2540 if (conn < 1)
2541 continue;
2542 if (!spec->multiout.dig_out_nid)
2543 spec->multiout.dig_out_nid = nid;
2544 else {
2545 spec->slave_dig_outs[0] = nid;
2546 break; /* at most two dig outs */
2547 }
2548 }
2549}
2550
Takashi Iwai12daef62011-06-18 17:45:49 +02002551static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002552{
2553 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002554 hda_nid_t dig_nid;
2555 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002556
Takashi Iwai12daef62011-06-18 17:45:49 +02002557 if (!spec->autocfg.dig_in_pin)
2558 return;
Harald Welted949cac2008-09-09 15:56:01 +08002559
Takashi Iwai12daef62011-06-18 17:45:49 +02002560 dig_nid = codec->start_nid;
2561 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2562 unsigned int wcaps = get_wcaps(codec, dig_nid);
2563 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2564 continue;
2565 if (!(wcaps & AC_WCAP_DIGITAL))
2566 continue;
2567 if (!(wcaps & AC_WCAP_CONN_LIST))
2568 continue;
2569 err = get_connection_index(codec, dig_nid,
2570 spec->autocfg.dig_in_pin);
2571 if (err >= 0) {
2572 spec->dig_in_nid = dig_nid;
2573 break;
2574 }
2575 }
Harald Welted949cac2008-09-09 15:56:01 +08002576}
2577
2578#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002579static const struct hda_amp_list vt1708S_loopbacks[] = {
Harald Welted949cac2008-09-09 15:56:01 +08002580 { 0x16, HDA_INPUT, 1 },
2581 { 0x16, HDA_INPUT, 2 },
2582 { 0x16, HDA_INPUT, 3 },
2583 { 0x16, HDA_INPUT, 4 },
2584 { } /* end */
2585};
2586#endif
2587
Lydia Wang6369bcf2009-10-10 19:08:31 +08002588static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2589 int offset, int num_steps, int step_size)
2590{
2591 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2592 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2593 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2594 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2595 (0 << AC_AMPCAP_MUTE_SHIFT));
2596}
2597
Harald Welted949cac2008-09-09 15:56:01 +08002598static int patch_vt1708S(struct hda_codec *codec)
2599{
2600 struct via_spec *spec;
2601 int err;
2602
2603 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002604 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002605 if (spec == NULL)
2606 return -ENOMEM;
2607
Takashi Iwai620e2b22011-06-17 17:19:19 +02002608 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002609 override_mic_boost(codec, 0x1a, 0, 3, 40);
2610 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002611
Harald Welted949cac2008-09-09 15:56:01 +08002612 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002613 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002614 if (err < 0) {
2615 via_free(codec);
2616 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002617 }
2618
Takashi Iwai096a8852011-06-20 12:09:02 +02002619 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002620
Harald Welted949cac2008-09-09 15:56:01 +08002621 codec->patch_ops = via_patch_ops;
2622
Harald Welted949cac2008-09-09 15:56:01 +08002623#ifdef CONFIG_SND_HDA_POWER_SAVE
2624 spec->loopback.amplist = vt1708S_loopbacks;
2625#endif
2626
Lydia Wang518bf3b2009-10-10 19:07:29 +08002627 /* correct names for VT1708BCE */
2628 if (get_codec_type(codec) == VT1708BCE) {
2629 kfree(codec->chip_name);
2630 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2631 snprintf(codec->bus->card->mixername,
2632 sizeof(codec->bus->card->mixername),
2633 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002634 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002635 /* correct names for VT1705 */
2636 if (codec->vendor_id == 0x11064397) {
2637 kfree(codec->chip_name);
2638 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2639 snprintf(codec->bus->card->mixername,
2640 sizeof(codec->bus->card->mixername),
2641 "%s %s", codec->vendor_name, codec->chip_name);
2642 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002643 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002644 return 0;
2645}
2646
2647/* Patch for VT1702 */
2648
Takashi Iwai096a8852011-06-20 12:09:02 +02002649static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002650 /* mixer enable */
2651 {0x1, 0xF88, 0x3},
2652 /* GPIO 0~2 */
2653 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002654 { }
2655};
2656
Harald Welted949cac2008-09-09 15:56:01 +08002657#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002658static const struct hda_amp_list vt1702_loopbacks[] = {
Harald Welted949cac2008-09-09 15:56:01 +08002659 { 0x1A, HDA_INPUT, 1 },
2660 { 0x1A, HDA_INPUT, 2 },
2661 { 0x1A, HDA_INPUT, 3 },
2662 { 0x1A, HDA_INPUT, 4 },
2663 { } /* end */
2664};
2665#endif
2666
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002667static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2668{
2669 int imux_is_smixer =
2670 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2671 unsigned int parm;
2672 /* inputs */
2673 /* PW 1/2/5 (14h/15h/18h) */
2674 parm = AC_PWRST_D3;
2675 set_pin_power_state(codec, 0x14, &parm);
2676 set_pin_power_state(codec, 0x15, &parm);
2677 set_pin_power_state(codec, 0x18, &parm);
2678 if (imux_is_smixer)
2679 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2680 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2681 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2682 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2683 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2684 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2685
2686 /* outputs */
2687 /* PW 3/4 (16h/17h) */
2688 parm = AC_PWRST_D3;
2689 set_pin_power_state(codec, 0x17, &parm);
2690 set_pin_power_state(codec, 0x16, &parm);
2691 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2692 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2693 imux_is_smixer ? AC_PWRST_D0 : parm);
2694 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2695 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2696}
2697
Harald Welted949cac2008-09-09 15:56:01 +08002698static int patch_vt1702(struct hda_codec *codec)
2699{
2700 struct via_spec *spec;
2701 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002702
2703 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002704 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002705 if (spec == NULL)
2706 return -ENOMEM;
2707
Takashi Iwai620e2b22011-06-17 17:19:19 +02002708 spec->aa_mix_nid = 0x1a;
2709
Takashi Iwai12daef62011-06-18 17:45:49 +02002710 /* limit AA path volume to 0 dB */
2711 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2712 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2713 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2714 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2715 (1 << AC_AMPCAP_MUTE_SHIFT));
2716
Harald Welted949cac2008-09-09 15:56:01 +08002717 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002718 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002719 if (err < 0) {
2720 via_free(codec);
2721 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002722 }
2723
Takashi Iwai096a8852011-06-20 12:09:02 +02002724 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002725
Harald Welted949cac2008-09-09 15:56:01 +08002726 codec->patch_ops = via_patch_ops;
2727
Harald Welted949cac2008-09-09 15:56:01 +08002728#ifdef CONFIG_SND_HDA_POWER_SAVE
2729 spec->loopback.amplist = vt1702_loopbacks;
2730#endif
2731
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002732 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002733 return 0;
2734}
2735
Lydia Wangeb7188c2009-10-10 19:08:34 +08002736/* Patch for VT1718S */
2737
Takashi Iwai096a8852011-06-20 12:09:02 +02002738static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002739 /* Enable MW0 adjust Gain 5 */
2740 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002741 /* Enable Boost Volume backdoor */
2742 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002743
Lydia Wangeb7188c2009-10-10 19:08:34 +08002744 { }
2745};
2746
Lydia Wangeb7188c2009-10-10 19:08:34 +08002747#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002748static const struct hda_amp_list vt1718S_loopbacks[] = {
Lydia Wangeb7188c2009-10-10 19:08:34 +08002749 { 0x21, HDA_INPUT, 1 },
2750 { 0x21, HDA_INPUT, 2 },
2751 { 0x21, HDA_INPUT, 3 },
2752 { 0x21, HDA_INPUT, 4 },
2753 { } /* end */
2754};
2755#endif
2756
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002757static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2758{
2759 struct via_spec *spec = codec->spec;
2760 int imux_is_smixer;
2761 unsigned int parm;
2762 /* MUX6 (1eh) = stereo mixer */
2763 imux_is_smixer =
2764 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2765 /* inputs */
2766 /* PW 5/6/7 (29h/2ah/2bh) */
2767 parm = AC_PWRST_D3;
2768 set_pin_power_state(codec, 0x29, &parm);
2769 set_pin_power_state(codec, 0x2a, &parm);
2770 set_pin_power_state(codec, 0x2b, &parm);
2771 if (imux_is_smixer)
2772 parm = AC_PWRST_D0;
2773 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2774 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2775 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2776 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2777 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2778
2779 /* outputs */
2780 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2781 parm = AC_PWRST_D3;
2782 set_pin_power_state(codec, 0x27, &parm);
2783 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2784 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2785
2786 /* PW2 (26h), AOW2 (ah) */
2787 parm = AC_PWRST_D3;
2788 set_pin_power_state(codec, 0x26, &parm);
2789 if (spec->smart51_enabled)
2790 set_pin_power_state(codec, 0x2b, &parm);
2791 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2792
2793 /* PW0 (24h), AOW0 (8h) */
2794 parm = AC_PWRST_D3;
2795 set_pin_power_state(codec, 0x24, &parm);
2796 if (!spec->hp_independent_mode) /* check for redirected HP */
2797 set_pin_power_state(codec, 0x28, &parm);
2798 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2799 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2800 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2801 imux_is_smixer ? AC_PWRST_D0 : parm);
2802
2803 /* PW1 (25h), AOW1 (9h) */
2804 parm = AC_PWRST_D3;
2805 set_pin_power_state(codec, 0x25, &parm);
2806 if (spec->smart51_enabled)
2807 set_pin_power_state(codec, 0x2a, &parm);
2808 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2809
2810 if (spec->hp_independent_mode) {
2811 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2812 parm = AC_PWRST_D3;
2813 set_pin_power_state(codec, 0x28, &parm);
2814 snd_hda_codec_write(codec, 0x1b, 0,
2815 AC_VERB_SET_POWER_STATE, parm);
2816 snd_hda_codec_write(codec, 0x34, 0,
2817 AC_VERB_SET_POWER_STATE, parm);
2818 snd_hda_codec_write(codec, 0xc, 0,
2819 AC_VERB_SET_POWER_STATE, parm);
2820 }
2821}
2822
Lydia Wangeb7188c2009-10-10 19:08:34 +08002823static int patch_vt1718S(struct hda_codec *codec)
2824{
2825 struct via_spec *spec;
2826 int err;
2827
2828 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002829 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002830 if (spec == NULL)
2831 return -ENOMEM;
2832
Takashi Iwai620e2b22011-06-17 17:19:19 +02002833 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002834 override_mic_boost(codec, 0x2b, 0, 3, 40);
2835 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002836
Lydia Wangeb7188c2009-10-10 19:08:34 +08002837 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002838 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002839 if (err < 0) {
2840 via_free(codec);
2841 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002842 }
2843
Takashi Iwai096a8852011-06-20 12:09:02 +02002844 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002845
Lydia Wangeb7188c2009-10-10 19:08:34 +08002846 codec->patch_ops = via_patch_ops;
2847
Lydia Wangeb7188c2009-10-10 19:08:34 +08002848#ifdef CONFIG_SND_HDA_POWER_SAVE
2849 spec->loopback.amplist = vt1718S_loopbacks;
2850#endif
2851
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002852 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2853
Lydia Wangeb7188c2009-10-10 19:08:34 +08002854 return 0;
2855}
Lydia Wangf3db4232009-10-10 19:08:41 +08002856
2857/* Patch for VT1716S */
2858
2859static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2860 struct snd_ctl_elem_info *uinfo)
2861{
2862 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2863 uinfo->count = 1;
2864 uinfo->value.integer.min = 0;
2865 uinfo->value.integer.max = 1;
2866 return 0;
2867}
2868
2869static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2870 struct snd_ctl_elem_value *ucontrol)
2871{
2872 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2873 int index = 0;
2874
2875 index = snd_hda_codec_read(codec, 0x26, 0,
2876 AC_VERB_GET_CONNECT_SEL, 0);
2877 if (index != -1)
2878 *ucontrol->value.integer.value = index;
2879
2880 return 0;
2881}
2882
2883static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2884 struct snd_ctl_elem_value *ucontrol)
2885{
2886 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2887 struct via_spec *spec = codec->spec;
2888 int index = *ucontrol->value.integer.value;
2889
2890 snd_hda_codec_write(codec, 0x26, 0,
2891 AC_VERB_SET_CONNECT_SEL, index);
2892 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002893 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002894 return 1;
2895}
2896
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002897static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002898 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2899 {
2900 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2901 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002902 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08002903 .count = 1,
2904 .info = vt1716s_dmic_info,
2905 .get = vt1716s_dmic_get,
2906 .put = vt1716s_dmic_put,
2907 },
2908 {} /* end */
2909};
2910
2911
2912/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002913static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002914 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
2915 { } /* end */
2916};
2917
Takashi Iwai096a8852011-06-20 12:09:02 +02002918static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002919 /* Enable Boost Volume backdoor */
2920 {0x1, 0xf8a, 0x80},
2921 /* don't bybass mixer */
2922 {0x1, 0xf88, 0xc0},
2923 /* Enable mono output */
2924 {0x1, 0xf90, 0x08},
2925 { }
2926};
2927
Lydia Wangf3db4232009-10-10 19:08:41 +08002928#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002929static const struct hda_amp_list vt1716S_loopbacks[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002930 { 0x16, HDA_INPUT, 1 },
2931 { 0x16, HDA_INPUT, 2 },
2932 { 0x16, HDA_INPUT, 3 },
2933 { 0x16, HDA_INPUT, 4 },
2934 { } /* end */
2935};
2936#endif
2937
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002938static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
2939{
2940 struct via_spec *spec = codec->spec;
2941 int imux_is_smixer;
2942 unsigned int parm;
2943 unsigned int mono_out, present;
2944 /* SW0 (17h) = stereo mixer */
2945 imux_is_smixer =
2946 (snd_hda_codec_read(codec, 0x17, 0,
2947 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
2948 /* inputs */
2949 /* PW 1/2/5 (1ah/1bh/1eh) */
2950 parm = AC_PWRST_D3;
2951 set_pin_power_state(codec, 0x1a, &parm);
2952 set_pin_power_state(codec, 0x1b, &parm);
2953 set_pin_power_state(codec, 0x1e, &parm);
2954 if (imux_is_smixer)
2955 parm = AC_PWRST_D0;
2956 /* SW0 (17h), AIW0(13h) */
2957 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2958 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2959
2960 parm = AC_PWRST_D3;
2961 set_pin_power_state(codec, 0x1e, &parm);
2962 /* PW11 (22h) */
2963 if (spec->dmic_enabled)
2964 set_pin_power_state(codec, 0x22, &parm);
2965 else
2966 snd_hda_codec_write(codec, 0x22, 0,
2967 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
2968
2969 /* SW2(26h), AIW1(14h) */
2970 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
2971 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2972
2973 /* outputs */
2974 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2975 parm = AC_PWRST_D3;
2976 set_pin_power_state(codec, 0x19, &parm);
2977 /* Smart 5.1 PW2(1bh) */
2978 if (spec->smart51_enabled)
2979 set_pin_power_state(codec, 0x1b, &parm);
2980 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2981 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2982
2983 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
2984 parm = AC_PWRST_D3;
2985 set_pin_power_state(codec, 0x23, &parm);
2986 /* Smart 5.1 PW1(1ah) */
2987 if (spec->smart51_enabled)
2988 set_pin_power_state(codec, 0x1a, &parm);
2989 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
2990
2991 /* Smart 5.1 PW5(1eh) */
2992 if (spec->smart51_enabled)
2993 set_pin_power_state(codec, 0x1e, &parm);
2994 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
2995
2996 /* Mono out */
2997 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
2998 present = snd_hda_jack_detect(codec, 0x1c);
2999
3000 if (present)
3001 mono_out = 0;
3002 else {
3003 present = snd_hda_jack_detect(codec, 0x1d);
3004 if (!spec->hp_independent_mode && present)
3005 mono_out = 0;
3006 else
3007 mono_out = 1;
3008 }
3009 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3010 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3011 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3012 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3013
3014 /* PW 3/4 (1ch/1dh) */
3015 parm = AC_PWRST_D3;
3016 set_pin_power_state(codec, 0x1c, &parm);
3017 set_pin_power_state(codec, 0x1d, &parm);
3018 /* HP Independent Mode, power on AOW3 */
3019 if (spec->hp_independent_mode)
3020 snd_hda_codec_write(codec, 0x25, 0,
3021 AC_VERB_SET_POWER_STATE, parm);
3022
3023 /* force to D0 for internal Speaker */
3024 /* MW0 (16h), AOW0 (10h) */
3025 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3026 imux_is_smixer ? AC_PWRST_D0 : parm);
3027 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3028 mono_out ? AC_PWRST_D0 : parm);
3029}
3030
Lydia Wangf3db4232009-10-10 19:08:41 +08003031static int patch_vt1716S(struct hda_codec *codec)
3032{
3033 struct via_spec *spec;
3034 int err;
3035
3036 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003037 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003038 if (spec == NULL)
3039 return -ENOMEM;
3040
Takashi Iwai620e2b22011-06-17 17:19:19 +02003041 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003042 override_mic_boost(codec, 0x1a, 0, 3, 40);
3043 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003044
Lydia Wangf3db4232009-10-10 19:08:41 +08003045 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003046 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003047 if (err < 0) {
3048 via_free(codec);
3049 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003050 }
3051
Takashi Iwai096a8852011-06-20 12:09:02 +02003052 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003053
Lydia Wangf3db4232009-10-10 19:08:41 +08003054 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3055 spec->num_mixers++;
3056
3057 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3058
3059 codec->patch_ops = via_patch_ops;
3060
Lydia Wangf3db4232009-10-10 19:08:41 +08003061#ifdef CONFIG_SND_HDA_POWER_SAVE
3062 spec->loopback.amplist = vt1716S_loopbacks;
3063#endif
3064
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003065 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003066 return 0;
3067}
Lydia Wang25eaba22009-10-10 19:08:43 +08003068
3069/* for vt2002P */
3070
Takashi Iwai096a8852011-06-20 12:09:02 +02003071static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003072 /* Class-D speaker related verbs */
3073 {0x1, 0xfe0, 0x4},
3074 {0x1, 0xfe9, 0x80},
3075 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003076 /* Enable Boost Volume backdoor */
3077 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003078 /* Enable AOW0 to MW9 */
3079 {0x1, 0xfb8, 0x88},
3080 { }
3081};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003082
Takashi Iwai096a8852011-06-20 12:09:02 +02003083static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003084 /* Enable Boost Volume backdoor */
3085 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003086 /* Enable AOW0 to MW9 */
3087 {0x1, 0xfb8, 0x88},
3088 { }
3089};
Lydia Wang25eaba22009-10-10 19:08:43 +08003090
Lydia Wang25eaba22009-10-10 19:08:43 +08003091#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003092static const struct hda_amp_list vt2002P_loopbacks[] = {
Lydia Wang25eaba22009-10-10 19:08:43 +08003093 { 0x21, HDA_INPUT, 0 },
3094 { 0x21, HDA_INPUT, 1 },
3095 { 0x21, HDA_INPUT, 2 },
3096 { } /* end */
3097};
3098#endif
3099
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003100static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3101{
3102 struct via_spec *spec = codec->spec;
3103 int imux_is_smixer;
3104 unsigned int parm;
3105 unsigned int present;
3106 /* MUX9 (1eh) = stereo mixer */
3107 imux_is_smixer =
3108 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3109 /* inputs */
3110 /* PW 5/6/7 (29h/2ah/2bh) */
3111 parm = AC_PWRST_D3;
3112 set_pin_power_state(codec, 0x29, &parm);
3113 set_pin_power_state(codec, 0x2a, &parm);
3114 set_pin_power_state(codec, 0x2b, &parm);
3115 parm = AC_PWRST_D0;
3116 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3117 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3118 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3119 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3120 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3121
3122 /* outputs */
3123 /* AOW0 (8h)*/
3124 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3125
Lydia Wang118909562011-03-23 17:57:34 +08003126 if (spec->codec_type == VT1802) {
3127 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3128 parm = AC_PWRST_D3;
3129 set_pin_power_state(codec, 0x28, &parm);
3130 snd_hda_codec_write(codec, 0x18, 0,
3131 AC_VERB_SET_POWER_STATE, parm);
3132 snd_hda_codec_write(codec, 0x38, 0,
3133 AC_VERB_SET_POWER_STATE, parm);
3134 } else {
3135 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3136 parm = AC_PWRST_D3;
3137 set_pin_power_state(codec, 0x26, &parm);
3138 snd_hda_codec_write(codec, 0x1c, 0,
3139 AC_VERB_SET_POWER_STATE, parm);
3140 snd_hda_codec_write(codec, 0x37, 0,
3141 AC_VERB_SET_POWER_STATE, parm);
3142 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003143
Lydia Wang118909562011-03-23 17:57:34 +08003144 if (spec->codec_type == VT1802) {
3145 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3146 parm = AC_PWRST_D3;
3147 set_pin_power_state(codec, 0x25, &parm);
3148 snd_hda_codec_write(codec, 0x15, 0,
3149 AC_VERB_SET_POWER_STATE, parm);
3150 snd_hda_codec_write(codec, 0x35, 0,
3151 AC_VERB_SET_POWER_STATE, parm);
3152 } else {
3153 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3154 parm = AC_PWRST_D3;
3155 set_pin_power_state(codec, 0x25, &parm);
3156 snd_hda_codec_write(codec, 0x19, 0,
3157 AC_VERB_SET_POWER_STATE, parm);
3158 snd_hda_codec_write(codec, 0x35, 0,
3159 AC_VERB_SET_POWER_STATE, parm);
3160 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003161
3162 if (spec->hp_independent_mode)
3163 snd_hda_codec_write(codec, 0x9, 0,
3164 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3165
3166 /* Class-D */
3167 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3168 present = snd_hda_jack_detect(codec, 0x25);
3169
3170 parm = AC_PWRST_D3;
3171 set_pin_power_state(codec, 0x24, &parm);
3172 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003173 if (spec->codec_type == VT1802)
3174 snd_hda_codec_write(codec, 0x14, 0,
3175 AC_VERB_SET_POWER_STATE, parm);
3176 else
3177 snd_hda_codec_write(codec, 0x18, 0,
3178 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003179 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3180
3181 /* Mono Out */
3182 present = snd_hda_jack_detect(codec, 0x26);
3183
3184 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003185 if (spec->codec_type == VT1802) {
3186 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3187 snd_hda_codec_write(codec, 0x33, 0,
3188 AC_VERB_SET_POWER_STATE, parm);
3189 snd_hda_codec_write(codec, 0x1c, 0,
3190 AC_VERB_SET_POWER_STATE, parm);
3191 snd_hda_codec_write(codec, 0x3c, 0,
3192 AC_VERB_SET_POWER_STATE, parm);
3193 } else {
3194 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3195 snd_hda_codec_write(codec, 0x31, 0,
3196 AC_VERB_SET_POWER_STATE, parm);
3197 snd_hda_codec_write(codec, 0x17, 0,
3198 AC_VERB_SET_POWER_STATE, parm);
3199 snd_hda_codec_write(codec, 0x3b, 0,
3200 AC_VERB_SET_POWER_STATE, parm);
3201 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003202 /* MW9 (21h) */
3203 if (imux_is_smixer || !is_aa_path_mute(codec))
3204 snd_hda_codec_write(codec, 0x21, 0,
3205 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3206 else
3207 snd_hda_codec_write(codec, 0x21, 0,
3208 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3209}
Lydia Wang25eaba22009-10-10 19:08:43 +08003210
3211/* patch for vt2002P */
3212static int patch_vt2002P(struct hda_codec *codec)
3213{
3214 struct via_spec *spec;
3215 int err;
3216
3217 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003218 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003219 if (spec == NULL)
3220 return -ENOMEM;
3221
Takashi Iwai620e2b22011-06-17 17:19:19 +02003222 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003223 override_mic_boost(codec, 0x2b, 0, 3, 40);
3224 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003225
Lydia Wang25eaba22009-10-10 19:08:43 +08003226 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003227 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003228 if (err < 0) {
3229 via_free(codec);
3230 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003231 }
3232
Lydia Wang118909562011-03-23 17:57:34 +08003233 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003234 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003235 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003236 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003237
Lydia Wang25eaba22009-10-10 19:08:43 +08003238 codec->patch_ops = via_patch_ops;
3239
Lydia Wang25eaba22009-10-10 19:08:43 +08003240#ifdef CONFIG_SND_HDA_POWER_SAVE
3241 spec->loopback.amplist = vt2002P_loopbacks;
3242#endif
3243
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003244 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003245 return 0;
3246}
Lydia Wangab6734e2009-10-10 19:08:46 +08003247
3248/* for vt1812 */
3249
Takashi Iwai096a8852011-06-20 12:09:02 +02003250static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003251 /* Enable Boost Volume backdoor */
3252 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003253 /* Enable AOW0 to MW9 */
3254 {0x1, 0xfb8, 0xa8},
3255 { }
3256};
3257
Lydia Wangab6734e2009-10-10 19:08:46 +08003258#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003259static const struct hda_amp_list vt1812_loopbacks[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003260 { 0x21, HDA_INPUT, 0 },
3261 { 0x21, HDA_INPUT, 1 },
3262 { 0x21, HDA_INPUT, 2 },
3263 { } /* end */
3264};
3265#endif
3266
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003267static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3268{
3269 struct via_spec *spec = codec->spec;
3270 int imux_is_smixer =
3271 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3272 unsigned int parm;
3273 unsigned int present;
3274 /* MUX10 (1eh) = stereo mixer */
3275 imux_is_smixer =
3276 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3277 /* inputs */
3278 /* PW 5/6/7 (29h/2ah/2bh) */
3279 parm = AC_PWRST_D3;
3280 set_pin_power_state(codec, 0x29, &parm);
3281 set_pin_power_state(codec, 0x2a, &parm);
3282 set_pin_power_state(codec, 0x2b, &parm);
3283 parm = AC_PWRST_D0;
3284 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3285 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3286 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3287 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3288 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3289
3290 /* outputs */
3291 /* AOW0 (8h)*/
3292 snd_hda_codec_write(codec, 0x8, 0,
3293 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3294
3295 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3296 parm = AC_PWRST_D3;
3297 set_pin_power_state(codec, 0x28, &parm);
3298 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3299 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3300
3301 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3302 parm = AC_PWRST_D3;
3303 set_pin_power_state(codec, 0x25, &parm);
3304 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3305 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3306 if (spec->hp_independent_mode)
3307 snd_hda_codec_write(codec, 0x9, 0,
3308 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3309
3310 /* Internal Speaker */
3311 /* PW0 (24h), MW0(14h), MUX0(34h) */
3312 present = snd_hda_jack_detect(codec, 0x25);
3313
3314 parm = AC_PWRST_D3;
3315 set_pin_power_state(codec, 0x24, &parm);
3316 if (present) {
3317 snd_hda_codec_write(codec, 0x14, 0,
3318 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3319 snd_hda_codec_write(codec, 0x34, 0,
3320 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3321 } else {
3322 snd_hda_codec_write(codec, 0x14, 0,
3323 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3324 snd_hda_codec_write(codec, 0x34, 0,
3325 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3326 }
3327
3328
3329 /* Mono Out */
3330 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3331 present = snd_hda_jack_detect(codec, 0x28);
3332
3333 parm = AC_PWRST_D3;
3334 set_pin_power_state(codec, 0x31, &parm);
3335 if (present) {
3336 snd_hda_codec_write(codec, 0x1c, 0,
3337 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3338 snd_hda_codec_write(codec, 0x3c, 0,
3339 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3340 snd_hda_codec_write(codec, 0x3e, 0,
3341 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3342 } else {
3343 snd_hda_codec_write(codec, 0x1c, 0,
3344 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3345 snd_hda_codec_write(codec, 0x3c, 0,
3346 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3347 snd_hda_codec_write(codec, 0x3e, 0,
3348 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3349 }
3350
3351 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3352 parm = AC_PWRST_D3;
3353 set_pin_power_state(codec, 0x33, &parm);
3354 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3355 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3356
3357}
Lydia Wangab6734e2009-10-10 19:08:46 +08003358
3359/* patch for vt1812 */
3360static int patch_vt1812(struct hda_codec *codec)
3361{
3362 struct via_spec *spec;
3363 int err;
3364
3365 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003366 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003367 if (spec == NULL)
3368 return -ENOMEM;
3369
Takashi Iwai620e2b22011-06-17 17:19:19 +02003370 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003371 override_mic_boost(codec, 0x2b, 0, 3, 40);
3372 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003373
Lydia Wangab6734e2009-10-10 19:08:46 +08003374 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003375 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003376 if (err < 0) {
3377 via_free(codec);
3378 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003379 }
3380
Takashi Iwai096a8852011-06-20 12:09:02 +02003381 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003382
Lydia Wangab6734e2009-10-10 19:08:46 +08003383 codec->patch_ops = via_patch_ops;
3384
Lydia Wangab6734e2009-10-10 19:08:46 +08003385#ifdef CONFIG_SND_HDA_POWER_SAVE
3386 spec->loopback.amplist = vt1812_loopbacks;
3387#endif
3388
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003389 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003390 return 0;
3391}
3392
Joseph Chanc577b8a2006-11-29 15:29:40 +01003393/*
3394 * patch entries
3395 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003396static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003397 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3398 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3399 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3400 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3401 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003402 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003403 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003404 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003405 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003406 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003407 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003408 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003409 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003410 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003411 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003412 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003413 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003414 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003415 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003416 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003417 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003418 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003419 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003420 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003421 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003422 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003423 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003424 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003425 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003426 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003427 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003428 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003429 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003430 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003431 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003432 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003433 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003434 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003435 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003436 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003437 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003438 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003439 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003440 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003441 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003442 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003443 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003444 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003445 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003446 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003447 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003448 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003449 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003450 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003451 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003452 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003453 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003454 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003455 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003456 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003457 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003458 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003459 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003460 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003461 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003462 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003463 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003464 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003465 { .id = 0x11060428, .name = "VT1718S",
3466 .patch = patch_vt1718S},
3467 { .id = 0x11064428, .name = "VT1718S",
3468 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003469 { .id = 0x11060441, .name = "VT2020",
3470 .patch = patch_vt1718S},
3471 { .id = 0x11064441, .name = "VT1828S",
3472 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003473 { .id = 0x11060433, .name = "VT1716S",
3474 .patch = patch_vt1716S},
3475 { .id = 0x1106a721, .name = "VT1716S",
3476 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003477 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3478 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003479 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003480 { .id = 0x11060440, .name = "VT1818S",
3481 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003482 { .id = 0x11060446, .name = "VT1802",
3483 .patch = patch_vt2002P},
3484 { .id = 0x11068446, .name = "VT1802",
3485 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003486 {} /* terminator */
3487};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003488
3489MODULE_ALIAS("snd-hda-codec-id:1106*");
3490
3491static struct hda_codec_preset_list via_list = {
3492 .preset = snd_hda_preset_via,
3493 .owner = THIS_MODULE,
3494};
3495
3496MODULE_LICENSE("GPL");
3497MODULE_DESCRIPTION("VIA HD-audio codec");
3498
3499static int __init patch_via_init(void)
3500{
3501 return snd_hda_add_codec_preset(&via_list);
3502}
3503
3504static void __exit patch_via_exit(void)
3505{
3506 snd_hda_delete_codec_preset(&via_list);
3507}
3508
3509module_init(patch_via_init)
3510module_exit(patch_via_exit)