blob: 1edcd3221c98b550b8449bfb40b3642ce5077d20 [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
1311static int via_build_controls(struct hda_codec *codec)
1312{
1313 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001314 struct snd_kcontrol *kctl;
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001315 const struct snd_kcontrol_new *knew;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001316 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001317
Takashi Iwai24088a52011-06-17 16:59:21 +02001318 if (spec->set_widgets_power_state)
1319 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1320 return -ENOMEM;
1321
Joseph Chanc577b8a2006-11-29 15:29:40 +01001322 for (i = 0; i < spec->num_mixers; i++) {
1323 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1324 if (err < 0)
1325 return err;
1326 }
1327
1328 if (spec->multiout.dig_out_nid) {
1329 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001330 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001331 spec->multiout.dig_out_nid);
1332 if (err < 0)
1333 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001334 err = snd_hda_create_spdif_share_sw(codec,
1335 &spec->multiout);
1336 if (err < 0)
1337 return err;
1338 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001339 }
1340 if (spec->dig_in_nid) {
1341 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1342 if (err < 0)
1343 return err;
1344 }
Lydia Wang17314372009-10-10 19:07:37 +08001345
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001346 /* assign Capture Source enums to NID */
1347 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1348 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001349 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001350 if (err < 0)
1351 return err;
1352 }
1353
1354 /* other nid->control mapping */
1355 for (i = 0; i < spec->num_mixers; i++) {
1356 for (knew = spec->mixers[i]; knew->name; knew++) {
1357 if (knew->iface != NID_MAPPING)
1358 continue;
1359 kctl = snd_hda_find_mixer_ctl(codec, knew->name);
1360 if (kctl == NULL)
1361 continue;
1362 err = snd_hda_add_nid(codec, kctl, 0,
1363 knew->subdevice);
1364 }
1365 }
1366
Lydia Wang17314372009-10-10 19:07:37 +08001367 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001368 set_widgets_power_state(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001369 analog_low_current_mode(codec, 1);
1370
Takashi Iwai603c4012008-07-30 15:01:44 +02001371 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001372 return 0;
1373}
1374
1375static int via_build_pcms(struct hda_codec *codec)
1376{
1377 struct via_spec *spec = codec->spec;
1378 struct hda_pcm *info = spec->pcm_rec;
1379
1380 codec->num_pcms = 1;
1381 codec->pcm_info = info;
1382
Takashi Iwai82673bc2011-06-17 16:24:21 +02001383 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1384 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001385 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001386
1387 if (!spec->stream_analog_playback)
1388 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001389 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001390 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001391 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1392 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001393 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1394 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001395
1396 if (!spec->stream_analog_capture)
1397 spec->stream_analog_capture = &via_pcm_analog_capture;
1398 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1399 *spec->stream_analog_capture;
1400 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1401 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1402 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001403
1404 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1405 codec->num_pcms++;
1406 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001407 snprintf(spec->stream_name_digital,
1408 sizeof(spec->stream_name_digital),
1409 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001410 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001411 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001412 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001413 if (!spec->stream_digital_playback)
1414 spec->stream_digital_playback =
1415 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001416 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001417 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001418 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1419 spec->multiout.dig_out_nid;
1420 }
1421 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001422 if (!spec->stream_digital_capture)
1423 spec->stream_digital_capture =
1424 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001425 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001426 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001427 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1428 spec->dig_in_nid;
1429 }
1430 }
1431
Takashi Iwaiece8d042011-06-19 16:24:21 +02001432 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001433 codec->num_pcms++;
1434 info++;
1435 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1436 "%s HP", codec->chip_name);
1437 info->name = spec->stream_name_hp;
1438 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1439 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001440 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001441 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001442 return 0;
1443}
1444
1445static void via_free(struct hda_codec *codec)
1446{
1447 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001448
1449 if (!spec)
1450 return;
1451
Takashi Iwai603c4012008-07-30 15:01:44 +02001452 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001453 vt1708_stop_hp_work(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001454 kfree(codec->spec);
1455}
1456
Takashi Iwai64be2852011-06-17 16:51:39 +02001457/* mute/unmute outputs */
1458static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1459 hda_nid_t *pins, bool mute)
1460{
1461 int i;
1462 for (i = 0; i < num_pins; i++)
1463 snd_hda_codec_write(codec, pins[i], 0,
1464 AC_VERB_SET_PIN_WIDGET_CONTROL,
1465 mute ? 0 : PIN_OUT);
1466}
1467
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001468/* mute internal speaker if line-out is plugged */
1469static void via_line_automute(struct hda_codec *codec, int present)
1470{
1471 struct via_spec *spec = codec->spec;
1472
1473 if (!spec->autocfg.speaker_outs)
1474 return;
1475 if (!present)
1476 present = snd_hda_jack_detect(codec,
1477 spec->autocfg.line_out_pins[0]);
1478 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1479 spec->autocfg.speaker_pins,
1480 present);
1481}
1482
Harald Welte69e52a82008-09-09 15:57:32 +08001483/* mute internal speaker if HP is plugged */
1484static void via_hp_automute(struct hda_codec *codec)
1485{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001486 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001487 struct via_spec *spec = codec->spec;
1488
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001489 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
1490 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai64be2852011-06-17 16:51:39 +02001491 toggle_output_mutes(codec, spec->autocfg.line_outs,
1492 spec->autocfg.line_out_pins,
1493 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001494 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001495 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001496}
1497
Harald Welte69e52a82008-09-09 15:57:32 +08001498static void via_gpio_control(struct hda_codec *codec)
1499{
1500 unsigned int gpio_data;
1501 unsigned int vol_counter;
1502 unsigned int vol;
1503 unsigned int master_vol;
1504
1505 struct via_spec *spec = codec->spec;
1506
1507 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1508 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1509
1510 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1511 0xF84, 0) & 0x3F0000) >> 16;
1512
1513 vol = vol_counter & 0x1F;
1514 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1515 AC_VERB_GET_AMP_GAIN_MUTE,
1516 AC_AMP_GET_INPUT);
1517
1518 if (gpio_data == 0x02) {
1519 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001520 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1521 AC_VERB_SET_PIN_WIDGET_CONTROL,
1522 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001523 if (vol_counter & 0x20) {
1524 /* decrease volume */
1525 if (vol > master_vol)
1526 vol = master_vol;
1527 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1528 0, HDA_AMP_VOLMASK,
1529 master_vol-vol);
1530 } else {
1531 /* increase volume */
1532 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1533 HDA_AMP_VOLMASK,
1534 ((master_vol+vol) > 0x2A) ? 0x2A :
1535 (master_vol+vol));
1536 }
1537 } else if (!(gpio_data & 0x02)) {
1538 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001539 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1540 AC_VERB_SET_PIN_WIDGET_CONTROL,
1541 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001542 }
1543}
1544
1545/* unsolicited event for jack sensing */
1546static void via_unsol_event(struct hda_codec *codec,
1547 unsigned int res)
1548{
1549 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001550
Lydia Wanga34df192009-10-10 19:08:01 +08001551 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001552 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001553
1554 res &= ~VIA_JACK_EVENT;
1555
1556 if (res == VIA_HP_EVENT)
1557 via_hp_automute(codec);
1558 else if (res == VIA_GPIO_EVENT)
1559 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001560 else if (res == VIA_LINE_EVENT)
1561 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001562}
1563
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001564#ifdef SND_HDA_NEEDS_RESUME
1565static int via_suspend(struct hda_codec *codec, pm_message_t state)
1566{
1567 struct via_spec *spec = codec->spec;
1568 vt1708_stop_hp_work(spec);
1569 return 0;
1570}
1571#endif
1572
Takashi Iwaicb53c622007-08-10 17:21:45 +02001573#ifdef CONFIG_SND_HDA_POWER_SAVE
1574static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1575{
1576 struct via_spec *spec = codec->spec;
1577 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1578}
1579#endif
1580
Joseph Chanc577b8a2006-11-29 15:29:40 +01001581/*
1582 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001583
1584static int via_init(struct hda_codec *codec);
1585
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001586static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001587 .build_controls = via_build_controls,
1588 .build_pcms = via_build_pcms,
1589 .init = via_init,
1590 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001591 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001592#ifdef SND_HDA_NEEDS_RESUME
1593 .suspend = via_suspend,
1594#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001595#ifdef CONFIG_SND_HDA_POWER_SAVE
1596 .check_power_status = via_check_power_status,
1597#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001598};
1599
Takashi Iwai4a796162011-06-17 17:53:38 +02001600static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001601{
Takashi Iwai4a796162011-06-17 17:53:38 +02001602 struct via_spec *spec = codec->spec;
1603 int i;
1604
1605 for (i = 0; i < spec->multiout.num_dacs; i++) {
1606 if (spec->multiout.dac_nids[i] == dac)
1607 return false;
1608 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001609 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001610 return false;
1611 return true;
1612}
1613
1614static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1615 hda_nid_t target_dac, struct nid_path *path,
1616 int depth, int wid_type)
1617{
1618 hda_nid_t conn[8];
1619 int i, nums;
1620
1621 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1622 for (i = 0; i < nums; i++) {
1623 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1624 continue;
1625 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1626 path->path[depth] = conn[i];
1627 path->idx[depth] = i;
1628 path->depth = ++depth;
1629 return true;
1630 }
1631 }
1632 if (depth > 4)
1633 return false;
1634 for (i = 0; i < nums; i++) {
1635 unsigned int type;
1636 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1637 if (type == AC_WID_AUD_OUT ||
1638 (wid_type != -1 && type != wid_type))
1639 continue;
1640 if (parse_output_path(codec, conn[i], target_dac,
1641 path, depth + 1, AC_WID_AUD_SEL)) {
1642 path->path[depth] = conn[i];
1643 path->idx[depth] = i;
1644 return true;
1645 }
1646 }
1647 return false;
1648}
1649
1650static int via_auto_fill_dac_nids(struct hda_codec *codec)
1651{
1652 struct via_spec *spec = codec->spec;
1653 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001654 int i;
1655 hda_nid_t nid;
1656
Joseph Chanc577b8a2006-11-29 15:29:40 +01001657 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001658 spec->multiout.num_dacs = cfg->line_outs;
1659 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001660 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001661 if (!nid)
1662 continue;
1663 if (parse_output_path(codec, nid, 0, &spec->out_path[i], 0, -1))
1664 spec->private_dac_nids[i] =
1665 spec->out_path[i].path[spec->out_path[i].depth - 1];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001666 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001667 return 0;
1668}
1669
Takashi Iwai4a796162011-06-17 17:53:38 +02001670static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
1671 hda_nid_t pin, hda_nid_t dac, int chs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001672{
Takashi Iwai4a796162011-06-17 17:53:38 +02001673 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001674 char name[32];
Takashi Iwai4a796162011-06-17 17:53:38 +02001675 hda_nid_t nid;
1676 int err;
1677
1678 if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
1679 nid = dac;
1680 else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
1681 nid = pin;
1682 else
1683 nid = 0;
1684 if (nid) {
1685 sprintf(name, "%s Playback Volume", pfx);
1686 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1687 HDA_COMPOSE_AMP_VAL(dac, chs, 0, HDA_OUTPUT));
1688 if (err < 0)
1689 return err;
1690 }
1691
1692 if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE)
1693 nid = dac;
1694 else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE)
1695 nid = pin;
1696 else
1697 nid = 0;
1698 if (nid) {
1699 sprintf(name, "%s Playback Switch", pfx);
1700 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1701 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1702 if (err < 0)
1703 return err;
1704 }
1705 return 0;
1706}
1707
Takashi Iwaif4a78282011-06-17 18:46:48 +02001708static void mangle_smart51(struct hda_codec *codec)
1709{
1710 struct via_spec *spec = codec->spec;
1711 struct auto_pin_cfg *cfg = &spec->autocfg;
1712 int i;
1713
1714 for (i = 0; i < cfg->num_inputs; i++) {
1715 if (!is_smart51_pins(codec, cfg->inputs[i].pin))
1716 continue;
1717 spec->can_smart51 = 1;
1718 cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin;
1719 if (cfg->line_outs == 3)
1720 break;
1721 }
1722}
1723
Takashi Iwai4a796162011-06-17 17:53:38 +02001724/* add playback controls from the parsed DAC table */
1725static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1726{
1727 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001728 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001729 static const char * const chname[4] = {
1730 "Front", "Surround", "C/LFE", "Side"
1731 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001732 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001733 int old_line_outs;
1734
1735 /* check smart51 */
1736 old_line_outs = cfg->line_outs;
1737 if (cfg->line_outs == 1)
1738 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001739
Takashi Iwai4a796162011-06-17 17:53:38 +02001740 for (i = 0; i < cfg->line_outs; i++) {
1741 hda_nid_t pin, dac;
1742 pin = cfg->line_out_pins[i];
1743 dac = spec->multiout.dac_nids[i];
1744 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001745 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001746 if (i == HDA_CLFE) {
Takashi Iwai4a796162011-06-17 17:53:38 +02001747 err = create_ch_ctls(codec, "Center", pin, dac, 1);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001748 if (err < 0)
1749 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02001750 err = create_ch_ctls(codec, "LFE", pin, dac, 2);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001751 if (err < 0)
1752 return err;
1753 } else {
Takashi Iwai4a796162011-06-17 17:53:38 +02001754 err = create_ch_ctls(codec, chname[i], pin, dac, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001755 if (err < 0)
1756 return err;
1757 }
1758 }
1759
Takashi Iwai4a796162011-06-17 17:53:38 +02001760 idx = get_connection_index(codec, spec->aa_mix_nid,
1761 spec->multiout.dac_nids[0]);
1762 if (idx >= 0) {
1763 /* add control to mixer */
1764 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1765 "PCM Playback Volume",
1766 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1767 idx, HDA_INPUT));
1768 if (err < 0)
1769 return err;
1770 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1771 "PCM Playback Switch",
1772 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1773 idx, HDA_INPUT));
1774 if (err < 0)
1775 return err;
1776 }
1777
Takashi Iwaif4a78282011-06-17 18:46:48 +02001778 cfg->line_outs = old_line_outs;
1779
Joseph Chanc577b8a2006-11-29 15:29:40 +01001780 return 0;
1781}
1782
Harald Welte0aa62ae2008-09-09 15:58:27 +08001783static void create_hp_imux(struct via_spec *spec)
1784{
1785 int i;
1786 struct hda_input_mux *imux = &spec->private_imux[1];
Takashi Iwaiea734962011-01-17 11:29:34 +01001787 static const char * const texts[] = { "OFF", "ON", NULL};
Harald Welte0aa62ae2008-09-09 15:58:27 +08001788
1789 /* for hp mode select */
Takashi Iwai10a20af2010-09-09 16:28:02 +02001790 for (i = 0; texts[i]; i++)
1791 snd_hda_add_imux_item(imux, texts[i], i, NULL);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001792
1793 spec->hp_mux = &spec->private_imux[1];
1794}
1795
Takashi Iwai4a796162011-06-17 17:53:38 +02001796static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001797{
Takashi Iwai4a796162011-06-17 17:53:38 +02001798 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001799 int err;
1800
1801 if (!pin)
1802 return 0;
1803
Takashi Iwai4a796162011-06-17 17:53:38 +02001804 if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001805 spec->hp_dac_nid = spec->hp_path.path[spec->hp_path.depth - 1];
Takashi Iwai4a796162011-06-17 17:53:38 +02001806 spec->hp_independent_mode_index =
1807 spec->hp_path.idx[spec->hp_path.depth - 1];
1808 create_hp_imux(spec);
1809 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001810
Takashi Iwaiece8d042011-06-19 16:24:21 +02001811 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
1812 &spec->hp_dep_path, 0, -1) &&
1813 !spec->hp_dac_nid)
1814 return 0;
1815
1816
1817 err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001818 if (err < 0)
1819 return err;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001820
Joseph Chanc577b8a2006-11-29 15:29:40 +01001821 return 0;
1822}
1823
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001824static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1825{
1826 struct via_spec *spec = codec->spec;
1827 hda_nid_t pin, dac;
1828
1829 pin = spec->autocfg.speaker_pins[0];
1830 if (!spec->autocfg.speaker_outs || !pin)
1831 return 0;
1832
1833 if (parse_output_path(codec, pin, 0, &spec->speaker_path, 0, -1)) {
1834 dac = spec->speaker_path.path[spec->speaker_path.depth - 1];
1835 spec->multiout.extra_out_nid[0] = dac;
1836 return create_ch_ctls(codec, "Speaker", pin, dac, 3);
1837 }
1838 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
1839 &spec->speaker_path, 0, -1))
1840 return create_ch_ctls(codec, "Headphone", pin, 0, 3);
1841
1842 return 0;
1843}
1844
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001845/* look for ADCs */
1846static int via_fill_adcs(struct hda_codec *codec)
1847{
1848 struct via_spec *spec = codec->spec;
1849 hda_nid_t nid = codec->start_nid;
1850 int i;
1851
1852 for (i = 0; i < codec->num_nodes; i++, nid++) {
1853 unsigned int wcaps = get_wcaps(codec, nid);
1854 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1855 continue;
1856 if (wcaps & AC_WCAP_DIGITAL)
1857 continue;
1858 if (!(wcaps & AC_WCAP_CONN_LIST))
1859 continue;
1860 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1861 return -ENOMEM;
1862 spec->adc_nids[spec->num_adc_nids++] = nid;
1863 }
1864 return 0;
1865}
1866
1867static int get_mux_nids(struct hda_codec *codec);
1868
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001869static const struct snd_kcontrol_new via_input_src_ctl = {
1870 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1871 /* The multiple "Capture Source" controls confuse alsamixer
1872 * So call somewhat different..
1873 */
1874 /* .name = "Capture Source", */
1875 .name = "Input Source",
1876 .info = via_mux_enum_info,
1877 .get = via_mux_enum_get,
1878 .put = via_mux_enum_put,
1879};
1880
Joseph Chanc577b8a2006-11-29 15:29:40 +01001881/* create playback/capture controls for input pins */
Takashi Iwai620e2b22011-06-17 17:19:19 +02001882static int via_auto_create_analog_input_ctls(struct hda_codec *codec,
1883 const struct auto_pin_cfg *cfg)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001884{
Takashi Iwai10a20af2010-09-09 16:28:02 +02001885 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001886 struct hda_input_mux *imux = &spec->private_imux[0];
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001887 int i, err, idx, idx2, type, type_idx = 0;
1888 hda_nid_t cap_nid;
1889 hda_nid_t pin_idxs[8];
1890 int num_idxs;
1891
1892 err = via_fill_adcs(codec);
1893 if (err < 0)
1894 return err;
1895 err = get_mux_nids(codec);
1896 if (err < 0)
1897 return err;
1898 cap_nid = spec->mux_nids[0];
1899
1900 num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
1901 ARRAY_SIZE(pin_idxs));
1902 if (num_idxs <= 0)
1903 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001904
1905 /* for internal loopback recording select */
Takashi Iwaif3268512010-08-30 11:00:19 +02001906 for (idx = 0; idx < num_idxs; idx++) {
Takashi Iwai620e2b22011-06-17 17:19:19 +02001907 if (pin_idxs[idx] == spec->aa_mix_nid) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001908 snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
Takashi Iwaif3268512010-08-30 11:00:19 +02001909 break;
1910 }
1911 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001912
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001913 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001914 const char *label;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001915 type = cfg->inputs[i].type;
Takashi Iwaif3268512010-08-30 11:00:19 +02001916 for (idx = 0; idx < num_idxs; idx++)
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001917 if (pin_idxs[idx] == cfg->inputs[i].pin)
Takashi Iwaif3268512010-08-30 11:00:19 +02001918 break;
1919 if (idx >= num_idxs)
1920 continue;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001921 if (i > 0 && type == cfg->inputs[i - 1].type)
1922 type_idx++;
1923 else
1924 type_idx = 0;
Takashi Iwai10a20af2010-09-09 16:28:02 +02001925 label = hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwai620e2b22011-06-17 17:19:19 +02001926 idx2 = get_connection_index(codec, spec->aa_mix_nid,
1927 pin_idxs[idx]);
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001928 if (idx2 >= 0)
Lydia Wang16922282011-03-22 16:24:10 +08001929 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwai620e2b22011-06-17 17:19:19 +02001930 idx2, spec->aa_mix_nid);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001931 if (err < 0)
1932 return err;
Takashi Iwai10a20af2010-09-09 16:28:02 +02001933 snd_hda_add_imux_item(imux, label, idx, NULL);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001934 }
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001935
1936 /* create capture mixer elements */
1937 for (i = 0; i < spec->num_adc_nids; i++) {
1938 hda_nid_t adc = spec->adc_nids[i];
1939 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
1940 "Capture Volume", i,
1941 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1942 HDA_INPUT));
1943 if (err < 0)
1944 return err;
1945 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1946 "Capture Switch", i,
1947 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1948 HDA_INPUT));
1949 if (err < 0)
1950 return err;
1951 }
1952
1953 /* input-source control */
1954 for (i = 0; i < spec->num_adc_nids; i++)
1955 if (!spec->mux_nids[i])
1956 break;
1957 if (i) {
1958 struct snd_kcontrol_new *knew;
1959 knew = via_clone_control(spec, &via_input_src_ctl);
1960 if (!knew)
1961 return -ENOMEM;
1962 knew->count = i;
1963 }
1964
1965 /* mic-boosts */
1966 for (i = 0; i < cfg->num_inputs; i++) {
1967 hda_nid_t pin = cfg->inputs[i].pin;
1968 unsigned int caps;
1969 const char *label;
1970 char name[32];
1971
1972 if (cfg->inputs[i].type != AUTO_PIN_MIC)
1973 continue;
1974 caps = query_amp_caps(codec, pin, HDA_INPUT);
1975 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
1976 continue;
1977 label = hda_get_autocfg_input_label(codec, cfg, i);
1978 snprintf(name, sizeof(name), "%s Boost Capture Volume", label);
1979 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1980 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
1981 if (err < 0)
1982 return err;
1983 }
1984
Joseph Chanc577b8a2006-11-29 15:29:40 +01001985 return 0;
1986}
1987
Takashi Iwaicb53c622007-08-10 17:21:45 +02001988#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001989static const struct hda_amp_list vt1708_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02001990 { 0x17, HDA_INPUT, 1 },
1991 { 0x17, HDA_INPUT, 2 },
1992 { 0x17, HDA_INPUT, 3 },
1993 { 0x17, HDA_INPUT, 4 },
1994 { } /* end */
1995};
1996#endif
1997
Harald Welte76d9b0d2008-09-09 15:50:37 +08001998static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
1999{
2000 unsigned int def_conf;
2001 unsigned char seqassoc;
2002
Takashi Iwai2f334f92009-02-20 14:37:42 +01002003 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002004 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2005 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002006 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2007 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2008 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2009 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002010 }
2011
2012 return;
2013}
2014
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002015static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002016 struct snd_ctl_elem_value *ucontrol)
2017{
2018 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2019 struct via_spec *spec = codec->spec;
2020
2021 if (spec->codec_type != VT1708)
2022 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002023 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002024 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002025 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002026 return 0;
2027}
2028
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002029static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002030 struct snd_ctl_elem_value *ucontrol)
2031{
2032 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2033 struct via_spec *spec = codec->spec;
2034 int change;
2035
2036 if (spec->codec_type != VT1708)
2037 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002038 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002039 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002040 == !spec->vt1708_jack_detect;
2041 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002042 mute_aa_path(codec, 1);
2043 notify_aa_path_ctls(codec);
2044 }
2045 return change;
2046}
2047
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002048static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2049 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2050 .name = "Jack Detect",
2051 .count = 1,
2052 .info = snd_ctl_boolean_mono_info,
2053 .get = vt1708_jack_detect_get,
2054 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002055};
2056
Takashi Iwai12daef62011-06-18 17:45:49 +02002057static void fill_dig_outs(struct hda_codec *codec);
2058static void fill_dig_in(struct hda_codec *codec);
2059
2060static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002061{
2062 struct via_spec *spec = codec->spec;
2063 int err;
2064
2065 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2066 if (err < 0)
2067 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002068 err = via_auto_fill_dac_nids(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002069 if (err < 0)
2070 return err;
2071 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002072 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002073
Takashi Iwai4a796162011-06-17 17:53:38 +02002074 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002075 if (err < 0)
2076 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002077 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002078 if (err < 0)
2079 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002080 err = via_auto_create_speaker_ctls(codec);
2081 if (err < 0)
2082 return err;
Takashi Iwai620e2b22011-06-17 17:19:19 +02002083 err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002084 if (err < 0)
2085 return err;
2086
2087 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2088
Takashi Iwai12daef62011-06-18 17:45:49 +02002089 fill_dig_outs(codec);
2090 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002091
Takashi Iwai603c4012008-07-30 15:01:44 +02002092 if (spec->kctls.list)
2093 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002094
Takashi Iwai096a8852011-06-20 12:09:02 +02002095 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002096
Harald Welte0aa62ae2008-09-09 15:58:27 +08002097 spec->input_mux = &spec->private_imux[0];
2098
Takashi Iwaiece8d042011-06-19 16:24:21 +02002099 if (spec->hp_mux) {
2100 err = via_hp_build(codec);
2101 if (err < 0)
2102 return err;
2103 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002104
Takashi Iwaif4a78282011-06-17 18:46:48 +02002105 err = via_smart51_build(codec);
2106 if (err < 0)
2107 return err;
2108
Takashi Iwai5d417622011-06-20 11:32:27 +02002109 /* assign slave outs */
2110 if (spec->slave_dig_outs[0])
2111 codec->slave_dig_outs = spec->slave_dig_outs;
2112
Joseph Chanc577b8a2006-11-29 15:29:40 +01002113 return 1;
2114}
2115
Takashi Iwai5d417622011-06-20 11:32:27 +02002116static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002117{
Lydia Wang25eaba22009-10-10 19:08:43 +08002118 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002119 if (spec->multiout.dig_out_nid)
2120 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2121 if (spec->slave_dig_outs[0])
2122 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2123}
Lydia Wang25eaba22009-10-10 19:08:43 +08002124
Takashi Iwai5d417622011-06-20 11:32:27 +02002125static void via_auto_init_dig_in(struct hda_codec *codec)
2126{
2127 struct via_spec *spec = codec->spec;
2128 if (!spec->dig_in_nid)
2129 return;
2130 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2131 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2132}
2133
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002134/* initialize the unsolicited events */
2135static void via_auto_init_unsol_event(struct hda_codec *codec)
2136{
2137 struct via_spec *spec = codec->spec;
2138 struct auto_pin_cfg *cfg = &spec->autocfg;
2139 unsigned int ev;
2140 int i;
2141
2142 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2143 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2144 AC_VERB_SET_UNSOLICITED_ENABLE,
2145 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2146
2147 if (cfg->speaker_pins[0])
2148 ev = VIA_LINE_EVENT;
2149 else
2150 ev = 0;
2151 for (i = 0; i < cfg->line_outs; i++) {
2152 if (cfg->line_out_pins[i] &&
2153 is_jack_detectable(codec, cfg->line_out_pins[i]))
2154 snd_hda_codec_write(codec, cfg->line_out_pins[0], 0,
2155 AC_VERB_SET_UNSOLICITED_ENABLE,
2156 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2157 }
2158
2159 for (i = 0; i < cfg->num_inputs; i++) {
2160 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2161 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2162 AC_VERB_SET_UNSOLICITED_ENABLE,
2163 AC_USRSP_EN | VIA_JACK_EVENT);
2164 }
2165}
2166
Takashi Iwai5d417622011-06-20 11:32:27 +02002167static int via_init(struct hda_codec *codec)
2168{
2169 struct via_spec *spec = codec->spec;
2170 int i;
2171
2172 for (i = 0; i < spec->num_iverbs; i++)
2173 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2174
Joseph Chanc577b8a2006-11-29 15:29:40 +01002175 via_auto_init_multi_out(codec);
2176 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002177 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002178 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002179 via_auto_init_dig_outs(codec);
2180 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002181
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002182 via_auto_init_unsol_event(codec);
2183
2184 via_hp_automute(codec);
2185 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002186
Joseph Chanc577b8a2006-11-29 15:29:40 +01002187 return 0;
2188}
2189
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002190static void vt1708_update_hp_jack_state(struct work_struct *work)
2191{
2192 struct via_spec *spec = container_of(work, struct via_spec,
2193 vt1708_hp_work.work);
2194 if (spec->codec_type != VT1708)
2195 return;
2196 /* if jack state toggled */
2197 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002198 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002199 spec->vt1708_hp_present ^= 1;
2200 via_hp_automute(spec->codec);
2201 }
2202 vt1708_start_hp_work(spec);
2203}
2204
Takashi Iwai337b9d02009-07-07 18:18:59 +02002205static int get_mux_nids(struct hda_codec *codec)
2206{
2207 struct via_spec *spec = codec->spec;
2208 hda_nid_t nid, conn[8];
2209 unsigned int type;
2210 int i, n;
2211
2212 for (i = 0; i < spec->num_adc_nids; i++) {
2213 nid = spec->adc_nids[i];
2214 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002215 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002216 if (type == AC_WID_PIN)
2217 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002218 n = snd_hda_get_connections(codec, nid, conn,
2219 ARRAY_SIZE(conn));
2220 if (n <= 0)
2221 break;
2222 if (n > 1) {
2223 spec->mux_nids[i] = nid;
2224 break;
2225 }
2226 nid = conn[0];
2227 }
2228 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002229 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002230}
2231
Joseph Chanc577b8a2006-11-29 15:29:40 +01002232static int patch_vt1708(struct hda_codec *codec)
2233{
2234 struct via_spec *spec;
2235 int err;
2236
2237 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002238 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002239 if (spec == NULL)
2240 return -ENOMEM;
2241
Takashi Iwai620e2b22011-06-17 17:19:19 +02002242 spec->aa_mix_nid = 0x17;
2243
Takashi Iwai12daef62011-06-18 17:45:49 +02002244 /* Add HP and CD pin config connect bit re-config action */
2245 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2246 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2247
Joseph Chanc577b8a2006-11-29 15:29:40 +01002248 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002249 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002250 if (err < 0) {
2251 via_free(codec);
2252 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002253 }
2254
Takashi Iwai12daef62011-06-18 17:45:49 +02002255 /* add jack detect on/off control */
2256 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2257 return -ENOMEM;
2258
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002259 /* disable 32bit format on VT1708 */
2260 if (codec->vendor_id == 0x11061708)
2261 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002262
Joseph Chanc577b8a2006-11-29 15:29:40 +01002263 codec->patch_ops = via_patch_ops;
2264
Takashi Iwaicb53c622007-08-10 17:21:45 +02002265#ifdef CONFIG_SND_HDA_POWER_SAVE
2266 spec->loopback.amplist = vt1708_loopbacks;
2267#endif
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002268 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002269 return 0;
2270}
2271
Joseph Chanc577b8a2006-11-29 15:29:40 +01002272/*
2273 * generic initialization of ADC, input mixers and output mixers
2274 */
Takashi Iwaicb53c622007-08-10 17:21:45 +02002275#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002276static const struct hda_amp_list vt1709_loopbacks[] = {
Takashi Iwaicb53c622007-08-10 17:21:45 +02002277 { 0x18, HDA_INPUT, 1 },
2278 { 0x18, HDA_INPUT, 2 },
2279 { 0x18, HDA_INPUT, 3 },
2280 { 0x18, HDA_INPUT, 4 },
2281 { } /* end */
2282};
2283#endif
2284
Joseph Chanc577b8a2006-11-29 15:29:40 +01002285static int patch_vt1709_10ch(struct hda_codec *codec)
2286{
2287 struct via_spec *spec;
2288 int err;
2289
2290 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002291 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002292 if (spec == NULL)
2293 return -ENOMEM;
2294
Takashi Iwai620e2b22011-06-17 17:19:19 +02002295 spec->aa_mix_nid = 0x18;
2296
Takashi Iwai12daef62011-06-18 17:45:49 +02002297 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002298 if (err < 0) {
2299 via_free(codec);
2300 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002301 }
2302
Joseph Chanc577b8a2006-11-29 15:29:40 +01002303 codec->patch_ops = via_patch_ops;
2304
Takashi Iwaicb53c622007-08-10 17:21:45 +02002305#ifdef CONFIG_SND_HDA_POWER_SAVE
2306 spec->loopback.amplist = vt1709_loopbacks;
2307#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01002308
2309 return 0;
2310}
2311/*
2312 * generic initialization of ADC, input mixers and output mixers
2313 */
Joseph Chanc577b8a2006-11-29 15:29:40 +01002314static int patch_vt1709_6ch(struct hda_codec *codec)
2315{
2316 struct via_spec *spec;
2317 int err;
2318
2319 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002320 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002321 if (spec == NULL)
2322 return -ENOMEM;
2323
Takashi Iwai620e2b22011-06-17 17:19:19 +02002324 spec->aa_mix_nid = 0x18;
2325
Takashi Iwai12daef62011-06-18 17:45:49 +02002326 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002327 if (err < 0) {
2328 via_free(codec);
2329 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002330 }
2331
Joseph Chanc577b8a2006-11-29 15:29:40 +01002332 codec->patch_ops = via_patch_ops;
2333
Takashi Iwaicb53c622007-08-10 17:21:45 +02002334#ifdef CONFIG_SND_HDA_POWER_SAVE
2335 spec->loopback.amplist = vt1709_loopbacks;
2336#endif
Josepch Chanf7278fd2007-12-13 16:40:40 +01002337 return 0;
2338}
2339
Josepch Chanf7278fd2007-12-13 16:40:40 +01002340/*
2341 * generic initialization of ADC, input mixers and output mixers
2342 */
Josepch Chanf7278fd2007-12-13 16:40:40 +01002343#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002344static const struct hda_amp_list vt1708B_loopbacks[] = {
Josepch Chanf7278fd2007-12-13 16:40:40 +01002345 { 0x16, HDA_INPUT, 1 },
2346 { 0x16, HDA_INPUT, 2 },
2347 { 0x16, HDA_INPUT, 3 },
2348 { 0x16, HDA_INPUT, 4 },
2349 { } /* end */
2350};
2351#endif
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002352
2353static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2354{
2355 struct via_spec *spec = codec->spec;
2356 int imux_is_smixer;
2357 unsigned int parm;
2358 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002359 if ((spec->codec_type != VT1708B_4CH) &&
2360 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002361 is_8ch = 1;
2362
2363 /* SW0 (17h) = stereo mixer */
2364 imux_is_smixer =
2365 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2366 == ((spec->codec_type == VT1708S) ? 5 : 0));
2367 /* inputs */
2368 /* PW 1/2/5 (1ah/1bh/1eh) */
2369 parm = AC_PWRST_D3;
2370 set_pin_power_state(codec, 0x1a, &parm);
2371 set_pin_power_state(codec, 0x1b, &parm);
2372 set_pin_power_state(codec, 0x1e, &parm);
2373 if (imux_is_smixer)
2374 parm = AC_PWRST_D0;
2375 /* SW0 (17h), AIW 0/1 (13h/14h) */
2376 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2377 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2378 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2379
2380 /* outputs */
2381 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2382 parm = AC_PWRST_D3;
2383 set_pin_power_state(codec, 0x19, &parm);
2384 if (spec->smart51_enabled)
2385 set_pin_power_state(codec, 0x1b, &parm);
2386 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2387 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2388
2389 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2390 if (is_8ch) {
2391 parm = AC_PWRST_D3;
2392 set_pin_power_state(codec, 0x22, &parm);
2393 if (spec->smart51_enabled)
2394 set_pin_power_state(codec, 0x1a, &parm);
2395 snd_hda_codec_write(codec, 0x26, 0,
2396 AC_VERB_SET_POWER_STATE, parm);
2397 snd_hda_codec_write(codec, 0x24, 0,
2398 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002399 } else if (codec->vendor_id == 0x11064397) {
2400 /* PW7(23h), SW2(27h), AOW2(25h) */
2401 parm = AC_PWRST_D3;
2402 set_pin_power_state(codec, 0x23, &parm);
2403 if (spec->smart51_enabled)
2404 set_pin_power_state(codec, 0x1a, &parm);
2405 snd_hda_codec_write(codec, 0x27, 0,
2406 AC_VERB_SET_POWER_STATE, parm);
2407 snd_hda_codec_write(codec, 0x25, 0,
2408 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002409 }
2410
2411 /* PW 3/4/7 (1ch/1dh/23h) */
2412 parm = AC_PWRST_D3;
2413 /* force to D0 for internal Speaker */
2414 set_pin_power_state(codec, 0x1c, &parm);
2415 set_pin_power_state(codec, 0x1d, &parm);
2416 if (is_8ch)
2417 set_pin_power_state(codec, 0x23, &parm);
2418
2419 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2420 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2421 imux_is_smixer ? AC_PWRST_D0 : parm);
2422 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2423 if (is_8ch) {
2424 snd_hda_codec_write(codec, 0x25, 0,
2425 AC_VERB_SET_POWER_STATE, parm);
2426 snd_hda_codec_write(codec, 0x27, 0,
2427 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002428 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2429 snd_hda_codec_write(codec, 0x25, 0,
2430 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002431}
2432
Lydia Wang518bf3b2009-10-10 19:07:29 +08002433static int patch_vt1708S(struct hda_codec *codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002434static int patch_vt1708B_8ch(struct hda_codec *codec)
2435{
2436 struct via_spec *spec;
2437 int err;
2438
Lydia Wang518bf3b2009-10-10 19:07:29 +08002439 if (get_codec_type(codec) == VT1708BCE)
2440 return patch_vt1708S(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002441 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002442 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002443 if (spec == NULL)
2444 return -ENOMEM;
2445
Takashi Iwai620e2b22011-06-17 17:19:19 +02002446 spec->aa_mix_nid = 0x16;
2447
Josepch Chanf7278fd2007-12-13 16:40:40 +01002448 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002449 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002450 if (err < 0) {
2451 via_free(codec);
2452 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002453 }
2454
Josepch Chanf7278fd2007-12-13 16:40:40 +01002455 codec->patch_ops = via_patch_ops;
2456
Josepch Chanf7278fd2007-12-13 16:40:40 +01002457#ifdef CONFIG_SND_HDA_POWER_SAVE
2458 spec->loopback.amplist = vt1708B_loopbacks;
2459#endif
2460
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002461 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2462
Josepch Chanf7278fd2007-12-13 16:40:40 +01002463 return 0;
2464}
2465
2466static int patch_vt1708B_4ch(struct hda_codec *codec)
2467{
2468 struct via_spec *spec;
2469 int err;
2470
2471 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002472 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002473 if (spec == NULL)
2474 return -ENOMEM;
2475
Josepch Chanf7278fd2007-12-13 16:40:40 +01002476 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002477 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002478 if (err < 0) {
2479 via_free(codec);
2480 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002481 }
2482
Josepch Chanf7278fd2007-12-13 16:40:40 +01002483 codec->patch_ops = via_patch_ops;
2484
Josepch Chanf7278fd2007-12-13 16:40:40 +01002485#ifdef CONFIG_SND_HDA_POWER_SAVE
2486 spec->loopback.amplist = vt1708B_loopbacks;
2487#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01002488
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002489 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2490
Joseph Chanc577b8a2006-11-29 15:29:40 +01002491 return 0;
2492}
2493
Harald Welted949cac2008-09-09 15:56:01 +08002494/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002495static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002496 /* Enable Mic Boost Volume backdoor */
2497 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002498 /* don't bybass mixer */
2499 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002500 { }
2501};
2502
Takashi Iwai9da29272009-05-07 16:31:14 +02002503/* fill out digital output widgets; one for master and one for slave outputs */
2504static void fill_dig_outs(struct hda_codec *codec)
2505{
2506 struct via_spec *spec = codec->spec;
2507 int i;
2508
2509 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2510 hda_nid_t nid;
2511 int conn;
2512
2513 nid = spec->autocfg.dig_out_pins[i];
2514 if (!nid)
2515 continue;
2516 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2517 if (conn < 1)
2518 continue;
2519 if (!spec->multiout.dig_out_nid)
2520 spec->multiout.dig_out_nid = nid;
2521 else {
2522 spec->slave_dig_outs[0] = nid;
2523 break; /* at most two dig outs */
2524 }
2525 }
2526}
2527
Takashi Iwai12daef62011-06-18 17:45:49 +02002528static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002529{
2530 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002531 hda_nid_t dig_nid;
2532 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002533
Takashi Iwai12daef62011-06-18 17:45:49 +02002534 if (!spec->autocfg.dig_in_pin)
2535 return;
Harald Welted949cac2008-09-09 15:56:01 +08002536
Takashi Iwai12daef62011-06-18 17:45:49 +02002537 dig_nid = codec->start_nid;
2538 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2539 unsigned int wcaps = get_wcaps(codec, dig_nid);
2540 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2541 continue;
2542 if (!(wcaps & AC_WCAP_DIGITAL))
2543 continue;
2544 if (!(wcaps & AC_WCAP_CONN_LIST))
2545 continue;
2546 err = get_connection_index(codec, dig_nid,
2547 spec->autocfg.dig_in_pin);
2548 if (err >= 0) {
2549 spec->dig_in_nid = dig_nid;
2550 break;
2551 }
2552 }
Harald Welted949cac2008-09-09 15:56:01 +08002553}
2554
2555#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002556static const struct hda_amp_list vt1708S_loopbacks[] = {
Harald Welted949cac2008-09-09 15:56:01 +08002557 { 0x16, HDA_INPUT, 1 },
2558 { 0x16, HDA_INPUT, 2 },
2559 { 0x16, HDA_INPUT, 3 },
2560 { 0x16, HDA_INPUT, 4 },
2561 { } /* end */
2562};
2563#endif
2564
Lydia Wang6369bcf2009-10-10 19:08:31 +08002565static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2566 int offset, int num_steps, int step_size)
2567{
2568 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2569 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2570 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2571 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2572 (0 << AC_AMPCAP_MUTE_SHIFT));
2573}
2574
Harald Welted949cac2008-09-09 15:56:01 +08002575static int patch_vt1708S(struct hda_codec *codec)
2576{
2577 struct via_spec *spec;
2578 int err;
2579
2580 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002581 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002582 if (spec == NULL)
2583 return -ENOMEM;
2584
Takashi Iwai620e2b22011-06-17 17:19:19 +02002585 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002586 override_mic_boost(codec, 0x1a, 0, 3, 40);
2587 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002588
Harald Welted949cac2008-09-09 15:56:01 +08002589 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002590 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002591 if (err < 0) {
2592 via_free(codec);
2593 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002594 }
2595
Takashi Iwai096a8852011-06-20 12:09:02 +02002596 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002597
Harald Welted949cac2008-09-09 15:56:01 +08002598 codec->patch_ops = via_patch_ops;
2599
Harald Welted949cac2008-09-09 15:56:01 +08002600#ifdef CONFIG_SND_HDA_POWER_SAVE
2601 spec->loopback.amplist = vt1708S_loopbacks;
2602#endif
2603
Lydia Wang518bf3b2009-10-10 19:07:29 +08002604 /* correct names for VT1708BCE */
2605 if (get_codec_type(codec) == VT1708BCE) {
2606 kfree(codec->chip_name);
2607 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2608 snprintf(codec->bus->card->mixername,
2609 sizeof(codec->bus->card->mixername),
2610 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002611 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002612 /* correct names for VT1705 */
2613 if (codec->vendor_id == 0x11064397) {
2614 kfree(codec->chip_name);
2615 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2616 snprintf(codec->bus->card->mixername,
2617 sizeof(codec->bus->card->mixername),
2618 "%s %s", codec->vendor_name, codec->chip_name);
2619 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002620 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002621 return 0;
2622}
2623
2624/* Patch for VT1702 */
2625
Takashi Iwai096a8852011-06-20 12:09:02 +02002626static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002627 /* mixer enable */
2628 {0x1, 0xF88, 0x3},
2629 /* GPIO 0~2 */
2630 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002631 { }
2632};
2633
Harald Welted949cac2008-09-09 15:56:01 +08002634#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002635static const struct hda_amp_list vt1702_loopbacks[] = {
Harald Welted949cac2008-09-09 15:56:01 +08002636 { 0x1A, HDA_INPUT, 1 },
2637 { 0x1A, HDA_INPUT, 2 },
2638 { 0x1A, HDA_INPUT, 3 },
2639 { 0x1A, HDA_INPUT, 4 },
2640 { } /* end */
2641};
2642#endif
2643
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002644static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2645{
2646 int imux_is_smixer =
2647 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2648 unsigned int parm;
2649 /* inputs */
2650 /* PW 1/2/5 (14h/15h/18h) */
2651 parm = AC_PWRST_D3;
2652 set_pin_power_state(codec, 0x14, &parm);
2653 set_pin_power_state(codec, 0x15, &parm);
2654 set_pin_power_state(codec, 0x18, &parm);
2655 if (imux_is_smixer)
2656 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2657 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2658 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2659 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2660 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2661 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2662
2663 /* outputs */
2664 /* PW 3/4 (16h/17h) */
2665 parm = AC_PWRST_D3;
2666 set_pin_power_state(codec, 0x17, &parm);
2667 set_pin_power_state(codec, 0x16, &parm);
2668 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2669 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2670 imux_is_smixer ? AC_PWRST_D0 : parm);
2671 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2672 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2673}
2674
Harald Welted949cac2008-09-09 15:56:01 +08002675static int patch_vt1702(struct hda_codec *codec)
2676{
2677 struct via_spec *spec;
2678 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002679
2680 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002681 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002682 if (spec == NULL)
2683 return -ENOMEM;
2684
Takashi Iwai620e2b22011-06-17 17:19:19 +02002685 spec->aa_mix_nid = 0x1a;
2686
Takashi Iwai12daef62011-06-18 17:45:49 +02002687 /* limit AA path volume to 0 dB */
2688 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2689 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2690 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2691 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2692 (1 << AC_AMPCAP_MUTE_SHIFT));
2693
Harald Welted949cac2008-09-09 15:56:01 +08002694 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002695 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002696 if (err < 0) {
2697 via_free(codec);
2698 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002699 }
2700
Takashi Iwai096a8852011-06-20 12:09:02 +02002701 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002702
Harald Welted949cac2008-09-09 15:56:01 +08002703 codec->patch_ops = via_patch_ops;
2704
Harald Welted949cac2008-09-09 15:56:01 +08002705#ifdef CONFIG_SND_HDA_POWER_SAVE
2706 spec->loopback.amplist = vt1702_loopbacks;
2707#endif
2708
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002709 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002710 return 0;
2711}
2712
Lydia Wangeb7188c2009-10-10 19:08:34 +08002713/* Patch for VT1718S */
2714
Takashi Iwai096a8852011-06-20 12:09:02 +02002715static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002716 /* Enable MW0 adjust Gain 5 */
2717 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002718 /* Enable Boost Volume backdoor */
2719 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002720
Lydia Wangeb7188c2009-10-10 19:08:34 +08002721 { }
2722};
2723
Lydia Wangeb7188c2009-10-10 19:08:34 +08002724#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002725static const struct hda_amp_list vt1718S_loopbacks[] = {
Lydia Wangeb7188c2009-10-10 19:08:34 +08002726 { 0x21, HDA_INPUT, 1 },
2727 { 0x21, HDA_INPUT, 2 },
2728 { 0x21, HDA_INPUT, 3 },
2729 { 0x21, HDA_INPUT, 4 },
2730 { } /* end */
2731};
2732#endif
2733
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002734static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2735{
2736 struct via_spec *spec = codec->spec;
2737 int imux_is_smixer;
2738 unsigned int parm;
2739 /* MUX6 (1eh) = stereo mixer */
2740 imux_is_smixer =
2741 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2742 /* inputs */
2743 /* PW 5/6/7 (29h/2ah/2bh) */
2744 parm = AC_PWRST_D3;
2745 set_pin_power_state(codec, 0x29, &parm);
2746 set_pin_power_state(codec, 0x2a, &parm);
2747 set_pin_power_state(codec, 0x2b, &parm);
2748 if (imux_is_smixer)
2749 parm = AC_PWRST_D0;
2750 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2751 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2752 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2753 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2754 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2755
2756 /* outputs */
2757 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2758 parm = AC_PWRST_D3;
2759 set_pin_power_state(codec, 0x27, &parm);
2760 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2761 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2762
2763 /* PW2 (26h), AOW2 (ah) */
2764 parm = AC_PWRST_D3;
2765 set_pin_power_state(codec, 0x26, &parm);
2766 if (spec->smart51_enabled)
2767 set_pin_power_state(codec, 0x2b, &parm);
2768 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2769
2770 /* PW0 (24h), AOW0 (8h) */
2771 parm = AC_PWRST_D3;
2772 set_pin_power_state(codec, 0x24, &parm);
2773 if (!spec->hp_independent_mode) /* check for redirected HP */
2774 set_pin_power_state(codec, 0x28, &parm);
2775 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2776 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2777 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2778 imux_is_smixer ? AC_PWRST_D0 : parm);
2779
2780 /* PW1 (25h), AOW1 (9h) */
2781 parm = AC_PWRST_D3;
2782 set_pin_power_state(codec, 0x25, &parm);
2783 if (spec->smart51_enabled)
2784 set_pin_power_state(codec, 0x2a, &parm);
2785 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2786
2787 if (spec->hp_independent_mode) {
2788 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2789 parm = AC_PWRST_D3;
2790 set_pin_power_state(codec, 0x28, &parm);
2791 snd_hda_codec_write(codec, 0x1b, 0,
2792 AC_VERB_SET_POWER_STATE, parm);
2793 snd_hda_codec_write(codec, 0x34, 0,
2794 AC_VERB_SET_POWER_STATE, parm);
2795 snd_hda_codec_write(codec, 0xc, 0,
2796 AC_VERB_SET_POWER_STATE, parm);
2797 }
2798}
2799
Lydia Wangeb7188c2009-10-10 19:08:34 +08002800static int patch_vt1718S(struct hda_codec *codec)
2801{
2802 struct via_spec *spec;
2803 int err;
2804
2805 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002806 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002807 if (spec == NULL)
2808 return -ENOMEM;
2809
Takashi Iwai620e2b22011-06-17 17:19:19 +02002810 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002811 override_mic_boost(codec, 0x2b, 0, 3, 40);
2812 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002813
Lydia Wangeb7188c2009-10-10 19:08:34 +08002814 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002815 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002816 if (err < 0) {
2817 via_free(codec);
2818 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002819 }
2820
Takashi Iwai096a8852011-06-20 12:09:02 +02002821 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002822
Lydia Wangeb7188c2009-10-10 19:08:34 +08002823 codec->patch_ops = via_patch_ops;
2824
Lydia Wangeb7188c2009-10-10 19:08:34 +08002825#ifdef CONFIG_SND_HDA_POWER_SAVE
2826 spec->loopback.amplist = vt1718S_loopbacks;
2827#endif
2828
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002829 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2830
Lydia Wangeb7188c2009-10-10 19:08:34 +08002831 return 0;
2832}
Lydia Wangf3db4232009-10-10 19:08:41 +08002833
2834/* Patch for VT1716S */
2835
2836static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2837 struct snd_ctl_elem_info *uinfo)
2838{
2839 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2840 uinfo->count = 1;
2841 uinfo->value.integer.min = 0;
2842 uinfo->value.integer.max = 1;
2843 return 0;
2844}
2845
2846static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2847 struct snd_ctl_elem_value *ucontrol)
2848{
2849 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2850 int index = 0;
2851
2852 index = snd_hda_codec_read(codec, 0x26, 0,
2853 AC_VERB_GET_CONNECT_SEL, 0);
2854 if (index != -1)
2855 *ucontrol->value.integer.value = index;
2856
2857 return 0;
2858}
2859
2860static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2861 struct snd_ctl_elem_value *ucontrol)
2862{
2863 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2864 struct via_spec *spec = codec->spec;
2865 int index = *ucontrol->value.integer.value;
2866
2867 snd_hda_codec_write(codec, 0x26, 0,
2868 AC_VERB_SET_CONNECT_SEL, index);
2869 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002870 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002871 return 1;
2872}
2873
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002874static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002875 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2876 {
2877 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2878 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002879 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08002880 .count = 1,
2881 .info = vt1716s_dmic_info,
2882 .get = vt1716s_dmic_get,
2883 .put = vt1716s_dmic_put,
2884 },
2885 {} /* end */
2886};
2887
2888
2889/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002890static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002891 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
2892 { } /* end */
2893};
2894
Takashi Iwai096a8852011-06-20 12:09:02 +02002895static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002896 /* Enable Boost Volume backdoor */
2897 {0x1, 0xf8a, 0x80},
2898 /* don't bybass mixer */
2899 {0x1, 0xf88, 0xc0},
2900 /* Enable mono output */
2901 {0x1, 0xf90, 0x08},
2902 { }
2903};
2904
Lydia Wangf3db4232009-10-10 19:08:41 +08002905#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002906static const struct hda_amp_list vt1716S_loopbacks[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002907 { 0x16, HDA_INPUT, 1 },
2908 { 0x16, HDA_INPUT, 2 },
2909 { 0x16, HDA_INPUT, 3 },
2910 { 0x16, HDA_INPUT, 4 },
2911 { } /* end */
2912};
2913#endif
2914
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002915static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
2916{
2917 struct via_spec *spec = codec->spec;
2918 int imux_is_smixer;
2919 unsigned int parm;
2920 unsigned int mono_out, present;
2921 /* SW0 (17h) = stereo mixer */
2922 imux_is_smixer =
2923 (snd_hda_codec_read(codec, 0x17, 0,
2924 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
2925 /* inputs */
2926 /* PW 1/2/5 (1ah/1bh/1eh) */
2927 parm = AC_PWRST_D3;
2928 set_pin_power_state(codec, 0x1a, &parm);
2929 set_pin_power_state(codec, 0x1b, &parm);
2930 set_pin_power_state(codec, 0x1e, &parm);
2931 if (imux_is_smixer)
2932 parm = AC_PWRST_D0;
2933 /* SW0 (17h), AIW0(13h) */
2934 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2935 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2936
2937 parm = AC_PWRST_D3;
2938 set_pin_power_state(codec, 0x1e, &parm);
2939 /* PW11 (22h) */
2940 if (spec->dmic_enabled)
2941 set_pin_power_state(codec, 0x22, &parm);
2942 else
2943 snd_hda_codec_write(codec, 0x22, 0,
2944 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
2945
2946 /* SW2(26h), AIW1(14h) */
2947 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
2948 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2949
2950 /* outputs */
2951 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2952 parm = AC_PWRST_D3;
2953 set_pin_power_state(codec, 0x19, &parm);
2954 /* Smart 5.1 PW2(1bh) */
2955 if (spec->smart51_enabled)
2956 set_pin_power_state(codec, 0x1b, &parm);
2957 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2958 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2959
2960 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
2961 parm = AC_PWRST_D3;
2962 set_pin_power_state(codec, 0x23, &parm);
2963 /* Smart 5.1 PW1(1ah) */
2964 if (spec->smart51_enabled)
2965 set_pin_power_state(codec, 0x1a, &parm);
2966 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
2967
2968 /* Smart 5.1 PW5(1eh) */
2969 if (spec->smart51_enabled)
2970 set_pin_power_state(codec, 0x1e, &parm);
2971 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
2972
2973 /* Mono out */
2974 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
2975 present = snd_hda_jack_detect(codec, 0x1c);
2976
2977 if (present)
2978 mono_out = 0;
2979 else {
2980 present = snd_hda_jack_detect(codec, 0x1d);
2981 if (!spec->hp_independent_mode && present)
2982 mono_out = 0;
2983 else
2984 mono_out = 1;
2985 }
2986 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
2987 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
2988 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
2989 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
2990
2991 /* PW 3/4 (1ch/1dh) */
2992 parm = AC_PWRST_D3;
2993 set_pin_power_state(codec, 0x1c, &parm);
2994 set_pin_power_state(codec, 0x1d, &parm);
2995 /* HP Independent Mode, power on AOW3 */
2996 if (spec->hp_independent_mode)
2997 snd_hda_codec_write(codec, 0x25, 0,
2998 AC_VERB_SET_POWER_STATE, parm);
2999
3000 /* force to D0 for internal Speaker */
3001 /* MW0 (16h), AOW0 (10h) */
3002 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3003 imux_is_smixer ? AC_PWRST_D0 : parm);
3004 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3005 mono_out ? AC_PWRST_D0 : parm);
3006}
3007
Lydia Wangf3db4232009-10-10 19:08:41 +08003008static int patch_vt1716S(struct hda_codec *codec)
3009{
3010 struct via_spec *spec;
3011 int err;
3012
3013 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003014 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003015 if (spec == NULL)
3016 return -ENOMEM;
3017
Takashi Iwai620e2b22011-06-17 17:19:19 +02003018 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003019 override_mic_boost(codec, 0x1a, 0, 3, 40);
3020 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003021
Lydia Wangf3db4232009-10-10 19:08:41 +08003022 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003023 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003024 if (err < 0) {
3025 via_free(codec);
3026 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003027 }
3028
Takashi Iwai096a8852011-06-20 12:09:02 +02003029 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003030
Lydia Wangf3db4232009-10-10 19:08:41 +08003031 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3032 spec->num_mixers++;
3033
3034 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3035
3036 codec->patch_ops = via_patch_ops;
3037
Lydia Wangf3db4232009-10-10 19:08:41 +08003038#ifdef CONFIG_SND_HDA_POWER_SAVE
3039 spec->loopback.amplist = vt1716S_loopbacks;
3040#endif
3041
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003042 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003043 return 0;
3044}
Lydia Wang25eaba22009-10-10 19:08:43 +08003045
3046/* for vt2002P */
3047
Takashi Iwai096a8852011-06-20 12:09:02 +02003048static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003049 /* Class-D speaker related verbs */
3050 {0x1, 0xfe0, 0x4},
3051 {0x1, 0xfe9, 0x80},
3052 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003053 /* Enable Boost Volume backdoor */
3054 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003055 /* Enable AOW0 to MW9 */
3056 {0x1, 0xfb8, 0x88},
3057 { }
3058};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003059
Takashi Iwai096a8852011-06-20 12:09:02 +02003060static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003061 /* Enable Boost Volume backdoor */
3062 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003063 /* Enable AOW0 to MW9 */
3064 {0x1, 0xfb8, 0x88},
3065 { }
3066};
Lydia Wang25eaba22009-10-10 19:08:43 +08003067
Lydia Wang25eaba22009-10-10 19:08:43 +08003068#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003069static const struct hda_amp_list vt2002P_loopbacks[] = {
Lydia Wang25eaba22009-10-10 19:08:43 +08003070 { 0x21, HDA_INPUT, 0 },
3071 { 0x21, HDA_INPUT, 1 },
3072 { 0x21, HDA_INPUT, 2 },
3073 { } /* end */
3074};
3075#endif
3076
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003077static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3078{
3079 struct via_spec *spec = codec->spec;
3080 int imux_is_smixer;
3081 unsigned int parm;
3082 unsigned int present;
3083 /* MUX9 (1eh) = stereo mixer */
3084 imux_is_smixer =
3085 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3086 /* inputs */
3087 /* PW 5/6/7 (29h/2ah/2bh) */
3088 parm = AC_PWRST_D3;
3089 set_pin_power_state(codec, 0x29, &parm);
3090 set_pin_power_state(codec, 0x2a, &parm);
3091 set_pin_power_state(codec, 0x2b, &parm);
3092 parm = AC_PWRST_D0;
3093 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3094 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3095 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3096 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3097 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3098
3099 /* outputs */
3100 /* AOW0 (8h)*/
3101 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3102
Lydia Wang118909562011-03-23 17:57:34 +08003103 if (spec->codec_type == VT1802) {
3104 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3105 parm = AC_PWRST_D3;
3106 set_pin_power_state(codec, 0x28, &parm);
3107 snd_hda_codec_write(codec, 0x18, 0,
3108 AC_VERB_SET_POWER_STATE, parm);
3109 snd_hda_codec_write(codec, 0x38, 0,
3110 AC_VERB_SET_POWER_STATE, parm);
3111 } else {
3112 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3113 parm = AC_PWRST_D3;
3114 set_pin_power_state(codec, 0x26, &parm);
3115 snd_hda_codec_write(codec, 0x1c, 0,
3116 AC_VERB_SET_POWER_STATE, parm);
3117 snd_hda_codec_write(codec, 0x37, 0,
3118 AC_VERB_SET_POWER_STATE, parm);
3119 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003120
Lydia Wang118909562011-03-23 17:57:34 +08003121 if (spec->codec_type == VT1802) {
3122 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3123 parm = AC_PWRST_D3;
3124 set_pin_power_state(codec, 0x25, &parm);
3125 snd_hda_codec_write(codec, 0x15, 0,
3126 AC_VERB_SET_POWER_STATE, parm);
3127 snd_hda_codec_write(codec, 0x35, 0,
3128 AC_VERB_SET_POWER_STATE, parm);
3129 } else {
3130 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3131 parm = AC_PWRST_D3;
3132 set_pin_power_state(codec, 0x25, &parm);
3133 snd_hda_codec_write(codec, 0x19, 0,
3134 AC_VERB_SET_POWER_STATE, parm);
3135 snd_hda_codec_write(codec, 0x35, 0,
3136 AC_VERB_SET_POWER_STATE, parm);
3137 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003138
3139 if (spec->hp_independent_mode)
3140 snd_hda_codec_write(codec, 0x9, 0,
3141 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3142
3143 /* Class-D */
3144 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3145 present = snd_hda_jack_detect(codec, 0x25);
3146
3147 parm = AC_PWRST_D3;
3148 set_pin_power_state(codec, 0x24, &parm);
3149 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003150 if (spec->codec_type == VT1802)
3151 snd_hda_codec_write(codec, 0x14, 0,
3152 AC_VERB_SET_POWER_STATE, parm);
3153 else
3154 snd_hda_codec_write(codec, 0x18, 0,
3155 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003156 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3157
3158 /* Mono Out */
3159 present = snd_hda_jack_detect(codec, 0x26);
3160
3161 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003162 if (spec->codec_type == VT1802) {
3163 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3164 snd_hda_codec_write(codec, 0x33, 0,
3165 AC_VERB_SET_POWER_STATE, parm);
3166 snd_hda_codec_write(codec, 0x1c, 0,
3167 AC_VERB_SET_POWER_STATE, parm);
3168 snd_hda_codec_write(codec, 0x3c, 0,
3169 AC_VERB_SET_POWER_STATE, parm);
3170 } else {
3171 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3172 snd_hda_codec_write(codec, 0x31, 0,
3173 AC_VERB_SET_POWER_STATE, parm);
3174 snd_hda_codec_write(codec, 0x17, 0,
3175 AC_VERB_SET_POWER_STATE, parm);
3176 snd_hda_codec_write(codec, 0x3b, 0,
3177 AC_VERB_SET_POWER_STATE, parm);
3178 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003179 /* MW9 (21h) */
3180 if (imux_is_smixer || !is_aa_path_mute(codec))
3181 snd_hda_codec_write(codec, 0x21, 0,
3182 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3183 else
3184 snd_hda_codec_write(codec, 0x21, 0,
3185 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3186}
Lydia Wang25eaba22009-10-10 19:08:43 +08003187
3188/* patch for vt2002P */
3189static int patch_vt2002P(struct hda_codec *codec)
3190{
3191 struct via_spec *spec;
3192 int err;
3193
3194 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003195 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003196 if (spec == NULL)
3197 return -ENOMEM;
3198
Takashi Iwai620e2b22011-06-17 17:19:19 +02003199 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003200 override_mic_boost(codec, 0x2b, 0, 3, 40);
3201 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003202
Lydia Wang25eaba22009-10-10 19:08:43 +08003203 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003204 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003205 if (err < 0) {
3206 via_free(codec);
3207 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003208 }
3209
Lydia Wang118909562011-03-23 17:57:34 +08003210 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003211 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003212 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003213 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003214
Lydia Wang25eaba22009-10-10 19:08:43 +08003215 codec->patch_ops = via_patch_ops;
3216
Lydia Wang25eaba22009-10-10 19:08:43 +08003217#ifdef CONFIG_SND_HDA_POWER_SAVE
3218 spec->loopback.amplist = vt2002P_loopbacks;
3219#endif
3220
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003221 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003222 return 0;
3223}
Lydia Wangab6734e2009-10-10 19:08:46 +08003224
3225/* for vt1812 */
3226
Takashi Iwai096a8852011-06-20 12:09:02 +02003227static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003228 /* Enable Boost Volume backdoor */
3229 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003230 /* Enable AOW0 to MW9 */
3231 {0x1, 0xfb8, 0xa8},
3232 { }
3233};
3234
Lydia Wangab6734e2009-10-10 19:08:46 +08003235#ifdef CONFIG_SND_HDA_POWER_SAVE
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003236static const struct hda_amp_list vt1812_loopbacks[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003237 { 0x21, HDA_INPUT, 0 },
3238 { 0x21, HDA_INPUT, 1 },
3239 { 0x21, HDA_INPUT, 2 },
3240 { } /* end */
3241};
3242#endif
3243
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003244static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3245{
3246 struct via_spec *spec = codec->spec;
3247 int imux_is_smixer =
3248 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3249 unsigned int parm;
3250 unsigned int present;
3251 /* MUX10 (1eh) = stereo mixer */
3252 imux_is_smixer =
3253 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3254 /* inputs */
3255 /* PW 5/6/7 (29h/2ah/2bh) */
3256 parm = AC_PWRST_D3;
3257 set_pin_power_state(codec, 0x29, &parm);
3258 set_pin_power_state(codec, 0x2a, &parm);
3259 set_pin_power_state(codec, 0x2b, &parm);
3260 parm = AC_PWRST_D0;
3261 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3262 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3263 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3264 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3265 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3266
3267 /* outputs */
3268 /* AOW0 (8h)*/
3269 snd_hda_codec_write(codec, 0x8, 0,
3270 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3271
3272 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3273 parm = AC_PWRST_D3;
3274 set_pin_power_state(codec, 0x28, &parm);
3275 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3276 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3277
3278 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3279 parm = AC_PWRST_D3;
3280 set_pin_power_state(codec, 0x25, &parm);
3281 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3282 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3283 if (spec->hp_independent_mode)
3284 snd_hda_codec_write(codec, 0x9, 0,
3285 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3286
3287 /* Internal Speaker */
3288 /* PW0 (24h), MW0(14h), MUX0(34h) */
3289 present = snd_hda_jack_detect(codec, 0x25);
3290
3291 parm = AC_PWRST_D3;
3292 set_pin_power_state(codec, 0x24, &parm);
3293 if (present) {
3294 snd_hda_codec_write(codec, 0x14, 0,
3295 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3296 snd_hda_codec_write(codec, 0x34, 0,
3297 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3298 } else {
3299 snd_hda_codec_write(codec, 0x14, 0,
3300 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3301 snd_hda_codec_write(codec, 0x34, 0,
3302 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3303 }
3304
3305
3306 /* Mono Out */
3307 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3308 present = snd_hda_jack_detect(codec, 0x28);
3309
3310 parm = AC_PWRST_D3;
3311 set_pin_power_state(codec, 0x31, &parm);
3312 if (present) {
3313 snd_hda_codec_write(codec, 0x1c, 0,
3314 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3315 snd_hda_codec_write(codec, 0x3c, 0,
3316 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3317 snd_hda_codec_write(codec, 0x3e, 0,
3318 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3319 } else {
3320 snd_hda_codec_write(codec, 0x1c, 0,
3321 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3322 snd_hda_codec_write(codec, 0x3c, 0,
3323 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3324 snd_hda_codec_write(codec, 0x3e, 0,
3325 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3326 }
3327
3328 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3329 parm = AC_PWRST_D3;
3330 set_pin_power_state(codec, 0x33, &parm);
3331 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3332 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3333
3334}
Lydia Wangab6734e2009-10-10 19:08:46 +08003335
3336/* patch for vt1812 */
3337static int patch_vt1812(struct hda_codec *codec)
3338{
3339 struct via_spec *spec;
3340 int err;
3341
3342 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003343 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003344 if (spec == NULL)
3345 return -ENOMEM;
3346
Takashi Iwai620e2b22011-06-17 17:19:19 +02003347 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003348 override_mic_boost(codec, 0x2b, 0, 3, 40);
3349 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003350
Lydia Wangab6734e2009-10-10 19:08:46 +08003351 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003352 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003353 if (err < 0) {
3354 via_free(codec);
3355 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003356 }
3357
Takashi Iwai096a8852011-06-20 12:09:02 +02003358 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003359
Lydia Wangab6734e2009-10-10 19:08:46 +08003360 codec->patch_ops = via_patch_ops;
3361
Lydia Wangab6734e2009-10-10 19:08:46 +08003362#ifdef CONFIG_SND_HDA_POWER_SAVE
3363 spec->loopback.amplist = vt1812_loopbacks;
3364#endif
3365
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003366 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003367 return 0;
3368}
3369
Joseph Chanc577b8a2006-11-29 15:29:40 +01003370/*
3371 * patch entries
3372 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003373static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003374 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3375 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3376 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3377 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3378 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003379 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003380 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003381 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003382 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003383 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003384 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003385 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003386 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003387 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003388 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003389 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003390 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003391 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003392 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003393 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003394 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003395 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003396 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003397 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003398 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003399 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003400 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003401 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003402 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003403 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003404 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003405 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003406 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003407 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003408 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003409 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003410 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003411 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003412 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003413 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003414 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003415 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003416 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003417 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003418 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003419 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003420 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003421 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003422 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003423 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003424 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003425 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003426 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003427 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003428 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003429 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003430 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003431 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003432 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003433 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003434 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003435 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003436 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003437 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003438 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003439 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003440 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003441 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003442 { .id = 0x11060428, .name = "VT1718S",
3443 .patch = patch_vt1718S},
3444 { .id = 0x11064428, .name = "VT1718S",
3445 .patch = patch_vt1718S},
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08003446 { .id = 0x11060441, .name = "VT2020",
3447 .patch = patch_vt1718S},
3448 { .id = 0x11064441, .name = "VT1828S",
3449 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003450 { .id = 0x11060433, .name = "VT1716S",
3451 .patch = patch_vt1716S},
3452 { .id = 0x1106a721, .name = "VT1716S",
3453 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003454 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3455 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003456 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003457 { .id = 0x11060440, .name = "VT1818S",
3458 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003459 { .id = 0x11060446, .name = "VT1802",
3460 .patch = patch_vt2002P},
3461 { .id = 0x11068446, .name = "VT1802",
3462 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003463 {} /* terminator */
3464};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003465
3466MODULE_ALIAS("snd-hda-codec-id:1106*");
3467
3468static struct hda_codec_preset_list via_list = {
3469 .preset = snd_hda_preset_via,
3470 .owner = THIS_MODULE,
3471};
3472
3473MODULE_LICENSE("GPL");
3474MODULE_DESCRIPTION("VIA HD-audio codec");
3475
3476static int __init patch_via_init(void)
3477{
3478 return snd_hda_add_codec_preset(&via_list);
3479}
3480
3481static void __exit patch_via_exit(void)
3482{
3483 snd_hda_delete_codec_preset(&via_list);
3484}
3485
3486module_init(patch_via_init)
3487module_exit(patch_via_exit)