blob: deb33ae109c8f40fa60d10c18339d08662ba4a85 [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;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200168 unsigned int can_smart51;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800169 unsigned int smart51_enabled;
Lydia Wangf3db4232009-10-10 19:08:41 +0800170 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200171 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800172 enum VIA_HDA_CODEC codec_type;
173
174 /* work to check hp jack state */
175 struct hda_codec *codec;
176 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200177 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800178 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800179
180 void (*set_widgets_power_state)(struct hda_codec *codec);
181
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800182#ifdef CONFIG_SND_HDA_POWER_SAVE
183 struct hda_loopback_check loopback;
184#endif
185};
186
Lydia Wang0341ccd2011-03-22 16:25:03 +0800187static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100188static struct via_spec * via_new_spec(struct hda_codec *codec)
189{
190 struct via_spec *spec;
191
192 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
193 if (spec == NULL)
194 return NULL;
195
196 codec->spec = spec;
197 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800198 spec->codec_type = get_codec_type(codec);
199 /* VT1708BCE & VT1708S are almost same */
200 if (spec->codec_type == VT1708BCE)
201 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100202 return spec;
203}
204
Lydia Wang744ff5f2009-10-10 19:07:26 +0800205static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800206{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800207 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800208 u16 ven_id = vendor_id >> 16;
209 u16 dev_id = vendor_id & 0xffff;
210 enum VIA_HDA_CODEC codec_type;
211
212 /* get codec type */
213 if (ven_id != 0x1106)
214 codec_type = UNKNOWN;
215 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
216 codec_type = VT1708;
217 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
218 codec_type = VT1709_10CH;
219 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
220 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800221 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800222 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800223 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
224 codec_type = VT1708BCE;
225 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800226 codec_type = VT1708B_4CH;
227 else if ((dev_id & 0xfff) == 0x397
228 && (dev_id >> 12) < 8)
229 codec_type = VT1708S;
230 else if ((dev_id & 0xfff) == 0x398
231 && (dev_id >> 12) < 8)
232 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800233 else if ((dev_id & 0xfff) == 0x428
234 && (dev_id >> 12) < 8)
235 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800236 else if (dev_id == 0x0433 || dev_id == 0xa721)
237 codec_type = VT1716S;
Lydia Wangbb3c6bf2009-10-10 19:08:39 +0800238 else if (dev_id == 0x0441 || dev_id == 0x4441)
239 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800240 else if (dev_id == 0x0438 || dev_id == 0x4438)
241 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800242 else if (dev_id == 0x0448)
243 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800244 else if (dev_id == 0x0440)
245 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800246 else if ((dev_id & 0xfff) == 0x446)
247 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800248 else
249 codec_type = UNKNOWN;
250 return codec_type;
251};
252
Lydia Wangec7e7e42011-03-24 12:43:44 +0800253#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800254#define VIA_HP_EVENT 0x01
255#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200256#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800257
Joseph Chanc577b8a2006-11-29 15:29:40 +0100258enum {
259 VIA_CTL_WIDGET_VOL,
260 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800261 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100262};
263
Lydia Wangf5271102009-10-10 19:07:35 +0800264static void analog_low_current_mode(struct hda_codec *codec, int stream_idle);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800265static int is_aa_path_mute(struct hda_codec *codec);
266
267static void vt1708_start_hp_work(struct via_spec *spec)
268{
269 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
270 return;
271 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200272 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800273 if (!delayed_work_pending(&spec->vt1708_hp_work))
274 schedule_delayed_work(&spec->vt1708_hp_work,
275 msecs_to_jiffies(100));
276}
277
278static void vt1708_stop_hp_work(struct via_spec *spec)
279{
280 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
281 return;
282 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
283 && !is_aa_path_mute(spec->codec))
284 return;
285 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200286 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100287 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800288}
Lydia Wangf5271102009-10-10 19:07:35 +0800289
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800290static void set_widgets_power_state(struct hda_codec *codec)
291{
292 struct via_spec *spec = codec->spec;
293 if (spec->set_widgets_power_state)
294 spec->set_widgets_power_state(codec);
295}
Lydia Wang25eaba22009-10-10 19:08:43 +0800296
Lydia Wangf5271102009-10-10 19:07:35 +0800297static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
298 struct snd_ctl_elem_value *ucontrol)
299{
300 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
301 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
302
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800303 set_widgets_power_state(codec);
Lydia Wangf5271102009-10-10 19:07:35 +0800304 analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800305 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
306 if (is_aa_path_mute(codec))
307 vt1708_start_hp_work(codec->spec);
308 else
309 vt1708_stop_hp_work(codec->spec);
310 }
Lydia Wangf5271102009-10-10 19:07:35 +0800311 return change;
312}
313
314/* modify .put = snd_hda_mixer_amp_switch_put */
315#define ANALOG_INPUT_MUTE \
316 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
317 .name = NULL, \
318 .index = 0, \
319 .info = snd_hda_mixer_amp_switch_info, \
320 .get = snd_hda_mixer_amp_switch_get, \
321 .put = analog_input_switch_put, \
322 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
323
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200324static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100325 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
326 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800327 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100328};
329
Lydia Wangab6734e2009-10-10 19:08:46 +0800330
Joseph Chanc577b8a2006-11-29 15:29:40 +0100331/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200332static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
333 const struct snd_kcontrol_new *tmpl,
334 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100335{
336 struct snd_kcontrol_new *knew;
337
Takashi Iwai603c4012008-07-30 15:01:44 +0200338 snd_array_init(&spec->kctls, sizeof(*knew), 32);
339 knew = snd_array_new(&spec->kctls);
340 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200341 return NULL;
342 *knew = *tmpl;
343 if (!name)
344 name = tmpl->name;
345 if (name) {
346 knew->name = kstrdup(name, GFP_KERNEL);
347 if (!knew->name)
348 return NULL;
349 }
350 return knew;
351}
352
353static int __via_add_control(struct via_spec *spec, int type, const char *name,
354 int idx, unsigned long val)
355{
356 struct snd_kcontrol_new *knew;
357
358 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
359 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100360 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200361 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100362 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100363 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100364 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100365 return 0;
366}
367
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200368#define via_add_control(spec, type, name, val) \
369 __via_add_control(spec, type, name, 0, val)
370
Takashi Iwai291c9e32011-06-17 16:15:26 +0200371#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100372
Takashi Iwai603c4012008-07-30 15:01:44 +0200373static void via_free_kctls(struct hda_codec *codec)
374{
375 struct via_spec *spec = codec->spec;
376
377 if (spec->kctls.list) {
378 struct snd_kcontrol_new *kctl = spec->kctls.list;
379 int i;
380 for (i = 0; i < spec->kctls.used; i++)
381 kfree(kctl[i].name);
382 }
383 snd_array_free(&spec->kctls);
384}
385
Joseph Chanc577b8a2006-11-29 15:29:40 +0100386/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800387static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200388 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100389{
390 char name[32];
391 int err;
392
393 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200394 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100395 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
396 if (err < 0)
397 return err;
398 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200399 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, 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 return 0;
404}
405
Takashi Iwai5d417622011-06-20 11:32:27 +0200406/* return the index of the given widget nid as the source of mux;
407 * return -1 if not found;
408 * if num_conns is non-NULL, set the total number of connections
409 */
410static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux,
411 hda_nid_t nid, int *num_conns)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100412{
Takashi Iwai5d417622011-06-20 11:32:27 +0200413 hda_nid_t conn[HDA_MAX_NUM_INPUTS];
414 int i, nums;
415
416 nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
417 if (num_conns)
418 *num_conns = nums;
419 for (i = 0; i < nums; i++)
420 if (conn[i] == nid)
421 return i;
422 return -1;
423}
424
425#define get_connection_index(codec, mux, nid) \
426 __get_connection_index(codec, mux, nid, NULL)
427
428/* unmute input amp and select the specificed source */
429static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid,
430 hda_nid_t src, hda_nid_t mix)
431{
432 int idx, num_conns;
433
434 idx = __get_connection_index(codec, nid, src, &num_conns);
435 if (idx < 0)
436 return;
437
438 /* select the route explicitly when multiple connections exist */
439 if (num_conns > 1)
Lydia Wang377ff312009-10-10 19:08:55 +0800440 snd_hda_codec_write(codec, nid, 0,
Takashi Iwai5d417622011-06-20 11:32:27 +0200441 AC_VERB_SET_CONNECT_SEL, idx);
442 /* unmute if the input amp is present */
443 if (!(query_amp_caps(codec, nid, HDA_INPUT) &
444 (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)))
445 return;
446 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
447 AMP_IN_UNMUTE(idx));
448
449 /* unmute AA-path if present */
450 if (!mix)
451 return;
452 idx = __get_connection_index(codec, nid, mix, NULL);
453 if (idx >= 0)
454 snd_hda_codec_write(codec, nid, 0,
455 AC_VERB_SET_AMP_GAIN_MUTE,
456 AMP_IN_UNMUTE(idx));
457}
458
459/* set the given pin as output */
460static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
461 int pin_type)
462{
463 if (!pin)
464 return;
465 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
466 pin_type);
467 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
468 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200469 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100470}
471
Takashi Iwai5d417622011-06-20 11:32:27 +0200472static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin,
473 int pin_type, struct nid_path *path)
474{
475 struct via_spec *spec = codec->spec;
476 unsigned int caps;
477 hda_nid_t nid;
478 int i;
479
480 if (!pin)
481 return;
482
483 init_output_pin(codec, pin, pin_type);
484 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
485 if (caps & AC_AMPCAP_MUTE) {
486 unsigned int val;
487 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
488 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
489 AMP_OUT_MUTE | val);
490 }
491
492 /* initialize the output path */
493 nid = pin;
494 for (i = 0; i < path->depth; i++) {
495 unmute_and_select(codec, nid, path->idx[i], spec->aa_mix_nid);
496 nid = path->path[i];
497 if (query_amp_caps(codec, nid, HDA_OUTPUT) &
498 (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE))
499 snd_hda_codec_write(codec, nid, 0,
500 AC_VERB_SET_AMP_GAIN_MUTE,
501 AMP_OUT_UNMUTE);
502 }
503}
504
Joseph Chanc577b8a2006-11-29 15:29:40 +0100505
506static void via_auto_init_multi_out(struct hda_codec *codec)
507{
508 struct via_spec *spec = codec->spec;
509 int i;
510
Takashi Iwai5d417622011-06-20 11:32:27 +0200511 for (i = 0; i < spec->autocfg.line_outs; i++)
512 via_auto_init_output(codec, spec->autocfg.line_out_pins[i],
513 PIN_OUT, &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100514}
515
516static void via_auto_init_hp_out(struct hda_codec *codec)
517{
518 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100519
Takashi Iwai5d417622011-06-20 11:32:27 +0200520 if (spec->hp_dac_nid)
521 via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
522 &spec->hp_path);
523 else
524 via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
525 &spec->hp_dep_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100526}
527
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200528static void via_auto_init_speaker_out(struct hda_codec *codec)
529{
530 struct via_spec *spec = codec->spec;
531
532 if (spec->autocfg.speaker_outs)
533 via_auto_init_output(codec, spec->autocfg.speaker_pins[0],
534 PIN_OUT, &spec->speaker_path);
535}
536
Takashi Iwaif4a78282011-06-17 18:46:48 +0200537static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200538
Joseph Chanc577b8a2006-11-29 15:29:40 +0100539static void via_auto_init_analog_input(struct hda_codec *codec)
540{
541 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200542 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200543 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200544 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200545 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100546
Takashi Iwai096a8852011-06-20 12:09:02 +0200547 /* init ADCs */
548 for (i = 0; i < spec->num_adc_nids; i++) {
549 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
550 AC_VERB_SET_AMP_GAIN_MUTE,
551 AMP_IN_UNMUTE(0));
552 }
553
554 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200555 for (i = 0; i < cfg->num_inputs; i++) {
556 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200557 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200558 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100559 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200560 ctl = PIN_VREF50;
561 else
562 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100563 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200564 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100565 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200566
567 /* init input-src */
568 for (i = 0; i < spec->num_adc_nids; i++) {
569 const struct hda_input_mux *imux = spec->input_mux;
570 if (!imux || !spec->mux_nids[i])
571 continue;
572 snd_hda_codec_write(codec, spec->mux_nids[i], 0,
573 AC_VERB_SET_CONNECT_SEL,
574 imux->items[spec->cur_mux[i]].index);
575 }
576
577 /* init aa-mixer */
578 if (!spec->aa_mix_nid)
579 return;
580 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
581 ARRAY_SIZE(conn));
582 for (i = 0; i < num_conns; i++) {
583 unsigned int caps = get_wcaps(codec, conn[i]);
584 if (get_wcaps_type(caps) == AC_WID_PIN)
585 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
586 AC_VERB_SET_AMP_GAIN_MUTE,
587 AMP_IN_MUTE(i));
588 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100589}
Lydia Wangf5271102009-10-10 19:07:35 +0800590
591static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
592 unsigned int *affected_parm)
593{
594 unsigned parm;
595 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
596 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
597 >> AC_DEFCFG_MISC_SHIFT
598 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800599 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200600 unsigned present = 0;
601
602 no_presence |= spec->no_pin_power_ctl;
603 if (!no_presence)
604 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200605 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800606 || ((no_presence || present)
607 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800608 *affected_parm = AC_PWRST_D0; /* if it's connected */
609 parm = AC_PWRST_D0;
610 } else
611 parm = AC_PWRST_D3;
612
613 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
614}
615
Takashi Iwai24088a52011-06-17 16:59:21 +0200616static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
617 struct snd_ctl_elem_info *uinfo)
618{
619 static const char * const texts[] = {
620 "Disabled", "Enabled"
621 };
622
623 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
624 uinfo->count = 1;
625 uinfo->value.enumerated.items = 2;
626 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
627 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
628 strcpy(uinfo->value.enumerated.name,
629 texts[uinfo->value.enumerated.item]);
630 return 0;
631}
632
633static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
634 struct snd_ctl_elem_value *ucontrol)
635{
636 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
637 struct via_spec *spec = codec->spec;
638 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
639 return 0;
640}
641
642static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
643 struct snd_ctl_elem_value *ucontrol)
644{
645 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
646 struct via_spec *spec = codec->spec;
647 unsigned int val = !ucontrol->value.enumerated.item[0];
648
649 if (val == spec->no_pin_power_ctl)
650 return 0;
651 spec->no_pin_power_ctl = val;
652 set_widgets_power_state(codec);
653 return 1;
654}
655
656static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
657 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
658 .name = "Dynamic Power-Control",
659 .info = via_pin_power_ctl_info,
660 .get = via_pin_power_ctl_get,
661 .put = via_pin_power_ctl_put,
662};
663
664
Joseph Chanc577b8a2006-11-29 15:29:40 +0100665/*
666 * input MUX handling
667 */
668static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
669 struct snd_ctl_elem_info *uinfo)
670{
671 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
672 struct via_spec *spec = codec->spec;
673 return snd_hda_input_mux_info(spec->input_mux, uinfo);
674}
675
676static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
677 struct snd_ctl_elem_value *ucontrol)
678{
679 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
680 struct via_spec *spec = codec->spec;
681 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
682
683 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
684 return 0;
685}
686
687static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
688 struct snd_ctl_elem_value *ucontrol)
689{
690 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
691 struct via_spec *spec = codec->spec;
692 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800693 int ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100694
Takashi Iwai337b9d02009-07-07 18:18:59 +0200695 if (!spec->mux_nids[adc_idx])
696 return -EINVAL;
Lydia Wanga80e6e32009-10-10 19:07:55 +0800697 /* switch to D0 beofre change index */
698 if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
699 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
700 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
701 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800702
703 ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
704 spec->mux_nids[adc_idx],
705 &spec->cur_mux[adc_idx]);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800706 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800707 set_widgets_power_state(codec);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800708
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800709 return ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100710}
711
Harald Welte0aa62ae2008-09-09 15:58:27 +0800712static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
713 struct snd_ctl_elem_info *uinfo)
714{
715 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
716 struct via_spec *spec = codec->spec;
717 return snd_hda_input_mux_info(spec->hp_mux, uinfo);
718}
719
720static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
721 struct snd_ctl_elem_value *ucontrol)
722{
723 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800724 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800725
Takashi Iwaiece8d042011-06-19 16:24:21 +0200726 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800727 return 0;
728}
729
Harald Welte0aa62ae2008-09-09 15:58:27 +0800730static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
731 struct snd_ctl_elem_value *ucontrol)
732{
733 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
734 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100735 hda_nid_t nid = kcontrol->private_value;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800736 unsigned int pinsel = ucontrol->value.enumerated.item[0];
Lydia Wangcdc17842009-10-10 19:07:47 +0800737 /* Get Independent Mode index of headphone pin widget */
738 spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
739 ? 1 : 0;
Takashi Iwaiece8d042011-06-19 16:24:21 +0200740 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800741
Lydia Wangce0e5a92011-03-22 16:22:37 +0800742 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800743 set_widgets_power_state(codec);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800744 return 0;
745}
746
Takashi Iwaiece8d042011-06-19 16:24:21 +0200747static const struct snd_kcontrol_new via_hp_mixer = {
748 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
749 .name = "Independent HP",
750 .info = via_independent_hp_info,
751 .get = via_independent_hp_get,
752 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800753};
754
Takashi Iwai3d83e572010-04-14 14:36:23 +0200755static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100756{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200757 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100758 struct snd_kcontrol_new *knew;
759 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100760
Takashi Iwaiece8d042011-06-19 16:24:21 +0200761 nid = spec->autocfg.hp_pins[0];
762 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200763 if (knew == NULL)
764 return -ENOMEM;
765
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100766 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
767 knew->private_value = nid;
768
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100769 return 0;
770}
771
Lydia Wang1564b282009-10-10 19:07:52 +0800772static void notify_aa_path_ctls(struct hda_codec *codec)
773{
774 int i;
775 struct snd_ctl_elem_id id;
Lydia Wang525566c2011-04-28 16:03:39 +0800776 const char *labels[] = {"Mic", "Front Mic", "Line", "Rear Mic"};
777 struct snd_kcontrol *ctl;
Lydia Wang1564b282009-10-10 19:07:52 +0800778
779 memset(&id, 0, sizeof(id));
780 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
781 for (i = 0; i < ARRAY_SIZE(labels); i++) {
782 sprintf(id.name, "%s Playback Volume", labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800783 ctl = snd_hda_find_mixer_ctl(codec, id.name);
784 if (ctl)
785 snd_ctl_notify(codec->bus->card,
786 SNDRV_CTL_EVENT_MASK_VALUE,
787 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800788 }
789}
790
791static void mute_aa_path(struct hda_codec *codec, int mute)
792{
793 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800794 int start_idx;
795 int end_idx;
796 int i;
797 /* get nid of MW0 and start & end index */
798 switch (spec->codec_type) {
799 case VT1708:
Lydia Wang1564b282009-10-10 19:07:52 +0800800 start_idx = 2;
801 end_idx = 4;
802 break;
803 case VT1709_10CH:
804 case VT1709_6CH:
Lydia Wang1564b282009-10-10 19:07:52 +0800805 start_idx = 2;
806 end_idx = 4;
807 break;
808 case VT1708B_8CH:
809 case VT1708B_4CH:
810 case VT1708S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800811 case VT1716S:
Lydia Wang1564b282009-10-10 19:07:52 +0800812 start_idx = 2;
813 end_idx = 4;
814 break;
Lydia Wangab657e02011-03-22 16:23:23 +0800815 case VT1718S:
Lydia Wangab657e02011-03-22 16:23:23 +0800816 start_idx = 1;
817 end_idx = 3;
818 break;
Lydia Wang1564b282009-10-10 19:07:52 +0800819 default:
820 return;
821 }
822 /* check AA path's mute status */
823 for (i = start_idx; i <= end_idx; i++) {
824 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Takashi Iwai620e2b22011-06-17 17:19:19 +0200825 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid, HDA_INPUT, i,
Lydia Wang1564b282009-10-10 19:07:52 +0800826 HDA_AMP_MUTE, val);
827 }
828}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200829
830static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
Lydia Wang1564b282009-10-10 19:07:52 +0800831{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200832 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200833 const struct auto_pin_cfg *cfg = &spec->autocfg;
834 int i;
835
836 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaif4a78282011-06-17 18:46:48 +0200837 unsigned int defcfg;
838 if (pin != cfg->inputs[i].pin)
839 continue;
840 if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
841 return false;
842 defcfg = snd_hda_codec_get_pincfg(codec, pin);
843 if (snd_hda_get_input_pin_attr(defcfg) < INPUT_PIN_ATTR_NORMAL)
844 return false;
845 return true;
Lydia Wang1564b282009-10-10 19:07:52 +0800846 }
Takashi Iwaif4a78282011-06-17 18:46:48 +0200847 return false;
Lydia Wang1564b282009-10-10 19:07:52 +0800848}
849
850static int via_smart51_info(struct snd_kcontrol *kcontrol,
851 struct snd_ctl_elem_info *uinfo)
852{
853 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
854 uinfo->count = 1;
855 uinfo->value.integer.min = 0;
856 uinfo->value.integer.max = 1;
857 return 0;
858}
859
860static int via_smart51_get(struct snd_kcontrol *kcontrol,
861 struct snd_ctl_elem_value *ucontrol)
862{
863 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
864 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200865 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang1564b282009-10-10 19:07:52 +0800866 int on = 1;
867 int i;
868
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200869 for (i = 0; i < cfg->num_inputs; i++) {
870 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200871 unsigned int ctl;
Takashi Iwai86e29592010-09-09 14:50:17 +0200872 if (cfg->inputs[i].type == AUTO_PIN_MIC &&
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200873 spec->hp_independent_mode && spec->codec_type != VT1718S)
874 continue; /* ignore FMic for independent HP */
Takashi Iwaif4a78282011-06-17 18:46:48 +0200875 if (!is_smart51_pins(codec, nid))
876 continue;
877 ctl = snd_hda_codec_read(codec, nid, 0,
878 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200879 if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
880 on = 0;
Lydia Wang1564b282009-10-10 19:07:52 +0800881 }
882 *ucontrol->value.integer.value = on;
883 return 0;
884}
885
886static int via_smart51_put(struct snd_kcontrol *kcontrol,
887 struct snd_ctl_elem_value *ucontrol)
888{
889 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
890 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200891 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang1564b282009-10-10 19:07:52 +0800892 int out_in = *ucontrol->value.integer.value
893 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800894 int i;
895
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200896 for (i = 0; i < cfg->num_inputs; i++) {
897 hda_nid_t nid = cfg->inputs[i].pin;
898 unsigned int parm;
899
Takashi Iwai86e29592010-09-09 14:50:17 +0200900 if (cfg->inputs[i].type == AUTO_PIN_MIC &&
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200901 spec->hp_independent_mode && spec->codec_type != VT1718S)
Lydia Wang1564b282009-10-10 19:07:52 +0800902 continue; /* don't retask FMic for independent HP */
Takashi Iwaif4a78282011-06-17 18:46:48 +0200903 if (!is_smart51_pins(codec, nid))
904 continue;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200905
906 parm = snd_hda_codec_read(codec, nid, 0,
907 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
908 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
909 parm |= out_in;
910 snd_hda_codec_write(codec, nid, 0,
911 AC_VERB_SET_PIN_WIDGET_CONTROL,
912 parm);
913 if (out_in == AC_PINCTL_OUT_EN) {
914 mute_aa_path(codec, 1);
915 notify_aa_path_ctls(codec);
916 }
Lydia Wang1564b282009-10-10 19:07:52 +0800917 }
918 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800919 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800920 return 1;
921}
922
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200923static const struct snd_kcontrol_new via_smart51_mixer = {
924 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
925 .name = "Smart 5.1",
926 .count = 1,
927 .info = via_smart51_info,
928 .get = via_smart51_get,
929 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800930};
931
Takashi Iwaif4a78282011-06-17 18:46:48 +0200932static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100933{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200934 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100935 struct snd_kcontrol_new *knew;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200936 const struct auto_pin_cfg *cfg = &spec->autocfg;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100937 hda_nid_t nid;
938 int i;
939
Takashi Iwaif4a78282011-06-17 18:46:48 +0200940 if (!spec->can_smart51)
Lydia Wangcb34c202011-04-27 17:44:16 +0800941 return 0;
942
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200943 knew = via_clone_control(spec, &via_smart51_mixer);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100944 if (knew == NULL)
945 return -ENOMEM;
946
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200947 for (i = 0; i < cfg->num_inputs; i++) {
948 nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200949 if (is_smart51_pins(codec, nid)) {
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200950 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200951 break;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100952 }
953 }
954
955 return 0;
956}
957
Lydia Wangf5271102009-10-10 19:07:35 +0800958/* check AA path's mute statue */
959static int is_aa_path_mute(struct hda_codec *codec)
960{
961 int mute = 1;
Lydia Wangf5271102009-10-10 19:07:35 +0800962 int start_idx;
963 int end_idx;
964 int i;
965 struct via_spec *spec = codec->spec;
966 /* get nid of MW0 and start & end index */
967 switch (spec->codec_type) {
968 case VT1708B_8CH:
969 case VT1708B_4CH:
970 case VT1708S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800971 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800972 start_idx = 2;
973 end_idx = 4;
974 break;
975 case VT1702:
Lydia Wangf5271102009-10-10 19:07:35 +0800976 start_idx = 1;
977 end_idx = 3;
978 break;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800979 case VT1718S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800980 start_idx = 1;
981 end_idx = 3;
982 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800983 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800984 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800985 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800986 start_idx = 0;
987 end_idx = 2;
988 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800989 default:
990 return 0;
991 }
992 /* check AA path's mute status */
993 for (i = start_idx; i <= end_idx; i++) {
994 unsigned int con_list = snd_hda_codec_read(
Takashi Iwai620e2b22011-06-17 17:19:19 +0200995 codec, spec->aa_mix_nid, 0, AC_VERB_GET_CONNECT_LIST, i/4*4);
Lydia Wangf5271102009-10-10 19:07:35 +0800996 int shift = 8 * (i % 4);
997 hda_nid_t nid_pin = (con_list & (0xff << shift)) >> shift;
998 unsigned int defconf = snd_hda_codec_get_pincfg(codec, nid_pin);
999 if (get_defcfg_connect(defconf) == AC_JACK_PORT_COMPLEX) {
1000 /* check mute status while the pin is connected */
Takashi Iwai620e2b22011-06-17 17:19:19 +02001001 int mute_l = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 0,
Lydia Wangf5271102009-10-10 19:07:35 +08001002 HDA_INPUT, i) >> 7;
Takashi Iwai620e2b22011-06-17 17:19:19 +02001003 int mute_r = snd_hda_codec_amp_read(codec, spec->aa_mix_nid, 1,
Lydia Wangf5271102009-10-10 19:07:35 +08001004 HDA_INPUT, i) >> 7;
1005 if (!mute_l || !mute_r) {
1006 mute = 0;
1007 break;
1008 }
1009 }
1010 }
1011 return mute;
1012}
1013
1014/* enter/exit analog low-current mode */
1015static void analog_low_current_mode(struct hda_codec *codec, int stream_idle)
1016{
1017 struct via_spec *spec = codec->spec;
1018 static int saved_stream_idle = 1; /* saved stream idle status */
1019 int enable = is_aa_path_mute(codec);
1020 unsigned int verb = 0;
1021 unsigned int parm = 0;
1022
1023 if (stream_idle == -1) /* stream status did not change */
1024 enable = enable && saved_stream_idle;
1025 else {
1026 enable = enable && stream_idle;
1027 saved_stream_idle = stream_idle;
1028 }
1029
1030 /* decide low current mode's verb & parameter */
1031 switch (spec->codec_type) {
1032 case VT1708B_8CH:
1033 case VT1708B_4CH:
1034 verb = 0xf70;
1035 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1036 break;
1037 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001038 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001039 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001040 verb = 0xf73;
1041 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1042 break;
1043 case VT1702:
1044 verb = 0xf73;
1045 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1046 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001047 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001048 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +08001049 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +08001050 verb = 0xf93;
1051 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1052 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001053 default:
1054 return; /* other codecs are not supported */
1055 }
1056 /* send verb */
1057 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1058}
1059
Joseph Chanc577b8a2006-11-29 15:29:40 +01001060/*
1061 * generic initialization of ADC, input mixers and output mixers
1062 */
Takashi Iwai096a8852011-06-20 12:09:02 +02001063static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001064 /* power down jack detect function */
1065 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001066 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001067};
1068
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001069static void substream_set_idle(struct hda_codec *codec,
1070 struct snd_pcm_substream *substream)
1071{
1072 int idle = substream->pstr->substream_opened == 1
1073 && substream->ref_count == 0;
1074 analog_low_current_mode(codec, idle);
1075}
1076
Takashi Iwaiece8d042011-06-19 16:24:21 +02001077static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001078 struct hda_codec *codec,
1079 struct snd_pcm_substream *substream)
1080{
1081 struct via_spec *spec = codec->spec;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001082
1083 if (!spec->hp_independent_mode)
1084 spec->multiout.hp_nid = spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001085 substream_set_idle(codec, substream);
Takashi Iwai9a081602008-02-12 18:37:26 +01001086 return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1087 hinfo);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001088}
1089
Takashi Iwaiece8d042011-06-19 16:24:21 +02001090static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001091 struct hda_codec *codec,
1092 struct snd_pcm_substream *substream)
1093{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001094 struct via_spec *spec = codec->spec;
1095
1096 spec->multiout.hp_nid = 0;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001097 substream_set_idle(codec, substream);
Takashi Iwai9af74212011-06-18 16:17:45 +02001098 return 0;
1099}
1100
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001101static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1102 struct hda_codec *codec,
1103 struct snd_pcm_substream *substream)
1104{
1105 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001106
Takashi Iwaiece8d042011-06-19 16:24:21 +02001107 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001108 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001109 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1110 return -EBUSY;
1111 substream_set_idle(codec, substream);
1112 return 0;
1113}
1114
1115static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1116 struct hda_codec *codec,
1117 struct snd_pcm_substream *substream)
1118{
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001119 substream_set_idle(codec, substream);
1120 return 0;
1121}
1122
1123static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1124 struct hda_codec *codec,
1125 unsigned int stream_tag,
1126 unsigned int format,
1127 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001128{
1129 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001130
Takashi Iwaiece8d042011-06-19 16:24:21 +02001131 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1132 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001133 vt1708_start_hp_work(spec);
1134 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001135}
1136
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001137static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1138 struct hda_codec *codec,
1139 unsigned int stream_tag,
1140 unsigned int format,
1141 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001142{
1143 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001144
Takashi Iwaiece8d042011-06-19 16:24:21 +02001145 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1146 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001147 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001148 return 0;
1149}
1150
1151static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1152 struct hda_codec *codec,
1153 struct snd_pcm_substream *substream)
1154{
1155 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001156
Takashi Iwaiece8d042011-06-19 16:24:21 +02001157 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001158 vt1708_stop_hp_work(spec);
1159 return 0;
1160}
1161
1162static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1163 struct hda_codec *codec,
1164 struct snd_pcm_substream *substream)
1165{
1166 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001167
Takashi Iwaiece8d042011-06-19 16:24:21 +02001168 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001169 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001170 return 0;
1171}
1172
Joseph Chanc577b8a2006-11-29 15:29:40 +01001173/*
1174 * Digital out
1175 */
1176static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1177 struct hda_codec *codec,
1178 struct snd_pcm_substream *substream)
1179{
1180 struct via_spec *spec = codec->spec;
1181 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1182}
1183
1184static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1185 struct hda_codec *codec,
1186 struct snd_pcm_substream *substream)
1187{
1188 struct via_spec *spec = codec->spec;
1189 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1190}
1191
Harald Welte5691ec72008-09-15 22:42:26 +08001192static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001193 struct hda_codec *codec,
1194 unsigned int stream_tag,
1195 unsigned int format,
1196 struct snd_pcm_substream *substream)
1197{
1198 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001199 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1200 stream_tag, format, substream);
1201}
Harald Welte5691ec72008-09-15 22:42:26 +08001202
Takashi Iwai9da29272009-05-07 16:31:14 +02001203static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1204 struct hda_codec *codec,
1205 struct snd_pcm_substream *substream)
1206{
1207 struct via_spec *spec = codec->spec;
1208 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001209 return 0;
1210}
1211
Joseph Chanc577b8a2006-11-29 15:29:40 +01001212/*
1213 * Analog capture
1214 */
1215static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1216 struct hda_codec *codec,
1217 unsigned int stream_tag,
1218 unsigned int format,
1219 struct snd_pcm_substream *substream)
1220{
1221 struct via_spec *spec = codec->spec;
1222
1223 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1224 stream_tag, 0, format);
1225 return 0;
1226}
1227
1228static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1229 struct hda_codec *codec,
1230 struct snd_pcm_substream *substream)
1231{
1232 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001233 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001234 return 0;
1235}
1236
Takashi Iwai9af74212011-06-18 16:17:45 +02001237static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001238 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001239 .channels_min = 2,
1240 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001241 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001242 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001243 .open = via_playback_multi_pcm_open,
1244 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001245 .prepare = via_playback_multi_pcm_prepare,
1246 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001247 },
1248};
1249
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001250static const struct hda_pcm_stream via_pcm_hp_playback = {
1251 .substreams = 1,
1252 .channels_min = 2,
1253 .channels_max = 2,
1254 /* NID is set in via_build_pcms */
1255 .ops = {
1256 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001257 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001258 .prepare = via_playback_hp_pcm_prepare,
1259 .cleanup = via_playback_hp_pcm_cleanup
1260 },
1261};
1262
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001263static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001264 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001265 .channels_min = 2,
1266 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001267 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001268 /* We got noisy outputs on the right channel on VT1708 when
1269 * 24bit samples are used. Until any workaround is found,
1270 * disable the 24bit format, so far.
1271 */
1272 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1273 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001274 .open = via_playback_multi_pcm_open,
1275 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001276 .prepare = via_playback_multi_pcm_prepare,
1277 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001278 },
1279};
1280
Takashi Iwai9af74212011-06-18 16:17:45 +02001281static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001282 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001283 .channels_min = 2,
1284 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001285 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001286 .ops = {
1287 .prepare = via_capture_pcm_prepare,
1288 .cleanup = via_capture_pcm_cleanup
1289 },
1290};
1291
Takashi Iwai9af74212011-06-18 16:17:45 +02001292static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001293 .substreams = 1,
1294 .channels_min = 2,
1295 .channels_max = 2,
1296 /* NID is set in via_build_pcms */
1297 .ops = {
1298 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001299 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001300 .prepare = via_dig_playback_pcm_prepare,
1301 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001302 },
1303};
1304
Takashi Iwai9af74212011-06-18 16:17:45 +02001305static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001306 .substreams = 1,
1307 .channels_min = 2,
1308 .channels_max = 2,
1309};
1310
Takashi Iwai370bafb2011-06-20 12:47:45 +02001311/*
1312 * slave controls for virtual master
1313 */
1314static const char * const via_slave_vols[] = {
1315 "Front Playback Volume",
1316 "Surround Playback Volume",
1317 "Center Playback Volume",
1318 "LFE Playback Volume",
1319 "Side Playback Volume",
1320 "Headphone Playback Volume",
1321 "Speaker Playback Volume",
1322 NULL,
1323};
1324
1325static const char * const via_slave_sws[] = {
1326 "Front Playback Switch",
1327 "Surround Playback Switch",
1328 "Center Playback Switch",
1329 "LFE Playback Switch",
1330 "Side Playback Switch",
1331 "Headphone Playback Switch",
1332 "Speaker Playback Switch",
1333 NULL,
1334};
1335
Joseph Chanc577b8a2006-11-29 15:29:40 +01001336static int via_build_controls(struct hda_codec *codec)
1337{
1338 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001339 struct snd_kcontrol *kctl;
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001340 const struct snd_kcontrol_new *knew;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001341 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001342
Takashi Iwai24088a52011-06-17 16:59:21 +02001343 if (spec->set_widgets_power_state)
1344 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1345 return -ENOMEM;
1346
Joseph Chanc577b8a2006-11-29 15:29:40 +01001347 for (i = 0; i < spec->num_mixers; i++) {
1348 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1349 if (err < 0)
1350 return err;
1351 }
1352
1353 if (spec->multiout.dig_out_nid) {
1354 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001355 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001356 spec->multiout.dig_out_nid);
1357 if (err < 0)
1358 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001359 err = snd_hda_create_spdif_share_sw(codec,
1360 &spec->multiout);
1361 if (err < 0)
1362 return err;
1363 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001364 }
1365 if (spec->dig_in_nid) {
1366 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1367 if (err < 0)
1368 return err;
1369 }
Lydia Wang17314372009-10-10 19:07:37 +08001370
Takashi Iwai370bafb2011-06-20 12:47:45 +02001371 /* if we have no master control, let's create it */
1372 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1373 unsigned int vmaster_tlv[4];
1374 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1375 HDA_OUTPUT, vmaster_tlv);
1376 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1377 vmaster_tlv, via_slave_vols);
1378 if (err < 0)
1379 return err;
1380 }
1381 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1382 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1383 NULL, via_slave_sws);
1384 if (err < 0)
1385 return err;
1386 }
1387
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001388 /* assign Capture Source enums to NID */
1389 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1390 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001391 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001392 if (err < 0)
1393 return err;
1394 }
1395
1396 /* other nid->control mapping */
1397 for (i = 0; i < spec->num_mixers; i++) {
1398 for (knew = spec->mixers[i]; knew->name; knew++) {
1399 if (knew->iface != NID_MAPPING)
1400 continue;
1401 kctl = snd_hda_find_mixer_ctl(codec, knew->name);
1402 if (kctl == NULL)
1403 continue;
1404 err = snd_hda_add_nid(codec, kctl, 0,
1405 knew->subdevice);
1406 }
1407 }
1408
Lydia Wang17314372009-10-10 19:07:37 +08001409 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001410 set_widgets_power_state(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001411 analog_low_current_mode(codec, 1);
1412
Takashi Iwai603c4012008-07-30 15:01:44 +02001413 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001414 return 0;
1415}
1416
1417static int via_build_pcms(struct hda_codec *codec)
1418{
1419 struct via_spec *spec = codec->spec;
1420 struct hda_pcm *info = spec->pcm_rec;
1421
1422 codec->num_pcms = 1;
1423 codec->pcm_info = info;
1424
Takashi Iwai82673bc2011-06-17 16:24:21 +02001425 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1426 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001427 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001428
1429 if (!spec->stream_analog_playback)
1430 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001431 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001432 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001433 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1434 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001435 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1436 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001437
1438 if (!spec->stream_analog_capture)
1439 spec->stream_analog_capture = &via_pcm_analog_capture;
1440 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1441 *spec->stream_analog_capture;
1442 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1443 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1444 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001445
1446 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1447 codec->num_pcms++;
1448 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001449 snprintf(spec->stream_name_digital,
1450 sizeof(spec->stream_name_digital),
1451 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001452 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001453 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001454 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001455 if (!spec->stream_digital_playback)
1456 spec->stream_digital_playback =
1457 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001458 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001459 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001460 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1461 spec->multiout.dig_out_nid;
1462 }
1463 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001464 if (!spec->stream_digital_capture)
1465 spec->stream_digital_capture =
1466 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001467 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001468 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001469 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1470 spec->dig_in_nid;
1471 }
1472 }
1473
Takashi Iwaiece8d042011-06-19 16:24:21 +02001474 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001475 codec->num_pcms++;
1476 info++;
1477 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1478 "%s HP", codec->chip_name);
1479 info->name = spec->stream_name_hp;
1480 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1481 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001482 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001483 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001484 return 0;
1485}
1486
1487static void via_free(struct hda_codec *codec)
1488{
1489 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001490
1491 if (!spec)
1492 return;
1493
Takashi Iwai603c4012008-07-30 15:01:44 +02001494 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001495 vt1708_stop_hp_work(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001496 kfree(codec->spec);
1497}
1498
Takashi Iwai64be2852011-06-17 16:51:39 +02001499/* mute/unmute outputs */
1500static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1501 hda_nid_t *pins, bool mute)
1502{
1503 int i;
1504 for (i = 0; i < num_pins; i++)
1505 snd_hda_codec_write(codec, pins[i], 0,
1506 AC_VERB_SET_PIN_WIDGET_CONTROL,
1507 mute ? 0 : PIN_OUT);
1508}
1509
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001510/* mute internal speaker if line-out is plugged */
1511static void via_line_automute(struct hda_codec *codec, int present)
1512{
1513 struct via_spec *spec = codec->spec;
1514
1515 if (!spec->autocfg.speaker_outs)
1516 return;
1517 if (!present)
1518 present = snd_hda_jack_detect(codec,
1519 spec->autocfg.line_out_pins[0]);
1520 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1521 spec->autocfg.speaker_pins,
1522 present);
1523}
1524
Harald Welte69e52a82008-09-09 15:57:32 +08001525/* mute internal speaker if HP is plugged */
1526static void via_hp_automute(struct hda_codec *codec)
1527{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001528 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001529 struct via_spec *spec = codec->spec;
1530
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001531 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
1532 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai64be2852011-06-17 16:51:39 +02001533 toggle_output_mutes(codec, spec->autocfg.line_outs,
1534 spec->autocfg.line_out_pins,
1535 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001536 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001537 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001538}
1539
Harald Welte69e52a82008-09-09 15:57:32 +08001540static void via_gpio_control(struct hda_codec *codec)
1541{
1542 unsigned int gpio_data;
1543 unsigned int vol_counter;
1544 unsigned int vol;
1545 unsigned int master_vol;
1546
1547 struct via_spec *spec = codec->spec;
1548
1549 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1550 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1551
1552 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1553 0xF84, 0) & 0x3F0000) >> 16;
1554
1555 vol = vol_counter & 0x1F;
1556 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1557 AC_VERB_GET_AMP_GAIN_MUTE,
1558 AC_AMP_GET_INPUT);
1559
1560 if (gpio_data == 0x02) {
1561 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001562 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1563 AC_VERB_SET_PIN_WIDGET_CONTROL,
1564 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001565 if (vol_counter & 0x20) {
1566 /* decrease volume */
1567 if (vol > master_vol)
1568 vol = master_vol;
1569 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1570 0, HDA_AMP_VOLMASK,
1571 master_vol-vol);
1572 } else {
1573 /* increase volume */
1574 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1575 HDA_AMP_VOLMASK,
1576 ((master_vol+vol) > 0x2A) ? 0x2A :
1577 (master_vol+vol));
1578 }
1579 } else if (!(gpio_data & 0x02)) {
1580 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001581 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1582 AC_VERB_SET_PIN_WIDGET_CONTROL,
1583 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001584 }
1585}
1586
1587/* unsolicited event for jack sensing */
1588static void via_unsol_event(struct hda_codec *codec,
1589 unsigned int res)
1590{
1591 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001592
Lydia Wanga34df192009-10-10 19:08:01 +08001593 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001594 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001595
1596 res &= ~VIA_JACK_EVENT;
1597
1598 if (res == VIA_HP_EVENT)
1599 via_hp_automute(codec);
1600 else if (res == VIA_GPIO_EVENT)
1601 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001602 else if (res == VIA_LINE_EVENT)
1603 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001604}
1605
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001606#ifdef SND_HDA_NEEDS_RESUME
1607static int via_suspend(struct hda_codec *codec, pm_message_t state)
1608{
1609 struct via_spec *spec = codec->spec;
1610 vt1708_stop_hp_work(spec);
1611 return 0;
1612}
1613#endif
1614
Takashi Iwaicb53c622007-08-10 17:21:45 +02001615#ifdef CONFIG_SND_HDA_POWER_SAVE
1616static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1617{
1618 struct via_spec *spec = codec->spec;
1619 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1620}
1621#endif
1622
Joseph Chanc577b8a2006-11-29 15:29:40 +01001623/*
1624 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001625
1626static int via_init(struct hda_codec *codec);
1627
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001628static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001629 .build_controls = via_build_controls,
1630 .build_pcms = via_build_pcms,
1631 .init = via_init,
1632 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001633 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001634#ifdef SND_HDA_NEEDS_RESUME
1635 .suspend = via_suspend,
1636#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001637#ifdef CONFIG_SND_HDA_POWER_SAVE
1638 .check_power_status = via_check_power_status,
1639#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001640};
1641
Takashi Iwai4a796162011-06-17 17:53:38 +02001642static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001643{
Takashi Iwai4a796162011-06-17 17:53:38 +02001644 struct via_spec *spec = codec->spec;
1645 int i;
1646
1647 for (i = 0; i < spec->multiout.num_dacs; i++) {
1648 if (spec->multiout.dac_nids[i] == dac)
1649 return false;
1650 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001651 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001652 return false;
1653 return true;
1654}
1655
1656static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1657 hda_nid_t target_dac, struct nid_path *path,
1658 int depth, int wid_type)
1659{
1660 hda_nid_t conn[8];
1661 int i, nums;
1662
1663 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1664 for (i = 0; i < nums; i++) {
1665 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1666 continue;
1667 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1668 path->path[depth] = conn[i];
1669 path->idx[depth] = i;
1670 path->depth = ++depth;
1671 return true;
1672 }
1673 }
1674 if (depth > 4)
1675 return false;
1676 for (i = 0; i < nums; i++) {
1677 unsigned int type;
1678 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1679 if (type == AC_WID_AUD_OUT ||
1680 (wid_type != -1 && type != wid_type))
1681 continue;
1682 if (parse_output_path(codec, conn[i], target_dac,
1683 path, depth + 1, AC_WID_AUD_SEL)) {
1684 path->path[depth] = conn[i];
1685 path->idx[depth] = i;
1686 return true;
1687 }
1688 }
1689 return false;
1690}
1691
1692static int via_auto_fill_dac_nids(struct hda_codec *codec)
1693{
1694 struct via_spec *spec = codec->spec;
1695 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001696 int i;
1697 hda_nid_t nid;
1698
Joseph Chanc577b8a2006-11-29 15:29:40 +01001699 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001700 spec->multiout.num_dacs = cfg->line_outs;
1701 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001702 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001703 if (!nid)
1704 continue;
1705 if (parse_output_path(codec, nid, 0, &spec->out_path[i], 0, -1))
1706 spec->private_dac_nids[i] =
1707 spec->out_path[i].path[spec->out_path[i].depth - 1];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001708 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001709 return 0;
1710}
1711
Takashi Iwai4a796162011-06-17 17:53:38 +02001712static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
1713 hda_nid_t pin, hda_nid_t dac, int chs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001714{
Takashi Iwai4a796162011-06-17 17:53:38 +02001715 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001716 char name[32];
Takashi Iwai4a796162011-06-17 17:53:38 +02001717 hda_nid_t nid;
1718 int err;
1719
1720 if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
1721 nid = dac;
1722 else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
1723 nid = pin;
1724 else
1725 nid = 0;
1726 if (nid) {
1727 sprintf(name, "%s Playback Volume", pfx);
1728 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1729 HDA_COMPOSE_AMP_VAL(dac, chs, 0, HDA_OUTPUT));
1730 if (err < 0)
1731 return err;
1732 }
1733
1734 if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE)
1735 nid = dac;
1736 else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE)
1737 nid = pin;
1738 else
1739 nid = 0;
1740 if (nid) {
1741 sprintf(name, "%s Playback Switch", pfx);
1742 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1743 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1744 if (err < 0)
1745 return err;
1746 }
1747 return 0;
1748}
1749
Takashi Iwaif4a78282011-06-17 18:46:48 +02001750static void mangle_smart51(struct hda_codec *codec)
1751{
1752 struct via_spec *spec = codec->spec;
1753 struct auto_pin_cfg *cfg = &spec->autocfg;
1754 int i;
1755
1756 for (i = 0; i < cfg->num_inputs; i++) {
1757 if (!is_smart51_pins(codec, cfg->inputs[i].pin))
1758 continue;
1759 spec->can_smart51 = 1;
1760 cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin;
1761 if (cfg->line_outs == 3)
1762 break;
1763 }
1764}
1765
Takashi Iwai4a796162011-06-17 17:53:38 +02001766/* add playback controls from the parsed DAC table */
1767static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1768{
1769 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001770 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001771 static const char * const chname[4] = {
1772 "Front", "Surround", "C/LFE", "Side"
1773 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001774 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001775 int old_line_outs;
1776
1777 /* check smart51 */
1778 old_line_outs = cfg->line_outs;
1779 if (cfg->line_outs == 1)
1780 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001781
Takashi Iwai4a796162011-06-17 17:53:38 +02001782 for (i = 0; i < cfg->line_outs; i++) {
1783 hda_nid_t pin, dac;
1784 pin = cfg->line_out_pins[i];
1785 dac = spec->multiout.dac_nids[i];
1786 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001787 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001788 if (i == HDA_CLFE) {
Takashi Iwai4a796162011-06-17 17:53:38 +02001789 err = create_ch_ctls(codec, "Center", pin, dac, 1);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001790 if (err < 0)
1791 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02001792 err = create_ch_ctls(codec, "LFE", pin, dac, 2);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001793 if (err < 0)
1794 return err;
1795 } else {
Takashi Iwai4a796162011-06-17 17:53:38 +02001796 err = create_ch_ctls(codec, chname[i], pin, dac, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001797 if (err < 0)
1798 return err;
1799 }
1800 }
1801
Takashi Iwai4a796162011-06-17 17:53:38 +02001802 idx = get_connection_index(codec, spec->aa_mix_nid,
1803 spec->multiout.dac_nids[0]);
1804 if (idx >= 0) {
1805 /* add control to mixer */
1806 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1807 "PCM Playback Volume",
1808 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1809 idx, HDA_INPUT));
1810 if (err < 0)
1811 return err;
1812 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1813 "PCM Playback Switch",
1814 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1815 idx, HDA_INPUT));
1816 if (err < 0)
1817 return err;
1818 }
1819
Takashi Iwaif4a78282011-06-17 18:46:48 +02001820 cfg->line_outs = old_line_outs;
1821
Joseph Chanc577b8a2006-11-29 15:29:40 +01001822 return 0;
1823}
1824
Harald Welte0aa62ae2008-09-09 15:58:27 +08001825static void create_hp_imux(struct via_spec *spec)
1826{
1827 int i;
1828 struct hda_input_mux *imux = &spec->private_imux[1];
Takashi Iwaiea734962011-01-17 11:29:34 +01001829 static const char * const texts[] = { "OFF", "ON", NULL};
Harald Welte0aa62ae2008-09-09 15:58:27 +08001830
1831 /* for hp mode select */
Takashi Iwai10a20af2010-09-09 16:28:02 +02001832 for (i = 0; texts[i]; i++)
1833 snd_hda_add_imux_item(imux, texts[i], i, NULL);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001834
1835 spec->hp_mux = &spec->private_imux[1];
1836}
1837
Takashi Iwai4a796162011-06-17 17:53:38 +02001838static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001839{
Takashi Iwai4a796162011-06-17 17:53:38 +02001840 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001841 int err;
1842
1843 if (!pin)
1844 return 0;
1845
Takashi Iwai4a796162011-06-17 17:53:38 +02001846 if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001847 spec->hp_dac_nid = spec->hp_path.path[spec->hp_path.depth - 1];
Takashi Iwai4a796162011-06-17 17:53:38 +02001848 spec->hp_independent_mode_index =
1849 spec->hp_path.idx[spec->hp_path.depth - 1];
1850 create_hp_imux(spec);
1851 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001852
Takashi Iwaiece8d042011-06-19 16:24:21 +02001853 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
1854 &spec->hp_dep_path, 0, -1) &&
1855 !spec->hp_dac_nid)
1856 return 0;
1857
1858
1859 err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001860 if (err < 0)
1861 return err;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001862
Joseph Chanc577b8a2006-11-29 15:29:40 +01001863 return 0;
1864}
1865
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001866static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1867{
1868 struct via_spec *spec = codec->spec;
1869 hda_nid_t pin, dac;
1870
1871 pin = spec->autocfg.speaker_pins[0];
1872 if (!spec->autocfg.speaker_outs || !pin)
1873 return 0;
1874
1875 if (parse_output_path(codec, pin, 0, &spec->speaker_path, 0, -1)) {
1876 dac = spec->speaker_path.path[spec->speaker_path.depth - 1];
1877 spec->multiout.extra_out_nid[0] = dac;
1878 return create_ch_ctls(codec, "Speaker", pin, dac, 3);
1879 }
1880 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
1881 &spec->speaker_path, 0, -1))
1882 return create_ch_ctls(codec, "Headphone", pin, 0, 3);
1883
1884 return 0;
1885}
1886
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001887/* look for ADCs */
1888static int via_fill_adcs(struct hda_codec *codec)
1889{
1890 struct via_spec *spec = codec->spec;
1891 hda_nid_t nid = codec->start_nid;
1892 int i;
1893
1894 for (i = 0; i < codec->num_nodes; i++, nid++) {
1895 unsigned int wcaps = get_wcaps(codec, nid);
1896 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1897 continue;
1898 if (wcaps & AC_WCAP_DIGITAL)
1899 continue;
1900 if (!(wcaps & AC_WCAP_CONN_LIST))
1901 continue;
1902 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1903 return -ENOMEM;
1904 spec->adc_nids[spec->num_adc_nids++] = nid;
1905 }
1906 return 0;
1907}
1908
1909static int get_mux_nids(struct hda_codec *codec);
1910
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001911static const struct snd_kcontrol_new via_input_src_ctl = {
1912 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1913 /* The multiple "Capture Source" controls confuse alsamixer
1914 * So call somewhat different..
1915 */
1916 /* .name = "Capture Source", */
1917 .name = "Input Source",
1918 .info = via_mux_enum_info,
1919 .get = via_mux_enum_get,
1920 .put = via_mux_enum_put,
1921};
1922
Joseph Chanc577b8a2006-11-29 15:29:40 +01001923/* create playback/capture controls for input pins */
Takashi Iwai620e2b22011-06-17 17:19:19 +02001924static int via_auto_create_analog_input_ctls(struct hda_codec *codec,
1925 const struct auto_pin_cfg *cfg)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001926{
Takashi Iwai10a20af2010-09-09 16:28:02 +02001927 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001928 struct hda_input_mux *imux = &spec->private_imux[0];
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001929 int i, err, idx, idx2, type, type_idx = 0;
1930 hda_nid_t cap_nid;
1931 hda_nid_t pin_idxs[8];
1932 int num_idxs;
1933
1934 err = via_fill_adcs(codec);
1935 if (err < 0)
1936 return err;
1937 err = get_mux_nids(codec);
1938 if (err < 0)
1939 return err;
1940 cap_nid = spec->mux_nids[0];
1941
1942 num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
1943 ARRAY_SIZE(pin_idxs));
1944 if (num_idxs <= 0)
1945 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001946
1947 /* for internal loopback recording select */
Takashi Iwaif3268512010-08-30 11:00:19 +02001948 for (idx = 0; idx < num_idxs; idx++) {
Takashi Iwai620e2b22011-06-17 17:19:19 +02001949 if (pin_idxs[idx] == spec->aa_mix_nid) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001950 snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
Takashi Iwaif3268512010-08-30 11:00:19 +02001951 break;
1952 }
1953 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001954
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001955 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001956 const char *label;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001957 type = cfg->inputs[i].type;
Takashi Iwaif3268512010-08-30 11:00:19 +02001958 for (idx = 0; idx < num_idxs; idx++)
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001959 if (pin_idxs[idx] == cfg->inputs[i].pin)
Takashi Iwaif3268512010-08-30 11:00:19 +02001960 break;
1961 if (idx >= num_idxs)
1962 continue;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001963 if (i > 0 && type == cfg->inputs[i - 1].type)
1964 type_idx++;
1965 else
1966 type_idx = 0;
Takashi Iwai10a20af2010-09-09 16:28:02 +02001967 label = hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwai620e2b22011-06-17 17:19:19 +02001968 idx2 = get_connection_index(codec, spec->aa_mix_nid,
1969 pin_idxs[idx]);
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001970 if (idx2 >= 0)
Lydia Wang16922282011-03-22 16:24:10 +08001971 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwai620e2b22011-06-17 17:19:19 +02001972 idx2, spec->aa_mix_nid);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001973 if (err < 0)
1974 return err;
Takashi Iwai10a20af2010-09-09 16:28:02 +02001975 snd_hda_add_imux_item(imux, label, idx, NULL);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001976 }
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001977
1978 /* create capture mixer elements */
1979 for (i = 0; i < spec->num_adc_nids; i++) {
1980 hda_nid_t adc = spec->adc_nids[i];
1981 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
1982 "Capture Volume", i,
1983 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1984 HDA_INPUT));
1985 if (err < 0)
1986 return err;
1987 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1988 "Capture Switch", i,
1989 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1990 HDA_INPUT));
1991 if (err < 0)
1992 return err;
1993 }
1994
1995 /* input-source control */
1996 for (i = 0; i < spec->num_adc_nids; i++)
1997 if (!spec->mux_nids[i])
1998 break;
1999 if (i) {
2000 struct snd_kcontrol_new *knew;
2001 knew = via_clone_control(spec, &via_input_src_ctl);
2002 if (!knew)
2003 return -ENOMEM;
2004 knew->count = i;
2005 }
2006
2007 /* mic-boosts */
2008 for (i = 0; i < cfg->num_inputs; i++) {
2009 hda_nid_t pin = cfg->inputs[i].pin;
2010 unsigned int caps;
2011 const char *label;
2012 char name[32];
2013
2014 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2015 continue;
2016 caps = query_amp_caps(codec, pin, HDA_INPUT);
2017 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2018 continue;
2019 label = hda_get_autocfg_input_label(codec, cfg, i);
2020 snprintf(name, sizeof(name), "%s Boost Capture Volume", label);
2021 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2022 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2023 if (err < 0)
2024 return err;
2025 }
2026
Joseph Chanc577b8a2006-11-29 15:29:40 +01002027 return 0;
2028}
2029
Takashi Iwaicb53c622007-08-10 17:21:45 +02002030#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002031static const struct hda_amp_list vt1708_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02002032 { 0x17, HDA_INPUT, 1 },
2033 { 0x17, HDA_INPUT, 2 },
2034 { 0x17, HDA_INPUT, 3 },
2035 { 0x17, HDA_INPUT, 4 },
2036 { } /* end */
2037};
2038#endif
2039
Harald Welte76d9b0d2008-09-09 15:50:37 +08002040static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2041{
2042 unsigned int def_conf;
2043 unsigned char seqassoc;
2044
Takashi Iwai2f334f92009-02-20 14:37:42 +01002045 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002046 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2047 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002048 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2049 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2050 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2051 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002052 }
2053
2054 return;
2055}
2056
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002057static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002058 struct snd_ctl_elem_value *ucontrol)
2059{
2060 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2061 struct via_spec *spec = codec->spec;
2062
2063 if (spec->codec_type != VT1708)
2064 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002065 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002066 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002067 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002068 return 0;
2069}
2070
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002071static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002072 struct snd_ctl_elem_value *ucontrol)
2073{
2074 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2075 struct via_spec *spec = codec->spec;
2076 int change;
2077
2078 if (spec->codec_type != VT1708)
2079 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002080 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002081 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002082 == !spec->vt1708_jack_detect;
2083 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002084 mute_aa_path(codec, 1);
2085 notify_aa_path_ctls(codec);
2086 }
2087 return change;
2088}
2089
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002090static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2091 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2092 .name = "Jack Detect",
2093 .count = 1,
2094 .info = snd_ctl_boolean_mono_info,
2095 .get = vt1708_jack_detect_get,
2096 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002097};
2098
Takashi Iwai12daef62011-06-18 17:45:49 +02002099static void fill_dig_outs(struct hda_codec *codec);
2100static void fill_dig_in(struct hda_codec *codec);
2101
2102static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002103{
2104 struct via_spec *spec = codec->spec;
2105 int err;
2106
2107 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2108 if (err < 0)
2109 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002110 err = via_auto_fill_dac_nids(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002111 if (err < 0)
2112 return err;
2113 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002114 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002115
Takashi Iwai4a796162011-06-17 17:53:38 +02002116 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002117 if (err < 0)
2118 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002119 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002120 if (err < 0)
2121 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002122 err = via_auto_create_speaker_ctls(codec);
2123 if (err < 0)
2124 return err;
Takashi Iwai620e2b22011-06-17 17:19:19 +02002125 err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002126 if (err < 0)
2127 return err;
2128
2129 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2130
Takashi Iwai12daef62011-06-18 17:45:49 +02002131 fill_dig_outs(codec);
2132 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002133
Takashi Iwai603c4012008-07-30 15:01:44 +02002134 if (spec->kctls.list)
2135 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002136
Takashi Iwai096a8852011-06-20 12:09:02 +02002137 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002138
Harald Welte0aa62ae2008-09-09 15:58:27 +08002139 spec->input_mux = &spec->private_imux[0];
2140
Takashi Iwaiece8d042011-06-19 16:24:21 +02002141 if (spec->hp_mux) {
2142 err = via_hp_build(codec);
2143 if (err < 0)
2144 return err;
2145 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002146
Takashi Iwaif4a78282011-06-17 18:46:48 +02002147 err = via_smart51_build(codec);
2148 if (err < 0)
2149 return err;
2150
Takashi Iwai5d417622011-06-20 11:32:27 +02002151 /* assign slave outs */
2152 if (spec->slave_dig_outs[0])
2153 codec->slave_dig_outs = spec->slave_dig_outs;
2154
Joseph Chanc577b8a2006-11-29 15:29:40 +01002155 return 1;
2156}
2157
Takashi Iwai5d417622011-06-20 11:32:27 +02002158static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002159{
Lydia Wang25eaba22009-10-10 19:08:43 +08002160 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002161 if (spec->multiout.dig_out_nid)
2162 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2163 if (spec->slave_dig_outs[0])
2164 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2165}
Lydia Wang25eaba22009-10-10 19:08:43 +08002166
Takashi Iwai5d417622011-06-20 11:32:27 +02002167static void via_auto_init_dig_in(struct hda_codec *codec)
2168{
2169 struct via_spec *spec = codec->spec;
2170 if (!spec->dig_in_nid)
2171 return;
2172 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2173 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2174}
2175
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002176/* initialize the unsolicited events */
2177static void via_auto_init_unsol_event(struct hda_codec *codec)
2178{
2179 struct via_spec *spec = codec->spec;
2180 struct auto_pin_cfg *cfg = &spec->autocfg;
2181 unsigned int ev;
2182 int i;
2183
2184 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2185 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2186 AC_VERB_SET_UNSOLICITED_ENABLE,
2187 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2188
2189 if (cfg->speaker_pins[0])
2190 ev = VIA_LINE_EVENT;
2191 else
2192 ev = 0;
2193 for (i = 0; i < cfg->line_outs; i++) {
2194 if (cfg->line_out_pins[i] &&
2195 is_jack_detectable(codec, cfg->line_out_pins[i]))
2196 snd_hda_codec_write(codec, cfg->line_out_pins[0], 0,
2197 AC_VERB_SET_UNSOLICITED_ENABLE,
2198 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2199 }
2200
2201 for (i = 0; i < cfg->num_inputs; i++) {
2202 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2203 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2204 AC_VERB_SET_UNSOLICITED_ENABLE,
2205 AC_USRSP_EN | VIA_JACK_EVENT);
2206 }
2207}
2208
Takashi Iwai5d417622011-06-20 11:32:27 +02002209static int via_init(struct hda_codec *codec)
2210{
2211 struct via_spec *spec = codec->spec;
2212 int i;
2213
2214 for (i = 0; i < spec->num_iverbs; i++)
2215 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2216
Joseph Chanc577b8a2006-11-29 15:29:40 +01002217 via_auto_init_multi_out(codec);
2218 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002219 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002220 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002221 via_auto_init_dig_outs(codec);
2222 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002223
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002224 via_auto_init_unsol_event(codec);
2225
2226 via_hp_automute(codec);
2227 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002228
Joseph Chanc577b8a2006-11-29 15:29:40 +01002229 return 0;
2230}
2231
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002232static void vt1708_update_hp_jack_state(struct work_struct *work)
2233{
2234 struct via_spec *spec = container_of(work, struct via_spec,
2235 vt1708_hp_work.work);
2236 if (spec->codec_type != VT1708)
2237 return;
2238 /* if jack state toggled */
2239 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002240 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002241 spec->vt1708_hp_present ^= 1;
2242 via_hp_automute(spec->codec);
2243 }
2244 vt1708_start_hp_work(spec);
2245}
2246
Takashi Iwai337b9d02009-07-07 18:18:59 +02002247static int get_mux_nids(struct hda_codec *codec)
2248{
2249 struct via_spec *spec = codec->spec;
2250 hda_nid_t nid, conn[8];
2251 unsigned int type;
2252 int i, n;
2253
2254 for (i = 0; i < spec->num_adc_nids; i++) {
2255 nid = spec->adc_nids[i];
2256 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002257 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002258 if (type == AC_WID_PIN)
2259 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002260 n = snd_hda_get_connections(codec, nid, conn,
2261 ARRAY_SIZE(conn));
2262 if (n <= 0)
2263 break;
2264 if (n > 1) {
2265 spec->mux_nids[i] = nid;
2266 break;
2267 }
2268 nid = conn[0];
2269 }
2270 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002271 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002272}
2273
Joseph Chanc577b8a2006-11-29 15:29:40 +01002274static int patch_vt1708(struct hda_codec *codec)
2275{
2276 struct via_spec *spec;
2277 int err;
2278
2279 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002280 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002281 if (spec == NULL)
2282 return -ENOMEM;
2283
Takashi Iwai620e2b22011-06-17 17:19:19 +02002284 spec->aa_mix_nid = 0x17;
2285
Takashi Iwai12daef62011-06-18 17:45:49 +02002286 /* Add HP and CD pin config connect bit re-config action */
2287 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2288 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2289
Joseph Chanc577b8a2006-11-29 15:29:40 +01002290 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002291 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002292 if (err < 0) {
2293 via_free(codec);
2294 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002295 }
2296
Takashi Iwai12daef62011-06-18 17:45:49 +02002297 /* add jack detect on/off control */
2298 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2299 return -ENOMEM;
2300
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002301 /* disable 32bit format on VT1708 */
2302 if (codec->vendor_id == 0x11061708)
2303 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002304
Joseph Chanc577b8a2006-11-29 15:29:40 +01002305 codec->patch_ops = via_patch_ops;
2306
Takashi Iwaicb53c622007-08-10 17:21:45 +02002307#ifdef CONFIG_SND_HDA_POWER_SAVE
2308 spec->loopback.amplist = vt1708_loopbacks;
2309#endif
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002310 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002311 return 0;
2312}
2313
Joseph Chanc577b8a2006-11-29 15:29:40 +01002314/*
2315 * generic initialization of ADC, input mixers and output mixers
2316 */
Takashi Iwaicb53c622007-08-10 17:21:45 +02002317#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002318static const struct hda_amp_list vt1709_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02002319 { 0x18, HDA_INPUT, 1 },
2320 { 0x18, HDA_INPUT, 2 },
2321 { 0x18, HDA_INPUT, 3 },
2322 { 0x18, HDA_INPUT, 4 },
2323 { } /* end */
2324};
2325#endif
2326
Joseph Chanc577b8a2006-11-29 15:29:40 +01002327static int patch_vt1709_10ch(struct hda_codec *codec)
2328{
2329 struct via_spec *spec;
2330 int err;
2331
2332 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002333 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002334 if (spec == NULL)
2335 return -ENOMEM;
2336
Takashi Iwai620e2b22011-06-17 17:19:19 +02002337 spec->aa_mix_nid = 0x18;
2338
Takashi Iwai12daef62011-06-18 17:45:49 +02002339 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002340 if (err < 0) {
2341 via_free(codec);
2342 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002343 }
2344
Joseph Chanc577b8a2006-11-29 15:29:40 +01002345 codec->patch_ops = via_patch_ops;
2346
Takashi Iwaicb53c622007-08-10 17:21:45 +02002347#ifdef CONFIG_SND_HDA_POWER_SAVE
2348 spec->loopback.amplist = vt1709_loopbacks;
2349#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01002350
2351 return 0;
2352}
2353/*
2354 * generic initialization of ADC, input mixers and output mixers
2355 */
Joseph Chanc577b8a2006-11-29 15:29:40 +01002356static int patch_vt1709_6ch(struct hda_codec *codec)
2357{
2358 struct via_spec *spec;
2359 int err;
2360
2361 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002362 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002363 if (spec == NULL)
2364 return -ENOMEM;
2365
Takashi Iwai620e2b22011-06-17 17:19:19 +02002366 spec->aa_mix_nid = 0x18;
2367
Takashi Iwai12daef62011-06-18 17:45:49 +02002368 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002369 if (err < 0) {
2370 via_free(codec);
2371 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002372 }
2373
Joseph Chanc577b8a2006-11-29 15:29:40 +01002374 codec->patch_ops = via_patch_ops;
2375
Takashi Iwaicb53c622007-08-10 17:21:45 +02002376#ifdef CONFIG_SND_HDA_POWER_SAVE
2377 spec->loopback.amplist = vt1709_loopbacks;
2378#endif
Josepch Chanf7278fd2007-12-13 16:40:40 +01002379 return 0;
2380}
2381
Josepch Chanf7278fd2007-12-13 16:40:40 +01002382/*
2383 * generic initialization of ADC, input mixers and output mixers
2384 */
Josepch Chanf7278fd2007-12-13 16:40:40 +01002385#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002386static const struct hda_amp_list vt1708B_loopbacks[] = {
Josepch Chanf7278fd2007-12-13 16:40:40 +01002387 { 0x16, HDA_INPUT, 1 },
2388 { 0x16, HDA_INPUT, 2 },
2389 { 0x16, HDA_INPUT, 3 },
2390 { 0x16, HDA_INPUT, 4 },
2391 { } /* end */
2392};
2393#endif
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002394
2395static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2396{
2397 struct via_spec *spec = codec->spec;
2398 int imux_is_smixer;
2399 unsigned int parm;
2400 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002401 if ((spec->codec_type != VT1708B_4CH) &&
2402 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002403 is_8ch = 1;
2404
2405 /* SW0 (17h) = stereo mixer */
2406 imux_is_smixer =
2407 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2408 == ((spec->codec_type == VT1708S) ? 5 : 0));
2409 /* inputs */
2410 /* PW 1/2/5 (1ah/1bh/1eh) */
2411 parm = AC_PWRST_D3;
2412 set_pin_power_state(codec, 0x1a, &parm);
2413 set_pin_power_state(codec, 0x1b, &parm);
2414 set_pin_power_state(codec, 0x1e, &parm);
2415 if (imux_is_smixer)
2416 parm = AC_PWRST_D0;
2417 /* SW0 (17h), AIW 0/1 (13h/14h) */
2418 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2419 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2420 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2421
2422 /* outputs */
2423 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2424 parm = AC_PWRST_D3;
2425 set_pin_power_state(codec, 0x19, &parm);
2426 if (spec->smart51_enabled)
2427 set_pin_power_state(codec, 0x1b, &parm);
2428 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2429 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2430
2431 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2432 if (is_8ch) {
2433 parm = AC_PWRST_D3;
2434 set_pin_power_state(codec, 0x22, &parm);
2435 if (spec->smart51_enabled)
2436 set_pin_power_state(codec, 0x1a, &parm);
2437 snd_hda_codec_write(codec, 0x26, 0,
2438 AC_VERB_SET_POWER_STATE, parm);
2439 snd_hda_codec_write(codec, 0x24, 0,
2440 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002441 } else if (codec->vendor_id == 0x11064397) {
2442 /* PW7(23h), SW2(27h), AOW2(25h) */
2443 parm = AC_PWRST_D3;
2444 set_pin_power_state(codec, 0x23, &parm);
2445 if (spec->smart51_enabled)
2446 set_pin_power_state(codec, 0x1a, &parm);
2447 snd_hda_codec_write(codec, 0x27, 0,
2448 AC_VERB_SET_POWER_STATE, parm);
2449 snd_hda_codec_write(codec, 0x25, 0,
2450 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002451 }
2452
2453 /* PW 3/4/7 (1ch/1dh/23h) */
2454 parm = AC_PWRST_D3;
2455 /* force to D0 for internal Speaker */
2456 set_pin_power_state(codec, 0x1c, &parm);
2457 set_pin_power_state(codec, 0x1d, &parm);
2458 if (is_8ch)
2459 set_pin_power_state(codec, 0x23, &parm);
2460
2461 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2462 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2463 imux_is_smixer ? AC_PWRST_D0 : parm);
2464 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2465 if (is_8ch) {
2466 snd_hda_codec_write(codec, 0x25, 0,
2467 AC_VERB_SET_POWER_STATE, parm);
2468 snd_hda_codec_write(codec, 0x27, 0,
2469 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002470 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2471 snd_hda_codec_write(codec, 0x25, 0,
2472 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002473}
2474
Lydia Wang518bf3b2009-10-10 19:07:29 +08002475static int patch_vt1708S(struct hda_codec *codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002476static int patch_vt1708B_8ch(struct hda_codec *codec)
2477{
2478 struct via_spec *spec;
2479 int err;
2480
Lydia Wang518bf3b2009-10-10 19:07:29 +08002481 if (get_codec_type(codec) == VT1708BCE)
2482 return patch_vt1708S(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002483 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002484 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002485 if (spec == NULL)
2486 return -ENOMEM;
2487
Takashi Iwai620e2b22011-06-17 17:19:19 +02002488 spec->aa_mix_nid = 0x16;
2489
Josepch Chanf7278fd2007-12-13 16:40:40 +01002490 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002491 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002492 if (err < 0) {
2493 via_free(codec);
2494 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002495 }
2496
Josepch Chanf7278fd2007-12-13 16:40:40 +01002497 codec->patch_ops = via_patch_ops;
2498
Josepch Chanf7278fd2007-12-13 16:40:40 +01002499#ifdef CONFIG_SND_HDA_POWER_SAVE
2500 spec->loopback.amplist = vt1708B_loopbacks;
2501#endif
2502
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002503 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2504
Josepch Chanf7278fd2007-12-13 16:40:40 +01002505 return 0;
2506}
2507
2508static int patch_vt1708B_4ch(struct hda_codec *codec)
2509{
2510 struct via_spec *spec;
2511 int err;
2512
2513 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002514 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002515 if (spec == NULL)
2516 return -ENOMEM;
2517
Josepch Chanf7278fd2007-12-13 16:40:40 +01002518 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002519 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002520 if (err < 0) {
2521 via_free(codec);
2522 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002523 }
2524
Josepch Chanf7278fd2007-12-13 16:40:40 +01002525 codec->patch_ops = via_patch_ops;
2526
Josepch Chanf7278fd2007-12-13 16:40:40 +01002527#ifdef CONFIG_SND_HDA_POWER_SAVE
2528 spec->loopback.amplist = vt1708B_loopbacks;
2529#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01002530
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002531 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2532
Joseph Chanc577b8a2006-11-29 15:29:40 +01002533 return 0;
2534}
2535
Harald Welted949cac2008-09-09 15:56:01 +08002536/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002537static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002538 /* Enable Mic Boost Volume backdoor */
2539 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002540 /* don't bybass mixer */
2541 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002542 { }
2543};
2544
Takashi Iwai9da29272009-05-07 16:31:14 +02002545/* fill out digital output widgets; one for master and one for slave outputs */
2546static void fill_dig_outs(struct hda_codec *codec)
2547{
2548 struct via_spec *spec = codec->spec;
2549 int i;
2550
2551 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2552 hda_nid_t nid;
2553 int conn;
2554
2555 nid = spec->autocfg.dig_out_pins[i];
2556 if (!nid)
2557 continue;
2558 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2559 if (conn < 1)
2560 continue;
2561 if (!spec->multiout.dig_out_nid)
2562 spec->multiout.dig_out_nid = nid;
2563 else {
2564 spec->slave_dig_outs[0] = nid;
2565 break; /* at most two dig outs */
2566 }
2567 }
2568}
2569
Takashi Iwai12daef62011-06-18 17:45:49 +02002570static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002571{
2572 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002573 hda_nid_t dig_nid;
2574 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002575
Takashi Iwai12daef62011-06-18 17:45:49 +02002576 if (!spec->autocfg.dig_in_pin)
2577 return;
Harald Welted949cac2008-09-09 15:56:01 +08002578
Takashi Iwai12daef62011-06-18 17:45:49 +02002579 dig_nid = codec->start_nid;
2580 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2581 unsigned int wcaps = get_wcaps(codec, dig_nid);
2582 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2583 continue;
2584 if (!(wcaps & AC_WCAP_DIGITAL))
2585 continue;
2586 if (!(wcaps & AC_WCAP_CONN_LIST))
2587 continue;
2588 err = get_connection_index(codec, dig_nid,
2589 spec->autocfg.dig_in_pin);
2590 if (err >= 0) {
2591 spec->dig_in_nid = dig_nid;
2592 break;
2593 }
2594 }
Harald Welted949cac2008-09-09 15:56:01 +08002595}
2596
2597#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002598static const struct hda_amp_list vt1708S_loopbacks[] = {
Harald Welted949cac2008-09-09 15:56:01 +08002599 { 0x16, HDA_INPUT, 1 },
2600 { 0x16, HDA_INPUT, 2 },
2601 { 0x16, HDA_INPUT, 3 },
2602 { 0x16, HDA_INPUT, 4 },
2603 { } /* end */
2604};
2605#endif
2606
Lydia Wang6369bcf2009-10-10 19:08:31 +08002607static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2608 int offset, int num_steps, int step_size)
2609{
2610 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2611 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2612 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2613 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2614 (0 << AC_AMPCAP_MUTE_SHIFT));
2615}
2616
Harald Welted949cac2008-09-09 15:56:01 +08002617static int patch_vt1708S(struct hda_codec *codec)
2618{
2619 struct via_spec *spec;
2620 int err;
2621
2622 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002623 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002624 if (spec == NULL)
2625 return -ENOMEM;
2626
Takashi Iwai620e2b22011-06-17 17:19:19 +02002627 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002628 override_mic_boost(codec, 0x1a, 0, 3, 40);
2629 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002630
Harald Welted949cac2008-09-09 15:56:01 +08002631 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002632 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002633 if (err < 0) {
2634 via_free(codec);
2635 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002636 }
2637
Takashi Iwai096a8852011-06-20 12:09:02 +02002638 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002639
Harald Welted949cac2008-09-09 15:56:01 +08002640 codec->patch_ops = via_patch_ops;
2641
Harald Welted949cac2008-09-09 15:56:01 +08002642#ifdef CONFIG_SND_HDA_POWER_SAVE
2643 spec->loopback.amplist = vt1708S_loopbacks;
2644#endif
2645
Lydia Wang518bf3b2009-10-10 19:07:29 +08002646 /* correct names for VT1708BCE */
2647 if (get_codec_type(codec) == VT1708BCE) {
2648 kfree(codec->chip_name);
2649 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2650 snprintf(codec->bus->card->mixername,
2651 sizeof(codec->bus->card->mixername),
2652 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002653 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002654 /* correct names for VT1705 */
2655 if (codec->vendor_id == 0x11064397) {
2656 kfree(codec->chip_name);
2657 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2658 snprintf(codec->bus->card->mixername,
2659 sizeof(codec->bus->card->mixername),
2660 "%s %s", codec->vendor_name, codec->chip_name);
2661 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002662 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002663 return 0;
2664}
2665
2666/* Patch for VT1702 */
2667
Takashi Iwai096a8852011-06-20 12:09:02 +02002668static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002669 /* mixer enable */
2670 {0x1, 0xF88, 0x3},
2671 /* GPIO 0~2 */
2672 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002673 { }
2674};
2675
Harald Welted949cac2008-09-09 15:56:01 +08002676#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002677static const struct hda_amp_list vt1702_loopbacks[] = {
Harald Welted949cac2008-09-09 15:56:01 +08002678 { 0x1A, HDA_INPUT, 1 },
2679 { 0x1A, HDA_INPUT, 2 },
2680 { 0x1A, HDA_INPUT, 3 },
2681 { 0x1A, HDA_INPUT, 4 },
2682 { } /* end */
2683};
2684#endif
2685
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002686static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2687{
2688 int imux_is_smixer =
2689 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2690 unsigned int parm;
2691 /* inputs */
2692 /* PW 1/2/5 (14h/15h/18h) */
2693 parm = AC_PWRST_D3;
2694 set_pin_power_state(codec, 0x14, &parm);
2695 set_pin_power_state(codec, 0x15, &parm);
2696 set_pin_power_state(codec, 0x18, &parm);
2697 if (imux_is_smixer)
2698 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2699 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2700 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2701 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2702 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2703 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2704
2705 /* outputs */
2706 /* PW 3/4 (16h/17h) */
2707 parm = AC_PWRST_D3;
2708 set_pin_power_state(codec, 0x17, &parm);
2709 set_pin_power_state(codec, 0x16, &parm);
2710 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2711 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2712 imux_is_smixer ? AC_PWRST_D0 : parm);
2713 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2714 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2715}
2716
Harald Welted949cac2008-09-09 15:56:01 +08002717static int patch_vt1702(struct hda_codec *codec)
2718{
2719 struct via_spec *spec;
2720 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002721
2722 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002723 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002724 if (spec == NULL)
2725 return -ENOMEM;
2726
Takashi Iwai620e2b22011-06-17 17:19:19 +02002727 spec->aa_mix_nid = 0x1a;
2728
Takashi Iwai12daef62011-06-18 17:45:49 +02002729 /* limit AA path volume to 0 dB */
2730 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2731 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2732 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2733 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2734 (1 << AC_AMPCAP_MUTE_SHIFT));
2735
Harald Welted949cac2008-09-09 15:56:01 +08002736 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002737 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002738 if (err < 0) {
2739 via_free(codec);
2740 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002741 }
2742
Takashi Iwai096a8852011-06-20 12:09:02 +02002743 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002744
Harald Welted949cac2008-09-09 15:56:01 +08002745 codec->patch_ops = via_patch_ops;
2746
Harald Welted949cac2008-09-09 15:56:01 +08002747#ifdef CONFIG_SND_HDA_POWER_SAVE
2748 spec->loopback.amplist = vt1702_loopbacks;
2749#endif
2750
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002751 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002752 return 0;
2753}
2754
Lydia Wangeb7188c2009-10-10 19:08:34 +08002755/* Patch for VT1718S */
2756
Takashi Iwai096a8852011-06-20 12:09:02 +02002757static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002758 /* Enable MW0 adjust Gain 5 */
2759 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002760 /* Enable Boost Volume backdoor */
2761 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002762
Lydia Wangeb7188c2009-10-10 19:08:34 +08002763 { }
2764};
2765
Lydia Wangeb7188c2009-10-10 19:08:34 +08002766#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002767static const struct hda_amp_list vt1718S_loopbacks[] = {
Lydia Wangeb7188c2009-10-10 19:08:34 +08002768 { 0x21, HDA_INPUT, 1 },
2769 { 0x21, HDA_INPUT, 2 },
2770 { 0x21, HDA_INPUT, 3 },
2771 { 0x21, HDA_INPUT, 4 },
2772 { } /* end */
2773};
2774#endif
2775
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002776static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2777{
2778 struct via_spec *spec = codec->spec;
2779 int imux_is_smixer;
2780 unsigned int parm;
2781 /* MUX6 (1eh) = stereo mixer */
2782 imux_is_smixer =
2783 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2784 /* inputs */
2785 /* PW 5/6/7 (29h/2ah/2bh) */
2786 parm = AC_PWRST_D3;
2787 set_pin_power_state(codec, 0x29, &parm);
2788 set_pin_power_state(codec, 0x2a, &parm);
2789 set_pin_power_state(codec, 0x2b, &parm);
2790 if (imux_is_smixer)
2791 parm = AC_PWRST_D0;
2792 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2793 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2794 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2795 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2796 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2797
2798 /* outputs */
2799 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2800 parm = AC_PWRST_D3;
2801 set_pin_power_state(codec, 0x27, &parm);
2802 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2803 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2804
2805 /* PW2 (26h), AOW2 (ah) */
2806 parm = AC_PWRST_D3;
2807 set_pin_power_state(codec, 0x26, &parm);
2808 if (spec->smart51_enabled)
2809 set_pin_power_state(codec, 0x2b, &parm);
2810 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2811
2812 /* PW0 (24h), AOW0 (8h) */
2813 parm = AC_PWRST_D3;
2814 set_pin_power_state(codec, 0x24, &parm);
2815 if (!spec->hp_independent_mode) /* check for redirected HP */
2816 set_pin_power_state(codec, 0x28, &parm);
2817 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2818 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2819 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2820 imux_is_smixer ? AC_PWRST_D0 : parm);
2821
2822 /* PW1 (25h), AOW1 (9h) */
2823 parm = AC_PWRST_D3;
2824 set_pin_power_state(codec, 0x25, &parm);
2825 if (spec->smart51_enabled)
2826 set_pin_power_state(codec, 0x2a, &parm);
2827 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2828
2829 if (spec->hp_independent_mode) {
2830 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2831 parm = AC_PWRST_D3;
2832 set_pin_power_state(codec, 0x28, &parm);
2833 snd_hda_codec_write(codec, 0x1b, 0,
2834 AC_VERB_SET_POWER_STATE, parm);
2835 snd_hda_codec_write(codec, 0x34, 0,
2836 AC_VERB_SET_POWER_STATE, parm);
2837 snd_hda_codec_write(codec, 0xc, 0,
2838 AC_VERB_SET_POWER_STATE, parm);
2839 }
2840}
2841
Lydia Wangeb7188c2009-10-10 19:08:34 +08002842static int patch_vt1718S(struct hda_codec *codec)
2843{
2844 struct via_spec *spec;
2845 int err;
2846
2847 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002848 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002849 if (spec == NULL)
2850 return -ENOMEM;
2851
Takashi Iwai620e2b22011-06-17 17:19:19 +02002852 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002853 override_mic_boost(codec, 0x2b, 0, 3, 40);
2854 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002855
Lydia Wangeb7188c2009-10-10 19:08:34 +08002856 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002857 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002858 if (err < 0) {
2859 via_free(codec);
2860 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002861 }
2862
Takashi Iwai096a8852011-06-20 12:09:02 +02002863 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002864
Lydia Wangeb7188c2009-10-10 19:08:34 +08002865 codec->patch_ops = via_patch_ops;
2866
Lydia Wangeb7188c2009-10-10 19:08:34 +08002867#ifdef CONFIG_SND_HDA_POWER_SAVE
2868 spec->loopback.amplist = vt1718S_loopbacks;
2869#endif
2870
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002871 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2872
Lydia Wangeb7188c2009-10-10 19:08:34 +08002873 return 0;
2874}
Lydia Wangf3db4232009-10-10 19:08:41 +08002875
2876/* Patch for VT1716S */
2877
2878static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2879 struct snd_ctl_elem_info *uinfo)
2880{
2881 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2882 uinfo->count = 1;
2883 uinfo->value.integer.min = 0;
2884 uinfo->value.integer.max = 1;
2885 return 0;
2886}
2887
2888static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2889 struct snd_ctl_elem_value *ucontrol)
2890{
2891 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2892 int index = 0;
2893
2894 index = snd_hda_codec_read(codec, 0x26, 0,
2895 AC_VERB_GET_CONNECT_SEL, 0);
2896 if (index != -1)
2897 *ucontrol->value.integer.value = index;
2898
2899 return 0;
2900}
2901
2902static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2903 struct snd_ctl_elem_value *ucontrol)
2904{
2905 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2906 struct via_spec *spec = codec->spec;
2907 int index = *ucontrol->value.integer.value;
2908
2909 snd_hda_codec_write(codec, 0x26, 0,
2910 AC_VERB_SET_CONNECT_SEL, index);
2911 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002912 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002913 return 1;
2914}
2915
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002916static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002917 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2918 {
2919 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2920 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002921 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08002922 .count = 1,
2923 .info = vt1716s_dmic_info,
2924 .get = vt1716s_dmic_get,
2925 .put = vt1716s_dmic_put,
2926 },
2927 {} /* end */
2928};
2929
2930
2931/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002932static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002933 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
2934 { } /* end */
2935};
2936
Takashi Iwai096a8852011-06-20 12:09:02 +02002937static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002938 /* Enable Boost Volume backdoor */
2939 {0x1, 0xf8a, 0x80},
2940 /* don't bybass mixer */
2941 {0x1, 0xf88, 0xc0},
2942 /* Enable mono output */
2943 {0x1, 0xf90, 0x08},
2944 { }
2945};
2946
Lydia Wangf3db4232009-10-10 19:08:41 +08002947#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002948static const struct hda_amp_list vt1716S_loopbacks[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002949 { 0x16, HDA_INPUT, 1 },
2950 { 0x16, HDA_INPUT, 2 },
2951 { 0x16, HDA_INPUT, 3 },
2952 { 0x16, HDA_INPUT, 4 },
2953 { } /* end */
2954};
2955#endif
2956
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002957static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
2958{
2959 struct via_spec *spec = codec->spec;
2960 int imux_is_smixer;
2961 unsigned int parm;
2962 unsigned int mono_out, present;
2963 /* SW0 (17h) = stereo mixer */
2964 imux_is_smixer =
2965 (snd_hda_codec_read(codec, 0x17, 0,
2966 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
2967 /* inputs */
2968 /* PW 1/2/5 (1ah/1bh/1eh) */
2969 parm = AC_PWRST_D3;
2970 set_pin_power_state(codec, 0x1a, &parm);
2971 set_pin_power_state(codec, 0x1b, &parm);
2972 set_pin_power_state(codec, 0x1e, &parm);
2973 if (imux_is_smixer)
2974 parm = AC_PWRST_D0;
2975 /* SW0 (17h), AIW0(13h) */
2976 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2977 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2978
2979 parm = AC_PWRST_D3;
2980 set_pin_power_state(codec, 0x1e, &parm);
2981 /* PW11 (22h) */
2982 if (spec->dmic_enabled)
2983 set_pin_power_state(codec, 0x22, &parm);
2984 else
2985 snd_hda_codec_write(codec, 0x22, 0,
2986 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
2987
2988 /* SW2(26h), AIW1(14h) */
2989 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
2990 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2991
2992 /* outputs */
2993 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2994 parm = AC_PWRST_D3;
2995 set_pin_power_state(codec, 0x19, &parm);
2996 /* Smart 5.1 PW2(1bh) */
2997 if (spec->smart51_enabled)
2998 set_pin_power_state(codec, 0x1b, &parm);
2999 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3000 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3001
3002 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3003 parm = AC_PWRST_D3;
3004 set_pin_power_state(codec, 0x23, &parm);
3005 /* Smart 5.1 PW1(1ah) */
3006 if (spec->smart51_enabled)
3007 set_pin_power_state(codec, 0x1a, &parm);
3008 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3009
3010 /* Smart 5.1 PW5(1eh) */
3011 if (spec->smart51_enabled)
3012 set_pin_power_state(codec, 0x1e, &parm);
3013 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3014
3015 /* Mono out */
3016 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3017 present = snd_hda_jack_detect(codec, 0x1c);
3018
3019 if (present)
3020 mono_out = 0;
3021 else {
3022 present = snd_hda_jack_detect(codec, 0x1d);
3023 if (!spec->hp_independent_mode && present)
3024 mono_out = 0;
3025 else
3026 mono_out = 1;
3027 }
3028 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3029 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3030 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3031 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3032
3033 /* PW 3/4 (1ch/1dh) */
3034 parm = AC_PWRST_D3;
3035 set_pin_power_state(codec, 0x1c, &parm);
3036 set_pin_power_state(codec, 0x1d, &parm);
3037 /* HP Independent Mode, power on AOW3 */
3038 if (spec->hp_independent_mode)
3039 snd_hda_codec_write(codec, 0x25, 0,
3040 AC_VERB_SET_POWER_STATE, parm);
3041
3042 /* force to D0 for internal Speaker */
3043 /* MW0 (16h), AOW0 (10h) */
3044 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3045 imux_is_smixer ? AC_PWRST_D0 : parm);
3046 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3047 mono_out ? AC_PWRST_D0 : parm);
3048}
3049
Lydia Wangf3db4232009-10-10 19:08:41 +08003050static int patch_vt1716S(struct hda_codec *codec)
3051{
3052 struct via_spec *spec;
3053 int err;
3054
3055 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003056 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003057 if (spec == NULL)
3058 return -ENOMEM;
3059
Takashi Iwai620e2b22011-06-17 17:19:19 +02003060 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003061 override_mic_boost(codec, 0x1a, 0, 3, 40);
3062 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003063
Lydia Wangf3db4232009-10-10 19:08:41 +08003064 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003065 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003066 if (err < 0) {
3067 via_free(codec);
3068 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003069 }
3070
Takashi Iwai096a8852011-06-20 12:09:02 +02003071 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003072
Lydia Wangf3db4232009-10-10 19:08:41 +08003073 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3074 spec->num_mixers++;
3075
3076 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3077
3078 codec->patch_ops = via_patch_ops;
3079
Lydia Wangf3db4232009-10-10 19:08:41 +08003080#ifdef CONFIG_SND_HDA_POWER_SAVE
3081 spec->loopback.amplist = vt1716S_loopbacks;
3082#endif
3083
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003084 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003085 return 0;
3086}
Lydia Wang25eaba22009-10-10 19:08:43 +08003087
3088/* for vt2002P */
3089
Takashi Iwai096a8852011-06-20 12:09:02 +02003090static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003091 /* Class-D speaker related verbs */
3092 {0x1, 0xfe0, 0x4},
3093 {0x1, 0xfe9, 0x80},
3094 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003095 /* Enable Boost Volume backdoor */
3096 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003097 /* Enable AOW0 to MW9 */
3098 {0x1, 0xfb8, 0x88},
3099 { }
3100};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003101
Takashi Iwai096a8852011-06-20 12:09:02 +02003102static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003103 /* Enable Boost Volume backdoor */
3104 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003105 /* Enable AOW0 to MW9 */
3106 {0x1, 0xfb8, 0x88},
3107 { }
3108};
Lydia Wang25eaba22009-10-10 19:08:43 +08003109
Lydia Wang25eaba22009-10-10 19:08:43 +08003110#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003111static const struct hda_amp_list vt2002P_loopbacks[] = {
Lydia Wang25eaba22009-10-10 19:08:43 +08003112 { 0x21, HDA_INPUT, 0 },
3113 { 0x21, HDA_INPUT, 1 },
3114 { 0x21, HDA_INPUT, 2 },
3115 { } /* end */
3116};
3117#endif
3118
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003119static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3120{
3121 struct via_spec *spec = codec->spec;
3122 int imux_is_smixer;
3123 unsigned int parm;
3124 unsigned int present;
3125 /* MUX9 (1eh) = stereo mixer */
3126 imux_is_smixer =
3127 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3128 /* inputs */
3129 /* PW 5/6/7 (29h/2ah/2bh) */
3130 parm = AC_PWRST_D3;
3131 set_pin_power_state(codec, 0x29, &parm);
3132 set_pin_power_state(codec, 0x2a, &parm);
3133 set_pin_power_state(codec, 0x2b, &parm);
3134 parm = AC_PWRST_D0;
3135 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3136 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3137 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3138 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3139 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3140
3141 /* outputs */
3142 /* AOW0 (8h)*/
3143 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3144
Lydia Wang118909562011-03-23 17:57:34 +08003145 if (spec->codec_type == VT1802) {
3146 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3147 parm = AC_PWRST_D3;
3148 set_pin_power_state(codec, 0x28, &parm);
3149 snd_hda_codec_write(codec, 0x18, 0,
3150 AC_VERB_SET_POWER_STATE, parm);
3151 snd_hda_codec_write(codec, 0x38, 0,
3152 AC_VERB_SET_POWER_STATE, parm);
3153 } else {
3154 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3155 parm = AC_PWRST_D3;
3156 set_pin_power_state(codec, 0x26, &parm);
3157 snd_hda_codec_write(codec, 0x1c, 0,
3158 AC_VERB_SET_POWER_STATE, parm);
3159 snd_hda_codec_write(codec, 0x37, 0,
3160 AC_VERB_SET_POWER_STATE, parm);
3161 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003162
Lydia Wang118909562011-03-23 17:57:34 +08003163 if (spec->codec_type == VT1802) {
3164 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3165 parm = AC_PWRST_D3;
3166 set_pin_power_state(codec, 0x25, &parm);
3167 snd_hda_codec_write(codec, 0x15, 0,
3168 AC_VERB_SET_POWER_STATE, parm);
3169 snd_hda_codec_write(codec, 0x35, 0,
3170 AC_VERB_SET_POWER_STATE, parm);
3171 } else {
3172 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3173 parm = AC_PWRST_D3;
3174 set_pin_power_state(codec, 0x25, &parm);
3175 snd_hda_codec_write(codec, 0x19, 0,
3176 AC_VERB_SET_POWER_STATE, parm);
3177 snd_hda_codec_write(codec, 0x35, 0,
3178 AC_VERB_SET_POWER_STATE, parm);
3179 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003180
3181 if (spec->hp_independent_mode)
3182 snd_hda_codec_write(codec, 0x9, 0,
3183 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3184
3185 /* Class-D */
3186 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3187 present = snd_hda_jack_detect(codec, 0x25);
3188
3189 parm = AC_PWRST_D3;
3190 set_pin_power_state(codec, 0x24, &parm);
3191 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003192 if (spec->codec_type == VT1802)
3193 snd_hda_codec_write(codec, 0x14, 0,
3194 AC_VERB_SET_POWER_STATE, parm);
3195 else
3196 snd_hda_codec_write(codec, 0x18, 0,
3197 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003198 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3199
3200 /* Mono Out */
3201 present = snd_hda_jack_detect(codec, 0x26);
3202
3203 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003204 if (spec->codec_type == VT1802) {
3205 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3206 snd_hda_codec_write(codec, 0x33, 0,
3207 AC_VERB_SET_POWER_STATE, parm);
3208 snd_hda_codec_write(codec, 0x1c, 0,
3209 AC_VERB_SET_POWER_STATE, parm);
3210 snd_hda_codec_write(codec, 0x3c, 0,
3211 AC_VERB_SET_POWER_STATE, parm);
3212 } else {
3213 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3214 snd_hda_codec_write(codec, 0x31, 0,
3215 AC_VERB_SET_POWER_STATE, parm);
3216 snd_hda_codec_write(codec, 0x17, 0,
3217 AC_VERB_SET_POWER_STATE, parm);
3218 snd_hda_codec_write(codec, 0x3b, 0,
3219 AC_VERB_SET_POWER_STATE, parm);
3220 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003221 /* MW9 (21h) */
3222 if (imux_is_smixer || !is_aa_path_mute(codec))
3223 snd_hda_codec_write(codec, 0x21, 0,
3224 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3225 else
3226 snd_hda_codec_write(codec, 0x21, 0,
3227 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3228}
Lydia Wang25eaba22009-10-10 19:08:43 +08003229
3230/* patch for vt2002P */
3231static int patch_vt2002P(struct hda_codec *codec)
3232{
3233 struct via_spec *spec;
3234 int err;
3235
3236 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003237 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003238 if (spec == NULL)
3239 return -ENOMEM;
3240
Takashi Iwai620e2b22011-06-17 17:19:19 +02003241 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003242 override_mic_boost(codec, 0x2b, 0, 3, 40);
3243 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003244
Lydia Wang25eaba22009-10-10 19:08:43 +08003245 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003246 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003247 if (err < 0) {
3248 via_free(codec);
3249 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003250 }
3251
Lydia Wang118909562011-03-23 17:57:34 +08003252 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003253 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003254 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003255 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003256
Lydia Wang25eaba22009-10-10 19:08:43 +08003257 codec->patch_ops = via_patch_ops;
3258
Lydia Wang25eaba22009-10-10 19:08:43 +08003259#ifdef CONFIG_SND_HDA_POWER_SAVE
3260 spec->loopback.amplist = vt2002P_loopbacks;
3261#endif
3262
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003263 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003264 return 0;
3265}
Lydia Wangab6734e2009-10-10 19:08:46 +08003266
3267/* for vt1812 */
3268
Takashi Iwai096a8852011-06-20 12:09:02 +02003269static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003270 /* Enable Boost Volume backdoor */
3271 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003272 /* Enable AOW0 to MW9 */
3273 {0x1, 0xfb8, 0xa8},
3274 { }
3275};
3276
Lydia Wangab6734e2009-10-10 19:08:46 +08003277#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003278static const struct hda_amp_list vt1812_loopbacks[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003279 { 0x21, HDA_INPUT, 0 },
3280 { 0x21, HDA_INPUT, 1 },
3281 { 0x21, HDA_INPUT, 2 },
3282 { } /* end */
3283};
3284#endif
3285
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003286static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3287{
3288 struct via_spec *spec = codec->spec;
3289 int imux_is_smixer =
3290 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3291 unsigned int parm;
3292 unsigned int present;
3293 /* MUX10 (1eh) = stereo mixer */
3294 imux_is_smixer =
3295 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3296 /* inputs */
3297 /* PW 5/6/7 (29h/2ah/2bh) */
3298 parm = AC_PWRST_D3;
3299 set_pin_power_state(codec, 0x29, &parm);
3300 set_pin_power_state(codec, 0x2a, &parm);
3301 set_pin_power_state(codec, 0x2b, &parm);
3302 parm = AC_PWRST_D0;
3303 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3304 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3305 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3306 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3307 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3308
3309 /* outputs */
3310 /* AOW0 (8h)*/
3311 snd_hda_codec_write(codec, 0x8, 0,
3312 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3313
3314 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3315 parm = AC_PWRST_D3;
3316 set_pin_power_state(codec, 0x28, &parm);
3317 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3318 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3319
3320 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3321 parm = AC_PWRST_D3;
3322 set_pin_power_state(codec, 0x25, &parm);
3323 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3324 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3325 if (spec->hp_independent_mode)
3326 snd_hda_codec_write(codec, 0x9, 0,
3327 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3328
3329 /* Internal Speaker */
3330 /* PW0 (24h), MW0(14h), MUX0(34h) */
3331 present = snd_hda_jack_detect(codec, 0x25);
3332
3333 parm = AC_PWRST_D3;
3334 set_pin_power_state(codec, 0x24, &parm);
3335 if (present) {
3336 snd_hda_codec_write(codec, 0x14, 0,
3337 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3338 snd_hda_codec_write(codec, 0x34, 0,
3339 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3340 } else {
3341 snd_hda_codec_write(codec, 0x14, 0,
3342 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3343 snd_hda_codec_write(codec, 0x34, 0,
3344 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3345 }
3346
3347
3348 /* Mono Out */
3349 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3350 present = snd_hda_jack_detect(codec, 0x28);
3351
3352 parm = AC_PWRST_D3;
3353 set_pin_power_state(codec, 0x31, &parm);
3354 if (present) {
3355 snd_hda_codec_write(codec, 0x1c, 0,
3356 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3357 snd_hda_codec_write(codec, 0x3c, 0,
3358 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3359 snd_hda_codec_write(codec, 0x3e, 0,
3360 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3361 } else {
3362 snd_hda_codec_write(codec, 0x1c, 0,
3363 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3364 snd_hda_codec_write(codec, 0x3c, 0,
3365 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3366 snd_hda_codec_write(codec, 0x3e, 0,
3367 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3368 }
3369
3370 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3371 parm = AC_PWRST_D3;
3372 set_pin_power_state(codec, 0x33, &parm);
3373 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3374 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3375
3376}
Lydia Wangab6734e2009-10-10 19:08:46 +08003377
3378/* patch for vt1812 */
3379static int patch_vt1812(struct hda_codec *codec)
3380{
3381 struct via_spec *spec;
3382 int err;
3383
3384 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003385 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003386 if (spec == NULL)
3387 return -ENOMEM;
3388
Takashi Iwai620e2b22011-06-17 17:19:19 +02003389 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003390 override_mic_boost(codec, 0x2b, 0, 3, 40);
3391 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003392
Lydia Wangab6734e2009-10-10 19:08:46 +08003393 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003394 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003395 if (err < 0) {
3396 via_free(codec);
3397 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003398 }
3399
Takashi Iwai096a8852011-06-20 12:09:02 +02003400 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003401
Lydia Wangab6734e2009-10-10 19:08:46 +08003402 codec->patch_ops = via_patch_ops;
3403
Lydia Wangab6734e2009-10-10 19:08:46 +08003404#ifdef CONFIG_SND_HDA_POWER_SAVE
3405 spec->loopback.amplist = vt1812_loopbacks;
3406#endif
3407
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003408 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003409 return 0;
3410}
3411
Joseph Chanc577b8a2006-11-29 15:29:40 +01003412/*
3413 * patch entries
3414 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003415static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003416 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3417 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3418 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3419 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3420 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003421 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003422 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003423 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003424 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003425 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003426 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003427 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003428 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003429 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003430 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003431 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003432 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003433 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003434 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003435 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003436 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003437 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003438 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003439 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003440 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003441 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003442 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003443 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003444 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003445 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003446 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003447 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003448 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003449 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003450 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003451 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003452 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003453 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003454 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003455 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003456 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003457 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003458 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003459 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003460 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003461 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003462 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003463 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003464 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003465 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003466 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003467 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003468 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003469 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003470 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003471 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003472 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003473 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003474 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003475 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003476 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003477 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003478 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003479 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003480 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003481 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003482 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003483 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003484 { .id = 0x11060428, .name = "VT1718S",
3485 .patch = patch_vt1718S},
3486 { .id = 0x11064428, .name = "VT1718S",
3487 .patch = patch_vt1718S},
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08003488 { .id = 0x11060441, .name = "VT2020",
3489 .patch = patch_vt1718S},
3490 { .id = 0x11064441, .name = "VT1828S",
3491 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003492 { .id = 0x11060433, .name = "VT1716S",
3493 .patch = patch_vt1716S},
3494 { .id = 0x1106a721, .name = "VT1716S",
3495 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003496 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3497 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003498 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003499 { .id = 0x11060440, .name = "VT1818S",
3500 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003501 { .id = 0x11060446, .name = "VT1802",
3502 .patch = patch_vt2002P},
3503 { .id = 0x11068446, .name = "VT1802",
3504 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003505 {} /* terminator */
3506};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003507
3508MODULE_ALIAS("snd-hda-codec-id:1106*");
3509
3510static struct hda_codec_preset_list via_list = {
3511 .preset = snd_hda_preset_via,
3512 .owner = THIS_MODULE,
3513};
3514
3515MODULE_LICENSE("GPL");
3516MODULE_DESCRIPTION("VIA HD-audio codec");
3517
3518static int __init patch_via_init(void)
3519{
3520 return snd_hda_add_codec_preset(&via_list);
3521}
3522
3523static void __exit patch_via_exit(void)
3524{
3525 snd_hda_delete_codec_preset(&via_list);
3526}
3527
3528module_init(patch_via_init)
3529module_exit(patch_via_exit)