blob: 76142c1389d748887c1b1e3fd81a5c2ad606699f [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
Joseph Chanc577b8a2006-11-29 15:29:40 +010057/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080058#define VT1708_HP_PIN_NID 0x20
59#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010060
Harald Welted7426322008-09-15 22:43:23 +080061enum VIA_HDA_CODEC {
62 UNKNOWN = -1,
63 VT1708,
64 VT1709_10CH,
65 VT1709_6CH,
66 VT1708B_8CH,
67 VT1708B_4CH,
68 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080069 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080070 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080071 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080072 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080073 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080074 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080075 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080076 CODEC_TYPES,
77};
78
Lydia Wang118909562011-03-23 17:57:34 +080079#define VT2002P_COMPATIBLE(spec) \
80 ((spec)->codec_type == VT2002P ||\
81 (spec)->codec_type == VT1812 ||\
82 (spec)->codec_type == VT1802)
83
Takashi Iwai8e3679d2011-06-21 09:01:36 +020084#define MAX_NID_PATH_DEPTH 5
85
Takashi Iwai09a9ad62011-06-21 15:57:44 +020086/* output-path: DAC -> ... -> pin
87 * idx[] contains the source index number of the next widget;
88 * e.g. idx[0] is the index of the DAC selected by path[1] widget
89 * multi[] indicates whether it's a selector widget with multi-connectors
90 * (i.e. the connection selection is mandatory)
91 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
92 */
Takashi Iwai4a796162011-06-17 17:53:38 +020093struct nid_path {
94 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020095 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad62011-06-21 15:57:44 +020096 unsigned char idx[MAX_NID_PATH_DEPTH];
97 unsigned char multi[MAX_NID_PATH_DEPTH];
98 unsigned int vol_ctl;
99 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200100};
101
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200102/* input-path */
103struct via_input {
104 hda_nid_t pin; /* input-pin or aa-mix */
105 int adc_idx; /* ADC index to be used */
106 int mux_idx; /* MUX index (if any) */
107 const char *label; /* input-source label */
108};
109
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800110struct via_spec {
111 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200112 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800113 unsigned int num_mixers;
114
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200115 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800116 unsigned int num_iverbs;
117
Takashi Iwai82673bc2011-06-17 16:24:21 +0200118 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200119 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200120 const struct hda_pcm_stream *stream_analog_playback;
121 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800122
Takashi Iwai82673bc2011-06-17 16:24:21 +0200123 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200124 const struct hda_pcm_stream *stream_digital_playback;
125 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800126
127 /* playback */
128 struct hda_multi_out multiout;
129 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200130 hda_nid_t hp_dac_nid;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200131 int num_active_streams;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800132
Takashi Iwai4a796162011-06-17 17:53:38 +0200133 struct nid_path out_path[4];
134 struct nid_path hp_path;
135 struct nid_path hp_dep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200136 struct nid_path speaker_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200137
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800138 /* capture */
139 unsigned int num_adc_nids;
Takashi Iwaia766d0d2011-06-17 09:01:29 +0200140 hda_nid_t adc_nids[3];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800141 hda_nid_t mux_nids[3];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200142 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800143 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800144
145 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200146 bool dyn_adc_switch;
147 int num_inputs;
148 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800149 unsigned int cur_mux[3];
150
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200151 /* dynamic ADC switching */
152 hda_nid_t cur_adc;
153 unsigned int cur_adc_stream_tag;
154 unsigned int cur_adc_format;
155
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800156 /* PCM information */
157 struct hda_pcm pcm_rec[3];
158
159 /* dynamic controls, init_verbs and input_mux */
160 struct auto_pin_cfg autocfg;
161 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800162 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
163
164 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800165 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800166 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200167 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800168 enum VIA_HDA_CODEC codec_type;
169
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200170 /* smart51 setup */
171 unsigned int smart51_nums;
172 hda_nid_t smart51_pins[2];
173 int smart51_idxs[2];
174 const char *smart51_labels[2];
175 unsigned int smart51_enabled;
176
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800177 /* work to check hp jack state */
178 struct hda_codec *codec;
179 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200180 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800181 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800182
183 void (*set_widgets_power_state)(struct hda_codec *codec);
184
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800185 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200186 int num_loopbacks;
187 struct hda_amp_list loopback_list[8];
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200188
189 /* bind capture-volume */
190 struct hda_bind_ctls *bind_cap_vol;
191 struct hda_bind_ctls *bind_cap_sw;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800192};
193
Lydia Wang0341ccd2011-03-22 16:25:03 +0800194static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100195static struct via_spec * via_new_spec(struct hda_codec *codec)
196{
197 struct via_spec *spec;
198
199 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
200 if (spec == NULL)
201 return NULL;
202
203 codec->spec = spec;
204 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800205 spec->codec_type = get_codec_type(codec);
206 /* VT1708BCE & VT1708S are almost same */
207 if (spec->codec_type == VT1708BCE)
208 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100209 return spec;
210}
211
Lydia Wang744ff5f2009-10-10 19:07:26 +0800212static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800213{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800214 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800215 u16 ven_id = vendor_id >> 16;
216 u16 dev_id = vendor_id & 0xffff;
217 enum VIA_HDA_CODEC codec_type;
218
219 /* get codec type */
220 if (ven_id != 0x1106)
221 codec_type = UNKNOWN;
222 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
223 codec_type = VT1708;
224 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
225 codec_type = VT1709_10CH;
226 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
227 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800228 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800229 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800230 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
231 codec_type = VT1708BCE;
232 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800233 codec_type = VT1708B_4CH;
234 else if ((dev_id & 0xfff) == 0x397
235 && (dev_id >> 12) < 8)
236 codec_type = VT1708S;
237 else if ((dev_id & 0xfff) == 0x398
238 && (dev_id >> 12) < 8)
239 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800240 else if ((dev_id & 0xfff) == 0x428
241 && (dev_id >> 12) < 8)
242 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800243 else if (dev_id == 0x0433 || dev_id == 0xa721)
244 codec_type = VT1716S;
Lydia Wangbb3c6bf2009-10-10 19:08:39 +0800245 else if (dev_id == 0x0441 || dev_id == 0x4441)
246 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800247 else if (dev_id == 0x0438 || dev_id == 0x4438)
248 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800249 else if (dev_id == 0x0448)
250 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800251 else if (dev_id == 0x0440)
252 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800253 else if ((dev_id & 0xfff) == 0x446)
254 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800255 else
256 codec_type = UNKNOWN;
257 return codec_type;
258};
259
Lydia Wangec7e7e42011-03-24 12:43:44 +0800260#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800261#define VIA_HP_EVENT 0x01
262#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200263#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800264
Joseph Chanc577b8a2006-11-29 15:29:40 +0100265enum {
266 VIA_CTL_WIDGET_VOL,
267 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800268 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100269};
270
Takashi Iwaiada509e2011-06-20 15:40:19 +0200271static void analog_low_current_mode(struct hda_codec *codec);
272static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800273
274static void vt1708_start_hp_work(struct via_spec *spec)
275{
276 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
277 return;
278 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200279 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800280 if (!delayed_work_pending(&spec->vt1708_hp_work))
281 schedule_delayed_work(&spec->vt1708_hp_work,
282 msecs_to_jiffies(100));
283}
284
285static void vt1708_stop_hp_work(struct via_spec *spec)
286{
287 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
288 return;
289 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
290 && !is_aa_path_mute(spec->codec))
291 return;
292 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200293 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100294 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800295}
Lydia Wangf5271102009-10-10 19:07:35 +0800296
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800297static void set_widgets_power_state(struct hda_codec *codec)
298{
299 struct via_spec *spec = codec->spec;
300 if (spec->set_widgets_power_state)
301 spec->set_widgets_power_state(codec);
302}
Lydia Wang25eaba22009-10-10 19:08:43 +0800303
Lydia Wangf5271102009-10-10 19:07:35 +0800304static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
305 struct snd_ctl_elem_value *ucontrol)
306{
307 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
308 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
309
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800310 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200311 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800312 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
313 if (is_aa_path_mute(codec))
314 vt1708_start_hp_work(codec->spec);
315 else
316 vt1708_stop_hp_work(codec->spec);
317 }
Lydia Wangf5271102009-10-10 19:07:35 +0800318 return change;
319}
320
321/* modify .put = snd_hda_mixer_amp_switch_put */
322#define ANALOG_INPUT_MUTE \
323 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
324 .name = NULL, \
325 .index = 0, \
326 .info = snd_hda_mixer_amp_switch_info, \
327 .get = snd_hda_mixer_amp_switch_get, \
328 .put = analog_input_switch_put, \
329 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
330
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200331static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100332 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
333 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800334 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100335};
336
Lydia Wangab6734e2009-10-10 19:08:46 +0800337
Joseph Chanc577b8a2006-11-29 15:29:40 +0100338/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200339static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
340 const struct snd_kcontrol_new *tmpl,
341 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100342{
343 struct snd_kcontrol_new *knew;
344
Takashi Iwai603c4012008-07-30 15:01:44 +0200345 snd_array_init(&spec->kctls, sizeof(*knew), 32);
346 knew = snd_array_new(&spec->kctls);
347 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200348 return NULL;
349 *knew = *tmpl;
350 if (!name)
351 name = tmpl->name;
352 if (name) {
353 knew->name = kstrdup(name, GFP_KERNEL);
354 if (!knew->name)
355 return NULL;
356 }
357 return knew;
358}
359
360static int __via_add_control(struct via_spec *spec, int type, const char *name,
361 int idx, unsigned long val)
362{
363 struct snd_kcontrol_new *knew;
364
365 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
366 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100367 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200368 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100369 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100370 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100371 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100372 return 0;
373}
374
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200375#define via_add_control(spec, type, name, val) \
376 __via_add_control(spec, type, name, 0, val)
377
Takashi Iwai291c9e32011-06-17 16:15:26 +0200378#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100379
Takashi Iwai603c4012008-07-30 15:01:44 +0200380static void via_free_kctls(struct hda_codec *codec)
381{
382 struct via_spec *spec = codec->spec;
383
384 if (spec->kctls.list) {
385 struct snd_kcontrol_new *kctl = spec->kctls.list;
386 int i;
387 for (i = 0; i < spec->kctls.used; i++)
388 kfree(kctl[i].name);
389 }
390 snd_array_free(&spec->kctls);
391}
392
Joseph Chanc577b8a2006-11-29 15:29:40 +0100393/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800394static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200395 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100396{
397 char name[32];
398 int err;
399
400 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200401 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100402 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
403 if (err < 0)
404 return err;
405 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200406 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100407 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
408 if (err < 0)
409 return err;
410 return 0;
411}
412
Takashi Iwai5d417622011-06-20 11:32:27 +0200413#define get_connection_index(codec, mux, nid) \
Takashi Iwai8d087c72011-06-28 12:45:47 +0200414 snd_hda_get_conn_index(codec, mux, nid, 0)
Takashi Iwai5d417622011-06-20 11:32:27 +0200415
Takashi Iwai8df2a312011-06-21 11:48:29 +0200416static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
417 unsigned int mask)
418{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200419 unsigned int caps;
420 if (!nid)
421 return false;
422 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200423 if (dir == HDA_INPUT)
424 caps &= AC_WCAP_IN_AMP;
425 else
426 caps &= AC_WCAP_OUT_AMP;
427 if (!caps)
428 return false;
429 if (query_amp_caps(codec, nid, dir) & mask)
430 return true;
431 return false;
432}
433
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200434#define have_mute(codec, nid, dir) \
435 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200436
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200437/* enable/disable the output-route */
438static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
439 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200440{
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200441 int i;
442 for (i = 0; i < path->depth; i++) {
443 hda_nid_t src, dst;
444 int idx = path->idx[i];
445 src = path->path[i];
446 if (i < path->depth - 1)
447 dst = path->path[i + 1];
448 else
449 dst = 0;
450 if (enable && path->multi[i])
451 snd_hda_codec_write(codec, dst, 0,
452 AC_VERB_SET_CONNECT_SEL, idx);
453 if (have_mute(codec, dst, HDA_INPUT)) {
454 int val = enable ? AMP_IN_UNMUTE(idx) :
455 AMP_IN_MUTE(idx);
456 snd_hda_codec_write(codec, dst, 0,
457 AC_VERB_SET_AMP_GAIN_MUTE, val);
458 }
459 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
460 continue;
461 if (have_mute(codec, src, HDA_OUTPUT)) {
462 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
463 snd_hda_codec_write(codec, src, 0,
464 AC_VERB_SET_AMP_GAIN_MUTE, val);
465 }
466 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200467}
468
469/* set the given pin as output */
470static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
471 int pin_type)
472{
473 if (!pin)
474 return;
475 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
476 pin_type);
477 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
478 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200479 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100480}
481
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200482static void via_auto_init_output(struct hda_codec *codec,
483 struct nid_path *path, int pin_type,
484 bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200485{
486 struct via_spec *spec = codec->spec;
487 unsigned int caps;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200488 hda_nid_t pin, nid;
489 int i, idx;
Takashi Iwai5d417622011-06-20 11:32:27 +0200490
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200491 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200492 return;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200493 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200494
495 init_output_pin(codec, pin, pin_type);
496 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
497 if (caps & AC_AMPCAP_MUTE) {
498 unsigned int val;
499 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
500 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
501 AMP_OUT_MUTE | val);
502 }
503
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200504 activate_output_path(codec, path, true, force);
505
506 /* initialize the AA-path */
507 if (!spec->aa_mix_nid)
508 return;
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200509 for (i = path->depth - 1; i > 0; i--) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200510 nid = path->path[i];
511 idx = get_connection_index(codec, nid, spec->aa_mix_nid);
512 if (idx >= 0) {
513 if (have_mute(codec, nid, HDA_INPUT))
514 snd_hda_codec_write(codec, nid, 0,
515 AC_VERB_SET_AMP_GAIN_MUTE,
516 AMP_IN_UNMUTE(idx));
517 break;
518 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200519 }
520}
521
Joseph Chanc577b8a2006-11-29 15:29:40 +0100522static void via_auto_init_multi_out(struct hda_codec *codec)
523{
524 struct via_spec *spec = codec->spec;
525 int i;
526
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200527 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200528 via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, true);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100529}
530
531static void via_auto_init_hp_out(struct hda_codec *codec)
532{
533 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100534
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200535 if (!spec->hp_dac_nid) {
536 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
537 return;
538 }
539 if (spec->hp_independent_mode) {
540 activate_output_path(codec, &spec->hp_dep_path, false, false);
541 via_auto_init_output(codec, &spec->hp_path, PIN_HP, true);
542 } else {
543 activate_output_path(codec, &spec->hp_path, false, false);
544 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
545 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100546}
547
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200548static void via_auto_init_speaker_out(struct hda_codec *codec)
549{
550 struct via_spec *spec = codec->spec;
551
552 if (spec->autocfg.speaker_outs)
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200553 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, true);
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200554}
555
Takashi Iwaif4a78282011-06-17 18:46:48 +0200556static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200557
Joseph Chanc577b8a2006-11-29 15:29:40 +0100558static void via_auto_init_analog_input(struct hda_codec *codec)
559{
560 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200561 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200562 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200563 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200564 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100565
Takashi Iwai096a8852011-06-20 12:09:02 +0200566 /* init ADCs */
567 for (i = 0; i < spec->num_adc_nids; i++) {
568 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
569 AC_VERB_SET_AMP_GAIN_MUTE,
570 AMP_IN_UNMUTE(0));
571 }
572
573 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200574 for (i = 0; i < cfg->num_inputs; i++) {
575 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200576 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200577 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100578 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200579 ctl = PIN_VREF50;
580 else
581 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100582 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200583 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100584 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200585
586 /* init input-src */
587 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200588 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
589 if (spec->mux_nids[adc_idx]) {
590 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
591 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
592 AC_VERB_SET_CONNECT_SEL,
593 mux_idx);
594 }
595 if (spec->dyn_adc_switch)
596 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200597 }
598
599 /* init aa-mixer */
600 if (!spec->aa_mix_nid)
601 return;
602 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
603 ARRAY_SIZE(conn));
604 for (i = 0; i < num_conns; i++) {
605 unsigned int caps = get_wcaps(codec, conn[i]);
606 if (get_wcaps_type(caps) == AC_WID_PIN)
607 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
608 AC_VERB_SET_AMP_GAIN_MUTE,
609 AMP_IN_MUTE(i));
610 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100611}
Lydia Wangf5271102009-10-10 19:07:35 +0800612
613static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
614 unsigned int *affected_parm)
615{
616 unsigned parm;
617 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
618 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
619 >> AC_DEFCFG_MISC_SHIFT
620 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800621 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200622 unsigned present = 0;
623
624 no_presence |= spec->no_pin_power_ctl;
625 if (!no_presence)
626 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200627 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800628 || ((no_presence || present)
629 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800630 *affected_parm = AC_PWRST_D0; /* if it's connected */
631 parm = AC_PWRST_D0;
632 } else
633 parm = AC_PWRST_D3;
634
635 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
636}
637
Takashi Iwai24088a52011-06-17 16:59:21 +0200638static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
639 struct snd_ctl_elem_info *uinfo)
640{
641 static const char * const texts[] = {
642 "Disabled", "Enabled"
643 };
644
645 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
646 uinfo->count = 1;
647 uinfo->value.enumerated.items = 2;
648 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
649 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
650 strcpy(uinfo->value.enumerated.name,
651 texts[uinfo->value.enumerated.item]);
652 return 0;
653}
654
655static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
656 struct snd_ctl_elem_value *ucontrol)
657{
658 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
659 struct via_spec *spec = codec->spec;
660 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
661 return 0;
662}
663
664static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
665 struct snd_ctl_elem_value *ucontrol)
666{
667 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
668 struct via_spec *spec = codec->spec;
669 unsigned int val = !ucontrol->value.enumerated.item[0];
670
671 if (val == spec->no_pin_power_ctl)
672 return 0;
673 spec->no_pin_power_ctl = val;
674 set_widgets_power_state(codec);
675 return 1;
676}
677
678static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
679 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
680 .name = "Dynamic Power-Control",
681 .info = via_pin_power_ctl_info,
682 .get = via_pin_power_ctl_get,
683 .put = via_pin_power_ctl_put,
684};
685
686
Harald Welte0aa62ae2008-09-09 15:58:27 +0800687static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
688 struct snd_ctl_elem_info *uinfo)
689{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200690 static const char * const texts[] = { "OFF", "ON" };
691
692 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
693 uinfo->count = 1;
694 uinfo->value.enumerated.items = 2;
695 if (uinfo->value.enumerated.item >= 2)
696 uinfo->value.enumerated.item = 1;
697 strcpy(uinfo->value.enumerated.name,
698 texts[uinfo->value.enumerated.item]);
699 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800700}
701
702static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
703 struct snd_ctl_elem_value *ucontrol)
704{
705 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800706 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800707
Takashi Iwaiece8d042011-06-19 16:24:21 +0200708 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800709 return 0;
710}
711
Harald Welte0aa62ae2008-09-09 15:58:27 +0800712static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
713 struct snd_ctl_elem_value *ucontrol)
714{
715 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
716 struct via_spec *spec = codec->spec;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200717
718 spec->hp_independent_mode = !!ucontrol->value.enumerated.item[0];
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200719 if (spec->hp_independent_mode) {
720 activate_output_path(codec, &spec->hp_dep_path, false, false);
721 activate_output_path(codec, &spec->hp_path, true, false);
722 } else {
723 activate_output_path(codec, &spec->hp_path, false, false);
724 activate_output_path(codec, &spec->hp_dep_path, true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200725 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800726
Lydia Wangce0e5a92011-03-22 16:22:37 +0800727 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800728 set_widgets_power_state(codec);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800729 return 0;
730}
731
Takashi Iwaiece8d042011-06-19 16:24:21 +0200732static const struct snd_kcontrol_new via_hp_mixer = {
733 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
734 .name = "Independent HP",
735 .info = via_independent_hp_info,
736 .get = via_independent_hp_get,
737 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800738};
739
Takashi Iwai3d83e572010-04-14 14:36:23 +0200740static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100741{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200742 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100743 struct snd_kcontrol_new *knew;
744 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100745
Takashi Iwaiece8d042011-06-19 16:24:21 +0200746 nid = spec->autocfg.hp_pins[0];
747 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200748 if (knew == NULL)
749 return -ENOMEM;
750
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100751 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100752
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100753 return 0;
754}
755
Lydia Wang1564b282009-10-10 19:07:52 +0800756static void notify_aa_path_ctls(struct hda_codec *codec)
757{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200758 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800759 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800760
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200761 for (i = 0; i < spec->smart51_nums; i++) {
762 struct snd_kcontrol *ctl;
763 struct snd_ctl_elem_id id;
764 memset(&id, 0, sizeof(id));
765 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
766 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800767 ctl = snd_hda_find_mixer_ctl(codec, id.name);
768 if (ctl)
769 snd_ctl_notify(codec->bus->card,
770 SNDRV_CTL_EVENT_MASK_VALUE,
771 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800772 }
773}
774
775static void mute_aa_path(struct hda_codec *codec, int mute)
776{
777 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200778 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800779 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200780
Lydia Wang1564b282009-10-10 19:07:52 +0800781 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200782 for (i = 0; i < spec->smart51_nums; i++) {
783 if (spec->smart51_idxs[i] < 0)
784 continue;
785 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
786 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800787 HDA_AMP_MUTE, val);
788 }
789}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200790
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200791static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
792{
793 struct via_spec *spec = codec->spec;
794 int i;
795
796 for (i = 0; i < spec->smart51_nums; i++)
797 if (spec->smart51_pins[i] == pin)
798 return true;
799 return false;
800}
801
Lydia Wang1564b282009-10-10 19:07:52 +0800802static int via_smart51_get(struct snd_kcontrol *kcontrol,
803 struct snd_ctl_elem_value *ucontrol)
804{
805 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
806 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800807
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200808 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800809 return 0;
810}
811
812static int via_smart51_put(struct snd_kcontrol *kcontrol,
813 struct snd_ctl_elem_value *ucontrol)
814{
815 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
816 struct via_spec *spec = codec->spec;
817 int out_in = *ucontrol->value.integer.value
818 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800819 int i;
820
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200821 for (i = 0; i < spec->smart51_nums; i++) {
822 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200823 unsigned int parm;
824
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200825 parm = snd_hda_codec_read(codec, nid, 0,
826 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
827 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
828 parm |= out_in;
829 snd_hda_codec_write(codec, nid, 0,
830 AC_VERB_SET_PIN_WIDGET_CONTROL,
831 parm);
832 if (out_in == AC_PINCTL_OUT_EN) {
833 mute_aa_path(codec, 1);
834 notify_aa_path_ctls(codec);
835 }
Lydia Wang1564b282009-10-10 19:07:52 +0800836 }
837 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800838 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800839 return 1;
840}
841
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200842static const struct snd_kcontrol_new via_smart51_mixer = {
843 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
844 .name = "Smart 5.1",
845 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200846 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200847 .get = via_smart51_get,
848 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800849};
850
Takashi Iwaif4a78282011-06-17 18:46:48 +0200851static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100852{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200853 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100854
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200855 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800856 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200857 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100858 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100859 return 0;
860}
861
Takashi Iwaiada509e2011-06-20 15:40:19 +0200862/* check AA path's mute status */
863static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800864{
Lydia Wangf5271102009-10-10 19:07:35 +0800865 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200866 const struct hda_amp_list *p;
867 int i, ch, v;
868
869 for (i = 0; i < spec->num_loopbacks; i++) {
870 p = &spec->loopback_list[i];
871 for (ch = 0; ch < 2; ch++) {
872 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
873 p->idx);
874 if (!(v & HDA_AMP_MUTE) && v > 0)
875 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800876 }
877 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200878 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800879}
880
881/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200882static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800883{
884 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200885 bool enable;
886 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800887
Takashi Iwaiada509e2011-06-20 15:40:19 +0200888 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800889
890 /* decide low current mode's verb & parameter */
891 switch (spec->codec_type) {
892 case VT1708B_8CH:
893 case VT1708B_4CH:
894 verb = 0xf70;
895 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
896 break;
897 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800898 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800899 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800900 verb = 0xf73;
901 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
902 break;
903 case VT1702:
904 verb = 0xf73;
905 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
906 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800907 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800908 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800909 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800910 verb = 0xf93;
911 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
912 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800913 default:
914 return; /* other codecs are not supported */
915 }
916 /* send verb */
917 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
918}
919
Joseph Chanc577b8a2006-11-29 15:29:40 +0100920/*
921 * generic initialization of ADC, input mixers and output mixers
922 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200923static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800924 /* power down jack detect function */
925 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +0100926 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100927};
928
Takashi Iwaiada509e2011-06-20 15:40:19 +0200929static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200930{
Takashi Iwaiada509e2011-06-20 15:40:19 +0200931 struct via_spec *spec = codec->spec;
932
933 if (active)
934 spec->num_active_streams++;
935 else
936 spec->num_active_streams--;
937 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200938}
939
Takashi Iwaiece8d042011-06-19 16:24:21 +0200940static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100941 struct hda_codec *codec,
942 struct snd_pcm_substream *substream)
943{
944 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200945 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +0200946
947 if (!spec->hp_independent_mode)
948 spec->multiout.hp_nid = spec->hp_dac_nid;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200949 set_stream_active(codec, true);
950 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
951 hinfo);
952 if (err < 0) {
953 spec->multiout.hp_nid = 0;
954 set_stream_active(codec, false);
955 return err;
956 }
957 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100958}
959
Takashi Iwaiece8d042011-06-19 16:24:21 +0200960static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +0200961 struct hda_codec *codec,
962 struct snd_pcm_substream *substream)
963{
Takashi Iwaiece8d042011-06-19 16:24:21 +0200964 struct via_spec *spec = codec->spec;
965
966 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200967 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +0200968 return 0;
969}
970
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200971static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
972 struct hda_codec *codec,
973 struct snd_pcm_substream *substream)
974{
975 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200976
Takashi Iwaiece8d042011-06-19 16:24:21 +0200977 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200978 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +0200979 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
980 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200981 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +0200982 return 0;
983}
984
985static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
986 struct hda_codec *codec,
987 struct snd_pcm_substream *substream)
988{
Takashi Iwaiada509e2011-06-20 15:40:19 +0200989 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200990 return 0;
991}
992
993static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
994 struct hda_codec *codec,
995 unsigned int stream_tag,
996 unsigned int format,
997 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +0800998{
999 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001000
Takashi Iwaiece8d042011-06-19 16:24:21 +02001001 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1002 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001003 vt1708_start_hp_work(spec);
1004 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001005}
1006
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001007static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1008 struct hda_codec *codec,
1009 unsigned int stream_tag,
1010 unsigned int format,
1011 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001012{
1013 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001014
Takashi Iwaiece8d042011-06-19 16:24:21 +02001015 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1016 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001017 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001018 return 0;
1019}
1020
1021static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1022 struct hda_codec *codec,
1023 struct snd_pcm_substream *substream)
1024{
1025 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001026
Takashi Iwaiece8d042011-06-19 16:24:21 +02001027 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001028 vt1708_stop_hp_work(spec);
1029 return 0;
1030}
1031
1032static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1033 struct hda_codec *codec,
1034 struct snd_pcm_substream *substream)
1035{
1036 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001037
Takashi Iwaiece8d042011-06-19 16:24:21 +02001038 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001039 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001040 return 0;
1041}
1042
Joseph Chanc577b8a2006-11-29 15:29:40 +01001043/*
1044 * Digital out
1045 */
1046static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1047 struct hda_codec *codec,
1048 struct snd_pcm_substream *substream)
1049{
1050 struct via_spec *spec = codec->spec;
1051 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1052}
1053
1054static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1055 struct hda_codec *codec,
1056 struct snd_pcm_substream *substream)
1057{
1058 struct via_spec *spec = codec->spec;
1059 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1060}
1061
Harald Welte5691ec72008-09-15 22:42:26 +08001062static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001063 struct hda_codec *codec,
1064 unsigned int stream_tag,
1065 unsigned int format,
1066 struct snd_pcm_substream *substream)
1067{
1068 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001069 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1070 stream_tag, format, substream);
1071}
Harald Welte5691ec72008-09-15 22:42:26 +08001072
Takashi Iwai9da29272009-05-07 16:31:14 +02001073static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1074 struct hda_codec *codec,
1075 struct snd_pcm_substream *substream)
1076{
1077 struct via_spec *spec = codec->spec;
1078 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001079 return 0;
1080}
1081
Joseph Chanc577b8a2006-11-29 15:29:40 +01001082/*
1083 * Analog capture
1084 */
1085static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1086 struct hda_codec *codec,
1087 unsigned int stream_tag,
1088 unsigned int format,
1089 struct snd_pcm_substream *substream)
1090{
1091 struct via_spec *spec = codec->spec;
1092
1093 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1094 stream_tag, 0, format);
1095 return 0;
1096}
1097
1098static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1099 struct hda_codec *codec,
1100 struct snd_pcm_substream *substream)
1101{
1102 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001103 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001104 return 0;
1105}
1106
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001107/* analog capture with dynamic ADC switching */
1108static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1109 struct hda_codec *codec,
1110 unsigned int stream_tag,
1111 unsigned int format,
1112 struct snd_pcm_substream *substream)
1113{
1114 struct via_spec *spec = codec->spec;
1115 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1116
1117 spec->cur_adc = spec->adc_nids[adc_idx];
1118 spec->cur_adc_stream_tag = stream_tag;
1119 spec->cur_adc_format = format;
1120 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
1121 return 0;
1122}
1123
1124static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1125 struct hda_codec *codec,
1126 struct snd_pcm_substream *substream)
1127{
1128 struct via_spec *spec = codec->spec;
1129
1130 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1131 spec->cur_adc = 0;
1132 return 0;
1133}
1134
1135/* re-setup the stream if running; called from input-src put */
1136static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1137{
1138 struct via_spec *spec = codec->spec;
1139 int adc_idx = spec->inputs[cur].adc_idx;
1140 hda_nid_t adc = spec->adc_nids[adc_idx];
1141
1142 if (spec->cur_adc && spec->cur_adc != adc) {
1143 /* stream is running, let's swap the current ADC */
1144 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1145 spec->cur_adc = adc;
1146 snd_hda_codec_setup_stream(codec, adc,
1147 spec->cur_adc_stream_tag, 0,
1148 spec->cur_adc_format);
1149 return true;
1150 }
1151 return false;
1152}
1153
Takashi Iwai9af74212011-06-18 16:17:45 +02001154static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001155 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001156 .channels_min = 2,
1157 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001158 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001159 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001160 .open = via_playback_multi_pcm_open,
1161 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001162 .prepare = via_playback_multi_pcm_prepare,
1163 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001164 },
1165};
1166
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001167static const struct hda_pcm_stream via_pcm_hp_playback = {
1168 .substreams = 1,
1169 .channels_min = 2,
1170 .channels_max = 2,
1171 /* NID is set in via_build_pcms */
1172 .ops = {
1173 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001174 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001175 .prepare = via_playback_hp_pcm_prepare,
1176 .cleanup = via_playback_hp_pcm_cleanup
1177 },
1178};
1179
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001180static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001181 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001182 .channels_min = 2,
1183 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001184 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001185 /* We got noisy outputs on the right channel on VT1708 when
1186 * 24bit samples are used. Until any workaround is found,
1187 * disable the 24bit format, so far.
1188 */
1189 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1190 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001191 .open = via_playback_multi_pcm_open,
1192 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001193 .prepare = via_playback_multi_pcm_prepare,
1194 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001195 },
1196};
1197
Takashi Iwai9af74212011-06-18 16:17:45 +02001198static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001199 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001200 .channels_min = 2,
1201 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001202 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001203 .ops = {
1204 .prepare = via_capture_pcm_prepare,
1205 .cleanup = via_capture_pcm_cleanup
1206 },
1207};
1208
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001209static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1210 .substreams = 1,
1211 .channels_min = 2,
1212 .channels_max = 2,
1213 /* NID is set in via_build_pcms */
1214 .ops = {
1215 .prepare = via_dyn_adc_capture_pcm_prepare,
1216 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1217 },
1218};
1219
Takashi Iwai9af74212011-06-18 16:17:45 +02001220static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001221 .substreams = 1,
1222 .channels_min = 2,
1223 .channels_max = 2,
1224 /* NID is set in via_build_pcms */
1225 .ops = {
1226 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001227 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001228 .prepare = via_dig_playback_pcm_prepare,
1229 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001230 },
1231};
1232
Takashi Iwai9af74212011-06-18 16:17:45 +02001233static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001234 .substreams = 1,
1235 .channels_min = 2,
1236 .channels_max = 2,
1237};
1238
Takashi Iwai370bafb2011-06-20 12:47:45 +02001239/*
1240 * slave controls for virtual master
1241 */
1242static const char * const via_slave_vols[] = {
1243 "Front Playback Volume",
1244 "Surround Playback Volume",
1245 "Center Playback Volume",
1246 "LFE Playback Volume",
1247 "Side Playback Volume",
1248 "Headphone Playback Volume",
1249 "Speaker Playback Volume",
1250 NULL,
1251};
1252
1253static const char * const via_slave_sws[] = {
1254 "Front Playback Switch",
1255 "Surround Playback Switch",
1256 "Center Playback Switch",
1257 "LFE Playback Switch",
1258 "Side Playback Switch",
1259 "Headphone Playback Switch",
1260 "Speaker Playback Switch",
1261 NULL,
1262};
1263
Joseph Chanc577b8a2006-11-29 15:29:40 +01001264static int via_build_controls(struct hda_codec *codec)
1265{
1266 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001267 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001268 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001269
Takashi Iwai24088a52011-06-17 16:59:21 +02001270 if (spec->set_widgets_power_state)
1271 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1272 return -ENOMEM;
1273
Joseph Chanc577b8a2006-11-29 15:29:40 +01001274 for (i = 0; i < spec->num_mixers; i++) {
1275 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1276 if (err < 0)
1277 return err;
1278 }
1279
1280 if (spec->multiout.dig_out_nid) {
1281 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001282 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001283 spec->multiout.dig_out_nid);
1284 if (err < 0)
1285 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001286 err = snd_hda_create_spdif_share_sw(codec,
1287 &spec->multiout);
1288 if (err < 0)
1289 return err;
1290 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001291 }
1292 if (spec->dig_in_nid) {
1293 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1294 if (err < 0)
1295 return err;
1296 }
Lydia Wang17314372009-10-10 19:07:37 +08001297
Takashi Iwai370bafb2011-06-20 12:47:45 +02001298 /* if we have no master control, let's create it */
1299 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1300 unsigned int vmaster_tlv[4];
1301 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1302 HDA_OUTPUT, vmaster_tlv);
1303 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1304 vmaster_tlv, via_slave_vols);
1305 if (err < 0)
1306 return err;
1307 }
1308 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1309 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1310 NULL, via_slave_sws);
1311 if (err < 0)
1312 return err;
1313 }
1314
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001315 /* assign Capture Source enums to NID */
1316 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1317 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001318 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001319 if (err < 0)
1320 return err;
1321 }
1322
Lydia Wang17314372009-10-10 19:07:37 +08001323 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001324 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001325 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001326
Takashi Iwai603c4012008-07-30 15:01:44 +02001327 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001328 return 0;
1329}
1330
1331static int via_build_pcms(struct hda_codec *codec)
1332{
1333 struct via_spec *spec = codec->spec;
1334 struct hda_pcm *info = spec->pcm_rec;
1335
1336 codec->num_pcms = 1;
1337 codec->pcm_info = info;
1338
Takashi Iwai82673bc2011-06-17 16:24:21 +02001339 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1340 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001341 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001342
1343 if (!spec->stream_analog_playback)
1344 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001345 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001346 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001347 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1348 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001349 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1350 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001351
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001352 if (!spec->stream_analog_capture) {
1353 if (spec->dyn_adc_switch)
1354 spec->stream_analog_capture =
1355 &via_pcm_dyn_adc_analog_capture;
1356 else
1357 spec->stream_analog_capture = &via_pcm_analog_capture;
1358 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001359 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1360 *spec->stream_analog_capture;
1361 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001362 if (!spec->dyn_adc_switch)
1363 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1364 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001365
1366 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1367 codec->num_pcms++;
1368 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001369 snprintf(spec->stream_name_digital,
1370 sizeof(spec->stream_name_digital),
1371 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001372 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001373 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001374 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001375 if (!spec->stream_digital_playback)
1376 spec->stream_digital_playback =
1377 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001378 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001379 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001380 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1381 spec->multiout.dig_out_nid;
1382 }
1383 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001384 if (!spec->stream_digital_capture)
1385 spec->stream_digital_capture =
1386 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001387 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001388 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001389 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1390 spec->dig_in_nid;
1391 }
1392 }
1393
Takashi Iwaiece8d042011-06-19 16:24:21 +02001394 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001395 codec->num_pcms++;
1396 info++;
1397 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1398 "%s HP", codec->chip_name);
1399 info->name = spec->stream_name_hp;
1400 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1401 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001402 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001403 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001404 return 0;
1405}
1406
1407static void via_free(struct hda_codec *codec)
1408{
1409 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001410
1411 if (!spec)
1412 return;
1413
Takashi Iwai603c4012008-07-30 15:01:44 +02001414 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001415 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001416 kfree(spec->bind_cap_vol);
1417 kfree(spec->bind_cap_sw);
1418 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001419}
1420
Takashi Iwai64be2852011-06-17 16:51:39 +02001421/* mute/unmute outputs */
1422static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1423 hda_nid_t *pins, bool mute)
1424{
1425 int i;
1426 for (i = 0; i < num_pins; i++)
1427 snd_hda_codec_write(codec, pins[i], 0,
1428 AC_VERB_SET_PIN_WIDGET_CONTROL,
1429 mute ? 0 : PIN_OUT);
1430}
1431
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001432/* mute internal speaker if line-out is plugged */
1433static void via_line_automute(struct hda_codec *codec, int present)
1434{
1435 struct via_spec *spec = codec->spec;
1436
1437 if (!spec->autocfg.speaker_outs)
1438 return;
1439 if (!present)
1440 present = snd_hda_jack_detect(codec,
1441 spec->autocfg.line_out_pins[0]);
1442 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1443 spec->autocfg.speaker_pins,
1444 present);
1445}
1446
Harald Welte69e52a82008-09-09 15:57:32 +08001447/* mute internal speaker if HP is plugged */
1448static void via_hp_automute(struct hda_codec *codec)
1449{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001450 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001451 struct via_spec *spec = codec->spec;
1452
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001453 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001454 int nums;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001455 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001456 if (spec->smart51_enabled)
1457 nums = spec->autocfg.line_outs + spec->smart51_nums;
1458 else
1459 nums = spec->autocfg.line_outs;
1460 toggle_output_mutes(codec, nums,
Takashi Iwai64be2852011-06-17 16:51:39 +02001461 spec->autocfg.line_out_pins,
1462 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001463 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001464 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001465}
1466
Harald Welte69e52a82008-09-09 15:57:32 +08001467static void via_gpio_control(struct hda_codec *codec)
1468{
1469 unsigned int gpio_data;
1470 unsigned int vol_counter;
1471 unsigned int vol;
1472 unsigned int master_vol;
1473
1474 struct via_spec *spec = codec->spec;
1475
1476 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1477 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1478
1479 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1480 0xF84, 0) & 0x3F0000) >> 16;
1481
1482 vol = vol_counter & 0x1F;
1483 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1484 AC_VERB_GET_AMP_GAIN_MUTE,
1485 AC_AMP_GET_INPUT);
1486
1487 if (gpio_data == 0x02) {
1488 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001489 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1490 AC_VERB_SET_PIN_WIDGET_CONTROL,
1491 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001492 if (vol_counter & 0x20) {
1493 /* decrease volume */
1494 if (vol > master_vol)
1495 vol = master_vol;
1496 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1497 0, HDA_AMP_VOLMASK,
1498 master_vol-vol);
1499 } else {
1500 /* increase volume */
1501 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1502 HDA_AMP_VOLMASK,
1503 ((master_vol+vol) > 0x2A) ? 0x2A :
1504 (master_vol+vol));
1505 }
1506 } else if (!(gpio_data & 0x02)) {
1507 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001508 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1509 AC_VERB_SET_PIN_WIDGET_CONTROL,
1510 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001511 }
1512}
1513
1514/* unsolicited event for jack sensing */
1515static void via_unsol_event(struct hda_codec *codec,
1516 unsigned int res)
1517{
1518 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001519
Lydia Wanga34df192009-10-10 19:08:01 +08001520 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001521 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001522
1523 res &= ~VIA_JACK_EVENT;
1524
1525 if (res == VIA_HP_EVENT)
1526 via_hp_automute(codec);
1527 else if (res == VIA_GPIO_EVENT)
1528 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001529 else if (res == VIA_LINE_EVENT)
1530 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001531}
1532
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001533#ifdef SND_HDA_NEEDS_RESUME
1534static int via_suspend(struct hda_codec *codec, pm_message_t state)
1535{
1536 struct via_spec *spec = codec->spec;
1537 vt1708_stop_hp_work(spec);
1538 return 0;
1539}
1540#endif
1541
Takashi Iwaicb53c622007-08-10 17:21:45 +02001542#ifdef CONFIG_SND_HDA_POWER_SAVE
1543static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1544{
1545 struct via_spec *spec = codec->spec;
1546 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1547}
1548#endif
1549
Joseph Chanc577b8a2006-11-29 15:29:40 +01001550/*
1551 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001552
1553static int via_init(struct hda_codec *codec);
1554
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001555static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001556 .build_controls = via_build_controls,
1557 .build_pcms = via_build_pcms,
1558 .init = via_init,
1559 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001560 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001561#ifdef SND_HDA_NEEDS_RESUME
1562 .suspend = via_suspend,
1563#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001564#ifdef CONFIG_SND_HDA_POWER_SAVE
1565 .check_power_status = via_check_power_status,
1566#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001567};
1568
Takashi Iwai4a796162011-06-17 17:53:38 +02001569static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001570{
Takashi Iwai4a796162011-06-17 17:53:38 +02001571 struct via_spec *spec = codec->spec;
1572 int i;
1573
1574 for (i = 0; i < spec->multiout.num_dacs; i++) {
1575 if (spec->multiout.dac_nids[i] == dac)
1576 return false;
1577 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001578 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001579 return false;
1580 return true;
1581}
1582
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001583static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001584 hda_nid_t target_dac, struct nid_path *path,
1585 int depth, int wid_type)
1586{
1587 hda_nid_t conn[8];
1588 int i, nums;
1589
1590 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1591 for (i = 0; i < nums; i++) {
1592 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1593 continue;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001594 if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
1595 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001596 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001597 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001598 return false;
1599 for (i = 0; i < nums; i++) {
1600 unsigned int type;
1601 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1602 if (type == AC_WID_AUD_OUT ||
1603 (wid_type != -1 && type != wid_type))
1604 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001605 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001606 path, depth + 1, AC_WID_AUD_SEL))
1607 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001608 }
1609 return false;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001610
1611 found:
1612 path->path[path->depth] = conn[i];
1613 path->idx[path->depth] = i;
1614 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1615 path->multi[path->depth] = 1;
1616 path->depth++;
1617 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001618}
1619
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001620static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1621 hda_nid_t target_dac, struct nid_path *path)
1622{
1623 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1624 path->path[path->depth] = nid;
1625 path->depth++;
1626 return true;
1627 }
1628 return false;
1629}
1630
Takashi Iwai4a796162011-06-17 17:53:38 +02001631static int via_auto_fill_dac_nids(struct hda_codec *codec)
1632{
1633 struct via_spec *spec = codec->spec;
1634 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001635 int i;
1636 hda_nid_t nid;
1637
Joseph Chanc577b8a2006-11-29 15:29:40 +01001638 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001639 spec->multiout.num_dacs = cfg->line_outs;
1640 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001641 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001642 if (!nid)
1643 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001644 if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
1645 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001646 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001647 return 0;
1648}
1649
Takashi Iwai4a796162011-06-17 17:53:38 +02001650static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001651 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001652{
Takashi Iwai4a796162011-06-17 17:53:38 +02001653 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001654 char name[32];
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001655 hda_nid_t dac, pin, sel, nid;
1656 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001657
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001658 dac = check_dac ? path->path[0] : 0;
1659 pin = path->path[path->depth - 1];
1660 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001661
Takashi Iwai8df2a312011-06-21 11:48:29 +02001662 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001663 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001664 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001665 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001666 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1667 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001668 else
1669 nid = 0;
1670 if (nid) {
1671 sprintf(name, "%s Playback Volume", pfx);
1672 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001673 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001674 if (err < 0)
1675 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001676 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001677 }
1678
Takashi Iwai8df2a312011-06-21 11:48:29 +02001679 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001680 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001681 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001682 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001683 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1684 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001685 else
1686 nid = 0;
1687 if (nid) {
1688 sprintf(name, "%s Playback Switch", pfx);
1689 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1690 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1691 if (err < 0)
1692 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001693 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001694 }
1695 return 0;
1696}
1697
Takashi Iwaif4a78282011-06-17 18:46:48 +02001698static void mangle_smart51(struct hda_codec *codec)
1699{
1700 struct via_spec *spec = codec->spec;
1701 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001702 struct auto_pin_cfg_item *ins = cfg->inputs;
1703 int i, j, nums, attr;
1704 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001705
Takashi Iwai0f98c242011-06-21 12:51:33 +02001706 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1707 nums = 0;
1708 for (i = 0; i < cfg->num_inputs; i++) {
1709 unsigned int def;
1710 if (ins[i].type > AUTO_PIN_LINE_IN)
1711 continue;
1712 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1713 if (snd_hda_get_input_pin_attr(def) != attr)
1714 continue;
1715 for (j = 0; j < nums; j++)
1716 if (ins[pins[j]].type < ins[i].type) {
1717 memmove(pins + j + 1, pins + j,
1718 (nums - j - 1) * sizeof(int));
1719 break;
1720 }
1721 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001722 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001723 }
1724 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001725 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001726 for (i = 0; i < nums; i++) {
1727 hda_nid_t pin = ins[pins[i]].pin;
1728 spec->smart51_pins[spec->smart51_nums++] = pin;
1729 cfg->line_out_pins[cfg->line_outs++] = pin;
1730 if (cfg->line_outs == 3)
1731 break;
1732 }
1733 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001734 }
1735}
1736
Takashi Iwai4a796162011-06-17 17:53:38 +02001737/* add playback controls from the parsed DAC table */
1738static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1739{
1740 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001741 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001742 static const char * const chname[4] = {
1743 "Front", "Surround", "C/LFE", "Side"
1744 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001745 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001746 int old_line_outs;
1747
1748 /* check smart51 */
1749 old_line_outs = cfg->line_outs;
1750 if (cfg->line_outs == 1)
1751 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001752
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001753 err = via_auto_fill_dac_nids(codec);
1754 if (err < 0)
1755 return err;
1756
Takashi Iwai4a796162011-06-17 17:53:38 +02001757 for (i = 0; i < cfg->line_outs; i++) {
1758 hda_nid_t pin, dac;
1759 pin = cfg->line_out_pins[i];
1760 dac = spec->multiout.dac_nids[i];
1761 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001762 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001763 if (i == HDA_CLFE) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001764 err = create_ch_ctls(codec, "Center", 1, true,
1765 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001766 if (err < 0)
1767 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001768 err = create_ch_ctls(codec, "LFE", 2, true,
1769 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001770 if (err < 0)
1771 return err;
1772 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001773 const char *pfx = chname[i];
1774 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1775 cfg->line_outs == 1)
1776 pfx = "Speaker";
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001777 err = create_ch_ctls(codec, pfx, 3, true,
1778 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001779 if (err < 0)
1780 return err;
1781 }
1782 }
1783
Takashi Iwai4a796162011-06-17 17:53:38 +02001784 idx = get_connection_index(codec, spec->aa_mix_nid,
1785 spec->multiout.dac_nids[0]);
1786 if (idx >= 0) {
1787 /* add control to mixer */
1788 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1789 "PCM Playback Volume",
1790 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1791 idx, HDA_INPUT));
1792 if (err < 0)
1793 return err;
1794 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1795 "PCM Playback Switch",
1796 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1797 idx, HDA_INPUT));
1798 if (err < 0)
1799 return err;
1800 }
1801
Takashi Iwaif4a78282011-06-17 18:46:48 +02001802 cfg->line_outs = old_line_outs;
1803
Joseph Chanc577b8a2006-11-29 15:29:40 +01001804 return 0;
1805}
1806
Takashi Iwai4a796162011-06-17 17:53:38 +02001807static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001808{
Takashi Iwai4a796162011-06-17 17:53:38 +02001809 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001810 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001811 int err;
1812
1813 if (!pin)
1814 return 0;
1815
Takashi Iwai8df2a312011-06-21 11:48:29 +02001816 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001817 spec->hp_dac_nid = spec->hp_path.path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001818
Takashi Iwaiece8d042011-06-19 16:24:21 +02001819 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001820 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001821 !spec->hp_dac_nid)
1822 return 0;
1823
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001824 if (spec->hp_dac_nid)
1825 path = &spec->hp_path;
1826 else
1827 path = &spec->hp_dep_path;
1828 err = create_ch_ctls(codec, "Headphone", 3, false, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001829 if (err < 0)
1830 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001831 if (spec->hp_dac_nid) {
1832 spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
1833 spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
1834 }
Harald Welte0aa62ae2008-09-09 15:58:27 +08001835
Joseph Chanc577b8a2006-11-29 15:29:40 +01001836 return 0;
1837}
1838
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001839static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1840{
1841 struct via_spec *spec = codec->spec;
1842 hda_nid_t pin, dac;
1843
1844 pin = spec->autocfg.speaker_pins[0];
1845 if (!spec->autocfg.speaker_outs || !pin)
1846 return 0;
1847
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001848 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1849 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001850 spec->multiout.extra_out_nid[0] = dac;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001851 return create_ch_ctls(codec, "Speaker", 3, true,
1852 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001853 }
1854 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001855 &spec->speaker_path))
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001856 return create_ch_ctls(codec, "Speaker", 3, false,
1857 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001858
1859 return 0;
1860}
1861
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001862/* look for ADCs */
1863static int via_fill_adcs(struct hda_codec *codec)
1864{
1865 struct via_spec *spec = codec->spec;
1866 hda_nid_t nid = codec->start_nid;
1867 int i;
1868
1869 for (i = 0; i < codec->num_nodes; i++, nid++) {
1870 unsigned int wcaps = get_wcaps(codec, nid);
1871 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1872 continue;
1873 if (wcaps & AC_WCAP_DIGITAL)
1874 continue;
1875 if (!(wcaps & AC_WCAP_CONN_LIST))
1876 continue;
1877 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1878 return -ENOMEM;
1879 spec->adc_nids[spec->num_adc_nids++] = nid;
1880 }
1881 return 0;
1882}
1883
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001884/* input-src control */
1885static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
1886 struct snd_ctl_elem_info *uinfo)
1887{
1888 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1889 struct via_spec *spec = codec->spec;
1890
1891 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1892 uinfo->count = 1;
1893 uinfo->value.enumerated.items = spec->num_inputs;
1894 if (uinfo->value.enumerated.item >= spec->num_inputs)
1895 uinfo->value.enumerated.item = spec->num_inputs - 1;
1896 strcpy(uinfo->value.enumerated.name,
1897 spec->inputs[uinfo->value.enumerated.item].label);
1898 return 0;
1899}
1900
1901static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
1902 struct snd_ctl_elem_value *ucontrol)
1903{
1904 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1905 struct via_spec *spec = codec->spec;
1906 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1907
1908 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
1909 return 0;
1910}
1911
1912static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
1913 struct snd_ctl_elem_value *ucontrol)
1914{
1915 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1916 struct via_spec *spec = codec->spec;
1917 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1918 hda_nid_t mux;
1919 int cur;
1920
1921 cur = ucontrol->value.enumerated.item[0];
1922 if (cur < 0 || cur >= spec->num_inputs)
1923 return -EINVAL;
1924 if (spec->cur_mux[idx] == cur)
1925 return 0;
1926 spec->cur_mux[idx] = cur;
1927 if (spec->dyn_adc_switch) {
1928 int adc_idx = spec->inputs[cur].adc_idx;
1929 mux = spec->mux_nids[adc_idx];
1930 via_dyn_adc_pcm_resetup(codec, cur);
1931 } else {
1932 mux = spec->mux_nids[idx];
1933 if (snd_BUG_ON(!mux))
1934 return -EINVAL;
1935 }
1936
1937 if (mux) {
1938 /* switch to D0 beofre change index */
1939 if (snd_hda_codec_read(codec, mux, 0,
1940 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
1941 snd_hda_codec_write(codec, mux, 0,
1942 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
1943 snd_hda_codec_write(codec, mux, 0,
1944 AC_VERB_SET_CONNECT_SEL,
1945 spec->inputs[cur].mux_idx);
1946 }
1947
1948 /* update jack power state */
1949 set_widgets_power_state(codec);
1950 return 0;
1951}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001952
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001953static const struct snd_kcontrol_new via_input_src_ctl = {
1954 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1955 /* The multiple "Capture Source" controls confuse alsamixer
1956 * So call somewhat different..
1957 */
1958 /* .name = "Capture Source", */
1959 .name = "Input Source",
1960 .info = via_mux_enum_info,
1961 .get = via_mux_enum_get,
1962 .put = via_mux_enum_put,
1963};
1964
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001965static int create_input_src_ctls(struct hda_codec *codec, int count)
1966{
1967 struct via_spec *spec = codec->spec;
1968 struct snd_kcontrol_new *knew;
1969
1970 if (spec->num_inputs <= 1 || !count)
1971 return 0; /* no need for single src */
1972
1973 knew = via_clone_control(spec, &via_input_src_ctl);
1974 if (!knew)
1975 return -ENOMEM;
1976 knew->count = count;
1977 return 0;
1978}
1979
1980/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02001981static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
1982{
1983 struct hda_amp_list *list;
1984
1985 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
1986 return;
1987 list = spec->loopback_list + spec->num_loopbacks;
1988 list->nid = mix;
1989 list->dir = HDA_INPUT;
1990 list->idx = idx;
1991 spec->num_loopbacks++;
1992 spec->loopback.amplist = spec->loopback_list;
1993}
Takashi Iwai13af8e72011-06-20 14:05:46 +02001994
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001995static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02001996 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001997{
Takashi Iwai8d087c72011-06-28 12:45:47 +02001998 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001999}
2000
2001/* add the input-route to the given pin */
2002static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002003{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002004 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002005 int c, idx;
2006
2007 spec->inputs[spec->num_inputs].adc_idx = -1;
2008 spec->inputs[spec->num_inputs].pin = pin;
2009 for (c = 0; c < spec->num_adc_nids; c++) {
2010 if (spec->mux_nids[c]) {
2011 idx = get_connection_index(codec, spec->mux_nids[c],
2012 pin);
2013 if (idx < 0)
2014 continue;
2015 spec->inputs[spec->num_inputs].mux_idx = idx;
2016 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002017 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002018 continue;
2019 }
2020 spec->inputs[spec->num_inputs].adc_idx = c;
2021 /* Can primary ADC satisfy all inputs? */
2022 if (!spec->dyn_adc_switch &&
2023 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2024 snd_printd(KERN_INFO
2025 "via: dynamic ADC switching enabled\n");
2026 spec->dyn_adc_switch = 1;
2027 }
2028 return true;
2029 }
2030 return false;
2031}
2032
2033static int get_mux_nids(struct hda_codec *codec);
2034
2035/* parse input-routes; fill ADCs, MUXs and input-src entries */
2036static int parse_analog_inputs(struct hda_codec *codec)
2037{
2038 struct via_spec *spec = codec->spec;
2039 const struct auto_pin_cfg *cfg = &spec->autocfg;
2040 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002041
2042 err = via_fill_adcs(codec);
2043 if (err < 0)
2044 return err;
2045 err = get_mux_nids(codec);
2046 if (err < 0)
2047 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002048
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002049 /* fill all input-routes */
2050 for (i = 0; i < cfg->num_inputs; i++) {
2051 if (add_input_route(codec, cfg->inputs[i].pin))
2052 spec->inputs[spec->num_inputs++].label =
2053 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002054 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002055
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002056 /* check for internal loopback recording */
2057 if (spec->aa_mix_nid &&
2058 add_input_route(codec, spec->aa_mix_nid))
2059 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2060
2061 return 0;
2062}
2063
2064/* create analog-loopback volume/switch controls */
2065static int create_loopback_ctls(struct hda_codec *codec)
2066{
2067 struct via_spec *spec = codec->spec;
2068 const struct auto_pin_cfg *cfg = &spec->autocfg;
2069 const char *prev_label = NULL;
2070 int type_idx = 0;
2071 int i, j, err, idx;
2072
2073 if (!spec->aa_mix_nid)
2074 return 0;
2075
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002076 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002077 hda_nid_t pin = cfg->inputs[i].pin;
2078 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2079
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002080 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002081 type_idx++;
2082 else
2083 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002084 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002085 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2086 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002087 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002088 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002089 if (err < 0)
2090 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002091 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002092 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002093
2094 /* remember the label for smart51 control */
2095 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002096 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002097 spec->smart51_idxs[j] = idx;
2098 spec->smart51_labels[j] = label;
2099 break;
2100 }
2101 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002102 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002103 return 0;
2104}
2105
2106/* create mic-boost controls (if present) */
2107static int create_mic_boost_ctls(struct hda_codec *codec)
2108{
2109 struct via_spec *spec = codec->spec;
2110 const struct auto_pin_cfg *cfg = &spec->autocfg;
2111 int i, err;
2112
2113 for (i = 0; i < cfg->num_inputs; i++) {
2114 hda_nid_t pin = cfg->inputs[i].pin;
2115 unsigned int caps;
2116 const char *label;
2117 char name[32];
2118
2119 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2120 continue;
2121 caps = query_amp_caps(codec, pin, HDA_INPUT);
2122 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2123 continue;
2124 label = hda_get_autocfg_input_label(codec, cfg, i);
2125 snprintf(name, sizeof(name), "%s Boost Volume", label);
2126 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2127 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2128 if (err < 0)
2129 return err;
2130 }
2131 return 0;
2132}
2133
2134/* create capture and input-src controls for multiple streams */
2135static int create_multi_adc_ctls(struct hda_codec *codec)
2136{
2137 struct via_spec *spec = codec->spec;
2138 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002139
2140 /* create capture mixer elements */
2141 for (i = 0; i < spec->num_adc_nids; i++) {
2142 hda_nid_t adc = spec->adc_nids[i];
2143 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2144 "Capture Volume", i,
2145 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2146 HDA_INPUT));
2147 if (err < 0)
2148 return err;
2149 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2150 "Capture Switch", i,
2151 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2152 HDA_INPUT));
2153 if (err < 0)
2154 return err;
2155 }
2156
2157 /* input-source control */
2158 for (i = 0; i < spec->num_adc_nids; i++)
2159 if (!spec->mux_nids[i])
2160 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002161 err = create_input_src_ctls(codec, i);
2162 if (err < 0)
2163 return err;
2164 return 0;
2165}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002166
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002167/* bind capture volume/switch */
2168static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2169 HDA_BIND_VOL("Capture Volume", 0);
2170static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2171 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002172
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002173static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2174 struct hda_ctl_ops *ops)
2175{
2176 struct hda_bind_ctls *ctl;
2177 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002178
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002179 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2180 if (!ctl)
2181 return -ENOMEM;
2182 ctl->ops = ops;
2183 for (i = 0; i < spec->num_adc_nids; i++)
2184 ctl->values[i] =
2185 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2186 *ctl_ret = ctl;
2187 return 0;
2188}
2189
2190/* create capture and input-src controls for dynamic ADC-switch case */
2191static int create_dyn_adc_ctls(struct hda_codec *codec)
2192{
2193 struct via_spec *spec = codec->spec;
2194 struct snd_kcontrol_new *knew;
2195 int err;
2196
2197 /* set up the bind capture ctls */
2198 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2199 if (err < 0)
2200 return err;
2201 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2202 if (err < 0)
2203 return err;
2204
2205 /* create capture mixer elements */
2206 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2207 if (!knew)
2208 return -ENOMEM;
2209 knew->private_value = (long)spec->bind_cap_vol;
2210
2211 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2212 if (!knew)
2213 return -ENOMEM;
2214 knew->private_value = (long)spec->bind_cap_sw;
2215
2216 /* input-source control */
2217 err = create_input_src_ctls(codec, 1);
2218 if (err < 0)
2219 return err;
2220 return 0;
2221}
2222
2223/* parse and create capture-related stuff */
2224static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2225{
2226 struct via_spec *spec = codec->spec;
2227 int err;
2228
2229 err = parse_analog_inputs(codec);
2230 if (err < 0)
2231 return err;
2232 if (spec->dyn_adc_switch)
2233 err = create_dyn_adc_ctls(codec);
2234 else
2235 err = create_multi_adc_ctls(codec);
2236 if (err < 0)
2237 return err;
2238 err = create_loopback_ctls(codec);
2239 if (err < 0)
2240 return err;
2241 err = create_mic_boost_ctls(codec);
2242 if (err < 0)
2243 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002244 return 0;
2245}
2246
Harald Welte76d9b0d2008-09-09 15:50:37 +08002247static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2248{
2249 unsigned int def_conf;
2250 unsigned char seqassoc;
2251
Takashi Iwai2f334f92009-02-20 14:37:42 +01002252 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002253 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2254 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002255 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2256 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2257 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2258 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002259 }
2260
2261 return;
2262}
2263
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002264static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002265 struct snd_ctl_elem_value *ucontrol)
2266{
2267 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2268 struct via_spec *spec = codec->spec;
2269
2270 if (spec->codec_type != VT1708)
2271 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002272 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002273 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002274 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002275 return 0;
2276}
2277
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002278static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002279 struct snd_ctl_elem_value *ucontrol)
2280{
2281 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2282 struct via_spec *spec = codec->spec;
2283 int change;
2284
2285 if (spec->codec_type != VT1708)
2286 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002287 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002288 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002289 == !spec->vt1708_jack_detect;
2290 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002291 mute_aa_path(codec, 1);
2292 notify_aa_path_ctls(codec);
2293 }
2294 return change;
2295}
2296
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002297static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2298 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2299 .name = "Jack Detect",
2300 .count = 1,
2301 .info = snd_ctl_boolean_mono_info,
2302 .get = vt1708_jack_detect_get,
2303 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002304};
2305
Takashi Iwai12daef62011-06-18 17:45:49 +02002306static void fill_dig_outs(struct hda_codec *codec);
2307static void fill_dig_in(struct hda_codec *codec);
2308
2309static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002310{
2311 struct via_spec *spec = codec->spec;
2312 int err;
2313
2314 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2315 if (err < 0)
2316 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002317 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002318 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002319
Takashi Iwai4a796162011-06-17 17:53:38 +02002320 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002321 if (err < 0)
2322 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002323 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002324 if (err < 0)
2325 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002326 err = via_auto_create_speaker_ctls(codec);
2327 if (err < 0)
2328 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002329 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002330 if (err < 0)
2331 return err;
2332
2333 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2334
Takashi Iwai12daef62011-06-18 17:45:49 +02002335 fill_dig_outs(codec);
2336 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002337
Takashi Iwai603c4012008-07-30 15:01:44 +02002338 if (spec->kctls.list)
2339 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002340
Takashi Iwai096a8852011-06-20 12:09:02 +02002341 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002342
Takashi Iwai8df2a312011-06-21 11:48:29 +02002343 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002344 err = via_hp_build(codec);
2345 if (err < 0)
2346 return err;
2347 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002348
Takashi Iwaif4a78282011-06-17 18:46:48 +02002349 err = via_smart51_build(codec);
2350 if (err < 0)
2351 return err;
2352
Takashi Iwai5d417622011-06-20 11:32:27 +02002353 /* assign slave outs */
2354 if (spec->slave_dig_outs[0])
2355 codec->slave_dig_outs = spec->slave_dig_outs;
2356
Joseph Chanc577b8a2006-11-29 15:29:40 +01002357 return 1;
2358}
2359
Takashi Iwai5d417622011-06-20 11:32:27 +02002360static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002361{
Lydia Wang25eaba22009-10-10 19:08:43 +08002362 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002363 if (spec->multiout.dig_out_nid)
2364 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2365 if (spec->slave_dig_outs[0])
2366 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2367}
Lydia Wang25eaba22009-10-10 19:08:43 +08002368
Takashi Iwai5d417622011-06-20 11:32:27 +02002369static void via_auto_init_dig_in(struct hda_codec *codec)
2370{
2371 struct via_spec *spec = codec->spec;
2372 if (!spec->dig_in_nid)
2373 return;
2374 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2375 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2376}
2377
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002378/* initialize the unsolicited events */
2379static void via_auto_init_unsol_event(struct hda_codec *codec)
2380{
2381 struct via_spec *spec = codec->spec;
2382 struct auto_pin_cfg *cfg = &spec->autocfg;
2383 unsigned int ev;
2384 int i;
2385
2386 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2387 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2388 AC_VERB_SET_UNSOLICITED_ENABLE,
2389 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2390
2391 if (cfg->speaker_pins[0])
2392 ev = VIA_LINE_EVENT;
2393 else
2394 ev = 0;
2395 for (i = 0; i < cfg->line_outs; i++) {
2396 if (cfg->line_out_pins[i] &&
2397 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002398 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002399 AC_VERB_SET_UNSOLICITED_ENABLE,
2400 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2401 }
2402
2403 for (i = 0; i < cfg->num_inputs; i++) {
2404 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2405 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2406 AC_VERB_SET_UNSOLICITED_ENABLE,
2407 AC_USRSP_EN | VIA_JACK_EVENT);
2408 }
2409}
2410
Takashi Iwai5d417622011-06-20 11:32:27 +02002411static int via_init(struct hda_codec *codec)
2412{
2413 struct via_spec *spec = codec->spec;
2414 int i;
2415
2416 for (i = 0; i < spec->num_iverbs; i++)
2417 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2418
Joseph Chanc577b8a2006-11-29 15:29:40 +01002419 via_auto_init_multi_out(codec);
2420 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002421 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002422 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002423 via_auto_init_dig_outs(codec);
2424 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002425
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002426 via_auto_init_unsol_event(codec);
2427
2428 via_hp_automute(codec);
2429 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002430
Joseph Chanc577b8a2006-11-29 15:29:40 +01002431 return 0;
2432}
2433
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002434static void vt1708_update_hp_jack_state(struct work_struct *work)
2435{
2436 struct via_spec *spec = container_of(work, struct via_spec,
2437 vt1708_hp_work.work);
2438 if (spec->codec_type != VT1708)
2439 return;
2440 /* if jack state toggled */
2441 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002442 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002443 spec->vt1708_hp_present ^= 1;
2444 via_hp_automute(spec->codec);
2445 }
2446 vt1708_start_hp_work(spec);
2447}
2448
Takashi Iwai337b9d02009-07-07 18:18:59 +02002449static int get_mux_nids(struct hda_codec *codec)
2450{
2451 struct via_spec *spec = codec->spec;
2452 hda_nid_t nid, conn[8];
2453 unsigned int type;
2454 int i, n;
2455
2456 for (i = 0; i < spec->num_adc_nids; i++) {
2457 nid = spec->adc_nids[i];
2458 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002459 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002460 if (type == AC_WID_PIN)
2461 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002462 n = snd_hda_get_connections(codec, nid, conn,
2463 ARRAY_SIZE(conn));
2464 if (n <= 0)
2465 break;
2466 if (n > 1) {
2467 spec->mux_nids[i] = nid;
2468 break;
2469 }
2470 nid = conn[0];
2471 }
2472 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002473 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002474}
2475
Joseph Chanc577b8a2006-11-29 15:29:40 +01002476static int patch_vt1708(struct hda_codec *codec)
2477{
2478 struct via_spec *spec;
2479 int err;
2480
2481 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002482 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002483 if (spec == NULL)
2484 return -ENOMEM;
2485
Takashi Iwai620e2b22011-06-17 17:19:19 +02002486 spec->aa_mix_nid = 0x17;
2487
Takashi Iwai12daef62011-06-18 17:45:49 +02002488 /* Add HP and CD pin config connect bit re-config action */
2489 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2490 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2491
Joseph Chanc577b8a2006-11-29 15:29:40 +01002492 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002493 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002494 if (err < 0) {
2495 via_free(codec);
2496 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002497 }
2498
Takashi Iwai12daef62011-06-18 17:45:49 +02002499 /* add jack detect on/off control */
2500 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2501 return -ENOMEM;
2502
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002503 /* disable 32bit format on VT1708 */
2504 if (codec->vendor_id == 0x11061708)
2505 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002506
Joseph Chanc577b8a2006-11-29 15:29:40 +01002507 codec->patch_ops = via_patch_ops;
2508
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002509 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002510 return 0;
2511}
2512
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002513static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002514{
2515 struct via_spec *spec;
2516 int err;
2517
2518 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002519 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002520 if (spec == NULL)
2521 return -ENOMEM;
2522
Takashi Iwai620e2b22011-06-17 17:19:19 +02002523 spec->aa_mix_nid = 0x18;
2524
Takashi Iwai12daef62011-06-18 17:45:49 +02002525 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002526 if (err < 0) {
2527 via_free(codec);
2528 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002529 }
2530
Joseph Chanc577b8a2006-11-29 15:29:40 +01002531 codec->patch_ops = via_patch_ops;
2532
Josepch Chanf7278fd2007-12-13 16:40:40 +01002533 return 0;
2534}
2535
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002536static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2537{
2538 struct via_spec *spec = codec->spec;
2539 int imux_is_smixer;
2540 unsigned int parm;
2541 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002542 if ((spec->codec_type != VT1708B_4CH) &&
2543 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002544 is_8ch = 1;
2545
2546 /* SW0 (17h) = stereo mixer */
2547 imux_is_smixer =
2548 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2549 == ((spec->codec_type == VT1708S) ? 5 : 0));
2550 /* inputs */
2551 /* PW 1/2/5 (1ah/1bh/1eh) */
2552 parm = AC_PWRST_D3;
2553 set_pin_power_state(codec, 0x1a, &parm);
2554 set_pin_power_state(codec, 0x1b, &parm);
2555 set_pin_power_state(codec, 0x1e, &parm);
2556 if (imux_is_smixer)
2557 parm = AC_PWRST_D0;
2558 /* SW0 (17h), AIW 0/1 (13h/14h) */
2559 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2560 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2561 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2562
2563 /* outputs */
2564 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2565 parm = AC_PWRST_D3;
2566 set_pin_power_state(codec, 0x19, &parm);
2567 if (spec->smart51_enabled)
2568 set_pin_power_state(codec, 0x1b, &parm);
2569 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2570 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2571
2572 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2573 if (is_8ch) {
2574 parm = AC_PWRST_D3;
2575 set_pin_power_state(codec, 0x22, &parm);
2576 if (spec->smart51_enabled)
2577 set_pin_power_state(codec, 0x1a, &parm);
2578 snd_hda_codec_write(codec, 0x26, 0,
2579 AC_VERB_SET_POWER_STATE, parm);
2580 snd_hda_codec_write(codec, 0x24, 0,
2581 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002582 } else if (codec->vendor_id == 0x11064397) {
2583 /* PW7(23h), SW2(27h), AOW2(25h) */
2584 parm = AC_PWRST_D3;
2585 set_pin_power_state(codec, 0x23, &parm);
2586 if (spec->smart51_enabled)
2587 set_pin_power_state(codec, 0x1a, &parm);
2588 snd_hda_codec_write(codec, 0x27, 0,
2589 AC_VERB_SET_POWER_STATE, parm);
2590 snd_hda_codec_write(codec, 0x25, 0,
2591 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002592 }
2593
2594 /* PW 3/4/7 (1ch/1dh/23h) */
2595 parm = AC_PWRST_D3;
2596 /* force to D0 for internal Speaker */
2597 set_pin_power_state(codec, 0x1c, &parm);
2598 set_pin_power_state(codec, 0x1d, &parm);
2599 if (is_8ch)
2600 set_pin_power_state(codec, 0x23, &parm);
2601
2602 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2603 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2604 imux_is_smixer ? AC_PWRST_D0 : parm);
2605 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2606 if (is_8ch) {
2607 snd_hda_codec_write(codec, 0x25, 0,
2608 AC_VERB_SET_POWER_STATE, parm);
2609 snd_hda_codec_write(codec, 0x27, 0,
2610 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002611 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2612 snd_hda_codec_write(codec, 0x25, 0,
2613 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002614}
2615
Lydia Wang518bf3b2009-10-10 19:07:29 +08002616static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002617static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002618{
2619 struct via_spec *spec;
2620 int err;
2621
Lydia Wang518bf3b2009-10-10 19:07:29 +08002622 if (get_codec_type(codec) == VT1708BCE)
2623 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002624
Josepch Chanf7278fd2007-12-13 16:40:40 +01002625 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002626 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002627 if (spec == NULL)
2628 return -ENOMEM;
2629
Takashi Iwai620e2b22011-06-17 17:19:19 +02002630 spec->aa_mix_nid = 0x16;
2631
Josepch Chanf7278fd2007-12-13 16:40:40 +01002632 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002633 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002634 if (err < 0) {
2635 via_free(codec);
2636 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002637 }
2638
Josepch Chanf7278fd2007-12-13 16:40:40 +01002639 codec->patch_ops = via_patch_ops;
2640
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002641 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2642
Josepch Chanf7278fd2007-12-13 16:40:40 +01002643 return 0;
2644}
2645
Harald Welted949cac2008-09-09 15:56:01 +08002646/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002647static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002648 /* Enable Mic Boost Volume backdoor */
2649 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002650 /* don't bybass mixer */
2651 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002652 { }
2653};
2654
Takashi Iwai9da29272009-05-07 16:31:14 +02002655/* fill out digital output widgets; one for master and one for slave outputs */
2656static void fill_dig_outs(struct hda_codec *codec)
2657{
2658 struct via_spec *spec = codec->spec;
2659 int i;
2660
2661 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2662 hda_nid_t nid;
2663 int conn;
2664
2665 nid = spec->autocfg.dig_out_pins[i];
2666 if (!nid)
2667 continue;
2668 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2669 if (conn < 1)
2670 continue;
2671 if (!spec->multiout.dig_out_nid)
2672 spec->multiout.dig_out_nid = nid;
2673 else {
2674 spec->slave_dig_outs[0] = nid;
2675 break; /* at most two dig outs */
2676 }
2677 }
2678}
2679
Takashi Iwai12daef62011-06-18 17:45:49 +02002680static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002681{
2682 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002683 hda_nid_t dig_nid;
2684 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002685
Takashi Iwai12daef62011-06-18 17:45:49 +02002686 if (!spec->autocfg.dig_in_pin)
2687 return;
Harald Welted949cac2008-09-09 15:56:01 +08002688
Takashi Iwai12daef62011-06-18 17:45:49 +02002689 dig_nid = codec->start_nid;
2690 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2691 unsigned int wcaps = get_wcaps(codec, dig_nid);
2692 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2693 continue;
2694 if (!(wcaps & AC_WCAP_DIGITAL))
2695 continue;
2696 if (!(wcaps & AC_WCAP_CONN_LIST))
2697 continue;
2698 err = get_connection_index(codec, dig_nid,
2699 spec->autocfg.dig_in_pin);
2700 if (err >= 0) {
2701 spec->dig_in_nid = dig_nid;
2702 break;
2703 }
2704 }
Harald Welted949cac2008-09-09 15:56:01 +08002705}
2706
Lydia Wang6369bcf2009-10-10 19:08:31 +08002707static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2708 int offset, int num_steps, int step_size)
2709{
2710 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2711 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2712 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2713 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2714 (0 << AC_AMPCAP_MUTE_SHIFT));
2715}
2716
Harald Welted949cac2008-09-09 15:56:01 +08002717static int patch_vt1708S(struct hda_codec *codec)
2718{
2719 struct via_spec *spec;
2720 int err;
2721
2722 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002723 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002724 if (spec == NULL)
2725 return -ENOMEM;
2726
Takashi Iwai620e2b22011-06-17 17:19:19 +02002727 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002728 override_mic_boost(codec, 0x1a, 0, 3, 40);
2729 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002730
Harald Welted949cac2008-09-09 15:56:01 +08002731 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002732 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002733 if (err < 0) {
2734 via_free(codec);
2735 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002736 }
2737
Takashi Iwai096a8852011-06-20 12:09:02 +02002738 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002739
Harald Welted949cac2008-09-09 15:56:01 +08002740 codec->patch_ops = via_patch_ops;
2741
Lydia Wang518bf3b2009-10-10 19:07:29 +08002742 /* correct names for VT1708BCE */
2743 if (get_codec_type(codec) == VT1708BCE) {
2744 kfree(codec->chip_name);
2745 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2746 snprintf(codec->bus->card->mixername,
2747 sizeof(codec->bus->card->mixername),
2748 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002749 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002750 /* correct names for VT1705 */
2751 if (codec->vendor_id == 0x11064397) {
2752 kfree(codec->chip_name);
2753 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2754 snprintf(codec->bus->card->mixername,
2755 sizeof(codec->bus->card->mixername),
2756 "%s %s", codec->vendor_name, codec->chip_name);
2757 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002758 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002759 return 0;
2760}
2761
2762/* Patch for VT1702 */
2763
Takashi Iwai096a8852011-06-20 12:09:02 +02002764static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002765 /* mixer enable */
2766 {0x1, 0xF88, 0x3},
2767 /* GPIO 0~2 */
2768 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002769 { }
2770};
2771
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002772static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2773{
2774 int imux_is_smixer =
2775 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2776 unsigned int parm;
2777 /* inputs */
2778 /* PW 1/2/5 (14h/15h/18h) */
2779 parm = AC_PWRST_D3;
2780 set_pin_power_state(codec, 0x14, &parm);
2781 set_pin_power_state(codec, 0x15, &parm);
2782 set_pin_power_state(codec, 0x18, &parm);
2783 if (imux_is_smixer)
2784 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2785 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2786 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2787 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2788 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2789 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2790
2791 /* outputs */
2792 /* PW 3/4 (16h/17h) */
2793 parm = AC_PWRST_D3;
2794 set_pin_power_state(codec, 0x17, &parm);
2795 set_pin_power_state(codec, 0x16, &parm);
2796 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2797 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2798 imux_is_smixer ? AC_PWRST_D0 : parm);
2799 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2800 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2801}
2802
Harald Welted949cac2008-09-09 15:56:01 +08002803static int patch_vt1702(struct hda_codec *codec)
2804{
2805 struct via_spec *spec;
2806 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002807
2808 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002809 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002810 if (spec == NULL)
2811 return -ENOMEM;
2812
Takashi Iwai620e2b22011-06-17 17:19:19 +02002813 spec->aa_mix_nid = 0x1a;
2814
Takashi Iwai12daef62011-06-18 17:45:49 +02002815 /* limit AA path volume to 0 dB */
2816 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2817 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2818 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2819 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2820 (1 << AC_AMPCAP_MUTE_SHIFT));
2821
Harald Welted949cac2008-09-09 15:56:01 +08002822 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002823 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002824 if (err < 0) {
2825 via_free(codec);
2826 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002827 }
2828
Takashi Iwai096a8852011-06-20 12:09:02 +02002829 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002830
Harald Welted949cac2008-09-09 15:56:01 +08002831 codec->patch_ops = via_patch_ops;
2832
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002833 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002834 return 0;
2835}
2836
Lydia Wangeb7188c2009-10-10 19:08:34 +08002837/* Patch for VT1718S */
2838
Takashi Iwai096a8852011-06-20 12:09:02 +02002839static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002840 /* Enable MW0 adjust Gain 5 */
2841 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002842 /* Enable Boost Volume backdoor */
2843 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002844
Lydia Wangeb7188c2009-10-10 19:08:34 +08002845 { }
2846};
2847
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002848static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2849{
2850 struct via_spec *spec = codec->spec;
2851 int imux_is_smixer;
2852 unsigned int parm;
2853 /* MUX6 (1eh) = stereo mixer */
2854 imux_is_smixer =
2855 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2856 /* inputs */
2857 /* PW 5/6/7 (29h/2ah/2bh) */
2858 parm = AC_PWRST_D3;
2859 set_pin_power_state(codec, 0x29, &parm);
2860 set_pin_power_state(codec, 0x2a, &parm);
2861 set_pin_power_state(codec, 0x2b, &parm);
2862 if (imux_is_smixer)
2863 parm = AC_PWRST_D0;
2864 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2865 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2866 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2867 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2868 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2869
2870 /* outputs */
2871 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2872 parm = AC_PWRST_D3;
2873 set_pin_power_state(codec, 0x27, &parm);
2874 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2875 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2876
2877 /* PW2 (26h), AOW2 (ah) */
2878 parm = AC_PWRST_D3;
2879 set_pin_power_state(codec, 0x26, &parm);
2880 if (spec->smart51_enabled)
2881 set_pin_power_state(codec, 0x2b, &parm);
2882 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2883
2884 /* PW0 (24h), AOW0 (8h) */
2885 parm = AC_PWRST_D3;
2886 set_pin_power_state(codec, 0x24, &parm);
2887 if (!spec->hp_independent_mode) /* check for redirected HP */
2888 set_pin_power_state(codec, 0x28, &parm);
2889 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2890 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2891 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2892 imux_is_smixer ? AC_PWRST_D0 : parm);
2893
2894 /* PW1 (25h), AOW1 (9h) */
2895 parm = AC_PWRST_D3;
2896 set_pin_power_state(codec, 0x25, &parm);
2897 if (spec->smart51_enabled)
2898 set_pin_power_state(codec, 0x2a, &parm);
2899 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2900
2901 if (spec->hp_independent_mode) {
2902 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2903 parm = AC_PWRST_D3;
2904 set_pin_power_state(codec, 0x28, &parm);
2905 snd_hda_codec_write(codec, 0x1b, 0,
2906 AC_VERB_SET_POWER_STATE, parm);
2907 snd_hda_codec_write(codec, 0x34, 0,
2908 AC_VERB_SET_POWER_STATE, parm);
2909 snd_hda_codec_write(codec, 0xc, 0,
2910 AC_VERB_SET_POWER_STATE, parm);
2911 }
2912}
2913
Lydia Wangeb7188c2009-10-10 19:08:34 +08002914static int patch_vt1718S(struct hda_codec *codec)
2915{
2916 struct via_spec *spec;
2917 int err;
2918
2919 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002920 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002921 if (spec == NULL)
2922 return -ENOMEM;
2923
Takashi Iwai620e2b22011-06-17 17:19:19 +02002924 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002925 override_mic_boost(codec, 0x2b, 0, 3, 40);
2926 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002927
Lydia Wangeb7188c2009-10-10 19:08:34 +08002928 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002929 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002930 if (err < 0) {
2931 via_free(codec);
2932 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002933 }
2934
Takashi Iwai096a8852011-06-20 12:09:02 +02002935 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002936
Lydia Wangeb7188c2009-10-10 19:08:34 +08002937 codec->patch_ops = via_patch_ops;
2938
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002939 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2940
Lydia Wangeb7188c2009-10-10 19:08:34 +08002941 return 0;
2942}
Lydia Wangf3db4232009-10-10 19:08:41 +08002943
2944/* Patch for VT1716S */
2945
2946static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2947 struct snd_ctl_elem_info *uinfo)
2948{
2949 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2950 uinfo->count = 1;
2951 uinfo->value.integer.min = 0;
2952 uinfo->value.integer.max = 1;
2953 return 0;
2954}
2955
2956static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2957 struct snd_ctl_elem_value *ucontrol)
2958{
2959 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2960 int index = 0;
2961
2962 index = snd_hda_codec_read(codec, 0x26, 0,
2963 AC_VERB_GET_CONNECT_SEL, 0);
2964 if (index != -1)
2965 *ucontrol->value.integer.value = index;
2966
2967 return 0;
2968}
2969
2970static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2971 struct snd_ctl_elem_value *ucontrol)
2972{
2973 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2974 struct via_spec *spec = codec->spec;
2975 int index = *ucontrol->value.integer.value;
2976
2977 snd_hda_codec_write(codec, 0x26, 0,
2978 AC_VERB_SET_CONNECT_SEL, index);
2979 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002980 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002981 return 1;
2982}
2983
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002984static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002985 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2986 {
2987 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2988 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002989 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08002990 .count = 1,
2991 .info = vt1716s_dmic_info,
2992 .get = vt1716s_dmic_get,
2993 .put = vt1716s_dmic_put,
2994 },
2995 {} /* end */
2996};
2997
2998
2999/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003000static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003001 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3002 { } /* end */
3003};
3004
Takashi Iwai096a8852011-06-20 12:09:02 +02003005static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003006 /* Enable Boost Volume backdoor */
3007 {0x1, 0xf8a, 0x80},
3008 /* don't bybass mixer */
3009 {0x1, 0xf88, 0xc0},
3010 /* Enable mono output */
3011 {0x1, 0xf90, 0x08},
3012 { }
3013};
3014
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003015static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3016{
3017 struct via_spec *spec = codec->spec;
3018 int imux_is_smixer;
3019 unsigned int parm;
3020 unsigned int mono_out, present;
3021 /* SW0 (17h) = stereo mixer */
3022 imux_is_smixer =
3023 (snd_hda_codec_read(codec, 0x17, 0,
3024 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3025 /* inputs */
3026 /* PW 1/2/5 (1ah/1bh/1eh) */
3027 parm = AC_PWRST_D3;
3028 set_pin_power_state(codec, 0x1a, &parm);
3029 set_pin_power_state(codec, 0x1b, &parm);
3030 set_pin_power_state(codec, 0x1e, &parm);
3031 if (imux_is_smixer)
3032 parm = AC_PWRST_D0;
3033 /* SW0 (17h), AIW0(13h) */
3034 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3035 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3036
3037 parm = AC_PWRST_D3;
3038 set_pin_power_state(codec, 0x1e, &parm);
3039 /* PW11 (22h) */
3040 if (spec->dmic_enabled)
3041 set_pin_power_state(codec, 0x22, &parm);
3042 else
3043 snd_hda_codec_write(codec, 0x22, 0,
3044 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3045
3046 /* SW2(26h), AIW1(14h) */
3047 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3048 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3049
3050 /* outputs */
3051 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3052 parm = AC_PWRST_D3;
3053 set_pin_power_state(codec, 0x19, &parm);
3054 /* Smart 5.1 PW2(1bh) */
3055 if (spec->smart51_enabled)
3056 set_pin_power_state(codec, 0x1b, &parm);
3057 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3058 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3059
3060 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3061 parm = AC_PWRST_D3;
3062 set_pin_power_state(codec, 0x23, &parm);
3063 /* Smart 5.1 PW1(1ah) */
3064 if (spec->smart51_enabled)
3065 set_pin_power_state(codec, 0x1a, &parm);
3066 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3067
3068 /* Smart 5.1 PW5(1eh) */
3069 if (spec->smart51_enabled)
3070 set_pin_power_state(codec, 0x1e, &parm);
3071 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3072
3073 /* Mono out */
3074 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3075 present = snd_hda_jack_detect(codec, 0x1c);
3076
3077 if (present)
3078 mono_out = 0;
3079 else {
3080 present = snd_hda_jack_detect(codec, 0x1d);
3081 if (!spec->hp_independent_mode && present)
3082 mono_out = 0;
3083 else
3084 mono_out = 1;
3085 }
3086 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3087 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3088 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3089 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3090
3091 /* PW 3/4 (1ch/1dh) */
3092 parm = AC_PWRST_D3;
3093 set_pin_power_state(codec, 0x1c, &parm);
3094 set_pin_power_state(codec, 0x1d, &parm);
3095 /* HP Independent Mode, power on AOW3 */
3096 if (spec->hp_independent_mode)
3097 snd_hda_codec_write(codec, 0x25, 0,
3098 AC_VERB_SET_POWER_STATE, parm);
3099
3100 /* force to D0 for internal Speaker */
3101 /* MW0 (16h), AOW0 (10h) */
3102 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3103 imux_is_smixer ? AC_PWRST_D0 : parm);
3104 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3105 mono_out ? AC_PWRST_D0 : parm);
3106}
3107
Lydia Wangf3db4232009-10-10 19:08:41 +08003108static int patch_vt1716S(struct hda_codec *codec)
3109{
3110 struct via_spec *spec;
3111 int err;
3112
3113 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003114 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003115 if (spec == NULL)
3116 return -ENOMEM;
3117
Takashi Iwai620e2b22011-06-17 17:19:19 +02003118 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003119 override_mic_boost(codec, 0x1a, 0, 3, 40);
3120 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003121
Lydia Wangf3db4232009-10-10 19:08:41 +08003122 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003123 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003124 if (err < 0) {
3125 via_free(codec);
3126 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003127 }
3128
Takashi Iwai096a8852011-06-20 12:09:02 +02003129 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003130
Lydia Wangf3db4232009-10-10 19:08:41 +08003131 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3132 spec->num_mixers++;
3133
3134 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3135
3136 codec->patch_ops = via_patch_ops;
3137
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003138 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003139 return 0;
3140}
Lydia Wang25eaba22009-10-10 19:08:43 +08003141
3142/* for vt2002P */
3143
Takashi Iwai096a8852011-06-20 12:09:02 +02003144static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003145 /* Class-D speaker related verbs */
3146 {0x1, 0xfe0, 0x4},
3147 {0x1, 0xfe9, 0x80},
3148 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003149 /* Enable Boost Volume backdoor */
3150 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003151 /* Enable AOW0 to MW9 */
3152 {0x1, 0xfb8, 0x88},
3153 { }
3154};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003155
Takashi Iwai096a8852011-06-20 12:09:02 +02003156static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003157 /* Enable Boost Volume backdoor */
3158 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003159 /* Enable AOW0 to MW9 */
3160 {0x1, 0xfb8, 0x88},
3161 { }
3162};
Lydia Wang25eaba22009-10-10 19:08:43 +08003163
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003164static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3165{
3166 struct via_spec *spec = codec->spec;
3167 int imux_is_smixer;
3168 unsigned int parm;
3169 unsigned int present;
3170 /* MUX9 (1eh) = stereo mixer */
3171 imux_is_smixer =
3172 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3173 /* inputs */
3174 /* PW 5/6/7 (29h/2ah/2bh) */
3175 parm = AC_PWRST_D3;
3176 set_pin_power_state(codec, 0x29, &parm);
3177 set_pin_power_state(codec, 0x2a, &parm);
3178 set_pin_power_state(codec, 0x2b, &parm);
3179 parm = AC_PWRST_D0;
3180 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3181 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3182 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3183 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3184 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3185
3186 /* outputs */
3187 /* AOW0 (8h)*/
3188 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3189
Lydia Wang118909562011-03-23 17:57:34 +08003190 if (spec->codec_type == VT1802) {
3191 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3192 parm = AC_PWRST_D3;
3193 set_pin_power_state(codec, 0x28, &parm);
3194 snd_hda_codec_write(codec, 0x18, 0,
3195 AC_VERB_SET_POWER_STATE, parm);
3196 snd_hda_codec_write(codec, 0x38, 0,
3197 AC_VERB_SET_POWER_STATE, parm);
3198 } else {
3199 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3200 parm = AC_PWRST_D3;
3201 set_pin_power_state(codec, 0x26, &parm);
3202 snd_hda_codec_write(codec, 0x1c, 0,
3203 AC_VERB_SET_POWER_STATE, parm);
3204 snd_hda_codec_write(codec, 0x37, 0,
3205 AC_VERB_SET_POWER_STATE, parm);
3206 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003207
Lydia Wang118909562011-03-23 17:57:34 +08003208 if (spec->codec_type == VT1802) {
3209 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3210 parm = AC_PWRST_D3;
3211 set_pin_power_state(codec, 0x25, &parm);
3212 snd_hda_codec_write(codec, 0x15, 0,
3213 AC_VERB_SET_POWER_STATE, parm);
3214 snd_hda_codec_write(codec, 0x35, 0,
3215 AC_VERB_SET_POWER_STATE, parm);
3216 } else {
3217 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3218 parm = AC_PWRST_D3;
3219 set_pin_power_state(codec, 0x25, &parm);
3220 snd_hda_codec_write(codec, 0x19, 0,
3221 AC_VERB_SET_POWER_STATE, parm);
3222 snd_hda_codec_write(codec, 0x35, 0,
3223 AC_VERB_SET_POWER_STATE, parm);
3224 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003225
3226 if (spec->hp_independent_mode)
3227 snd_hda_codec_write(codec, 0x9, 0,
3228 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3229
3230 /* Class-D */
3231 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3232 present = snd_hda_jack_detect(codec, 0x25);
3233
3234 parm = AC_PWRST_D3;
3235 set_pin_power_state(codec, 0x24, &parm);
3236 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003237 if (spec->codec_type == VT1802)
3238 snd_hda_codec_write(codec, 0x14, 0,
3239 AC_VERB_SET_POWER_STATE, parm);
3240 else
3241 snd_hda_codec_write(codec, 0x18, 0,
3242 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003243 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3244
3245 /* Mono Out */
3246 present = snd_hda_jack_detect(codec, 0x26);
3247
3248 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003249 if (spec->codec_type == VT1802) {
3250 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3251 snd_hda_codec_write(codec, 0x33, 0,
3252 AC_VERB_SET_POWER_STATE, parm);
3253 snd_hda_codec_write(codec, 0x1c, 0,
3254 AC_VERB_SET_POWER_STATE, parm);
3255 snd_hda_codec_write(codec, 0x3c, 0,
3256 AC_VERB_SET_POWER_STATE, parm);
3257 } else {
3258 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3259 snd_hda_codec_write(codec, 0x31, 0,
3260 AC_VERB_SET_POWER_STATE, parm);
3261 snd_hda_codec_write(codec, 0x17, 0,
3262 AC_VERB_SET_POWER_STATE, parm);
3263 snd_hda_codec_write(codec, 0x3b, 0,
3264 AC_VERB_SET_POWER_STATE, parm);
3265 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003266 /* MW9 (21h) */
3267 if (imux_is_smixer || !is_aa_path_mute(codec))
3268 snd_hda_codec_write(codec, 0x21, 0,
3269 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3270 else
3271 snd_hda_codec_write(codec, 0x21, 0,
3272 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3273}
Lydia Wang25eaba22009-10-10 19:08:43 +08003274
3275/* patch for vt2002P */
3276static int patch_vt2002P(struct hda_codec *codec)
3277{
3278 struct via_spec *spec;
3279 int err;
3280
3281 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003282 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003283 if (spec == NULL)
3284 return -ENOMEM;
3285
Takashi Iwai620e2b22011-06-17 17:19:19 +02003286 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003287 override_mic_boost(codec, 0x2b, 0, 3, 40);
3288 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003289
Lydia Wang25eaba22009-10-10 19:08:43 +08003290 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003291 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003292 if (err < 0) {
3293 via_free(codec);
3294 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003295 }
3296
Lydia Wang118909562011-03-23 17:57:34 +08003297 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003298 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003299 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003300 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003301
Lydia Wang25eaba22009-10-10 19:08:43 +08003302 codec->patch_ops = via_patch_ops;
3303
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003304 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003305 return 0;
3306}
Lydia Wangab6734e2009-10-10 19:08:46 +08003307
3308/* for vt1812 */
3309
Takashi Iwai096a8852011-06-20 12:09:02 +02003310static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003311 /* Enable Boost Volume backdoor */
3312 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003313 /* Enable AOW0 to MW9 */
3314 {0x1, 0xfb8, 0xa8},
3315 { }
3316};
3317
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003318static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3319{
3320 struct via_spec *spec = codec->spec;
3321 int imux_is_smixer =
3322 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3323 unsigned int parm;
3324 unsigned int present;
3325 /* MUX10 (1eh) = stereo mixer */
3326 imux_is_smixer =
3327 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3328 /* inputs */
3329 /* PW 5/6/7 (29h/2ah/2bh) */
3330 parm = AC_PWRST_D3;
3331 set_pin_power_state(codec, 0x29, &parm);
3332 set_pin_power_state(codec, 0x2a, &parm);
3333 set_pin_power_state(codec, 0x2b, &parm);
3334 parm = AC_PWRST_D0;
3335 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3336 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3337 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3338 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3339 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3340
3341 /* outputs */
3342 /* AOW0 (8h)*/
3343 snd_hda_codec_write(codec, 0x8, 0,
3344 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3345
3346 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3347 parm = AC_PWRST_D3;
3348 set_pin_power_state(codec, 0x28, &parm);
3349 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3350 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3351
3352 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3353 parm = AC_PWRST_D3;
3354 set_pin_power_state(codec, 0x25, &parm);
3355 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3356 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3357 if (spec->hp_independent_mode)
3358 snd_hda_codec_write(codec, 0x9, 0,
3359 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3360
3361 /* Internal Speaker */
3362 /* PW0 (24h), MW0(14h), MUX0(34h) */
3363 present = snd_hda_jack_detect(codec, 0x25);
3364
3365 parm = AC_PWRST_D3;
3366 set_pin_power_state(codec, 0x24, &parm);
3367 if (present) {
3368 snd_hda_codec_write(codec, 0x14, 0,
3369 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3370 snd_hda_codec_write(codec, 0x34, 0,
3371 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3372 } else {
3373 snd_hda_codec_write(codec, 0x14, 0,
3374 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3375 snd_hda_codec_write(codec, 0x34, 0,
3376 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3377 }
3378
3379
3380 /* Mono Out */
3381 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3382 present = snd_hda_jack_detect(codec, 0x28);
3383
3384 parm = AC_PWRST_D3;
3385 set_pin_power_state(codec, 0x31, &parm);
3386 if (present) {
3387 snd_hda_codec_write(codec, 0x1c, 0,
3388 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3389 snd_hda_codec_write(codec, 0x3c, 0,
3390 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3391 snd_hda_codec_write(codec, 0x3e, 0,
3392 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3393 } else {
3394 snd_hda_codec_write(codec, 0x1c, 0,
3395 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3396 snd_hda_codec_write(codec, 0x3c, 0,
3397 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3398 snd_hda_codec_write(codec, 0x3e, 0,
3399 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3400 }
3401
3402 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3403 parm = AC_PWRST_D3;
3404 set_pin_power_state(codec, 0x33, &parm);
3405 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3406 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3407
3408}
Lydia Wangab6734e2009-10-10 19:08:46 +08003409
3410/* patch for vt1812 */
3411static int patch_vt1812(struct hda_codec *codec)
3412{
3413 struct via_spec *spec;
3414 int err;
3415
3416 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003417 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003418 if (spec == NULL)
3419 return -ENOMEM;
3420
Takashi Iwai620e2b22011-06-17 17:19:19 +02003421 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003422 override_mic_boost(codec, 0x2b, 0, 3, 40);
3423 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003424
Lydia Wangab6734e2009-10-10 19:08:46 +08003425 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003426 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003427 if (err < 0) {
3428 via_free(codec);
3429 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003430 }
3431
Takashi Iwai096a8852011-06-20 12:09:02 +02003432 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003433
Lydia Wangab6734e2009-10-10 19:08:46 +08003434 codec->patch_ops = via_patch_ops;
3435
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003436 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003437 return 0;
3438}
3439
Joseph Chanc577b8a2006-11-29 15:29:40 +01003440/*
3441 * patch entries
3442 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003443static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003444 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3445 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3446 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3447 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3448 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003449 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003450 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003451 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003452 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003453 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003454 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003455 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003456 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003457 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003458 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003459 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003460 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003461 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003462 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003463 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003464 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003465 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003466 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003467 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003468 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003469 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003470 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003471 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003472 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003473 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003474 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003475 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003476 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003477 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003478 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003479 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003480 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003481 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003482 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003483 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003484 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003485 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003486 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003487 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003488 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003489 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003490 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003491 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003492 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003493 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003494 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003495 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003496 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003497 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003498 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003499 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003500 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003501 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003502 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003503 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003504 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003505 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003506 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003507 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003508 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003509 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003510 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003511 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003512 { .id = 0x11060428, .name = "VT1718S",
3513 .patch = patch_vt1718S},
3514 { .id = 0x11064428, .name = "VT1718S",
3515 .patch = patch_vt1718S},
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08003516 { .id = 0x11060441, .name = "VT2020",
3517 .patch = patch_vt1718S},
3518 { .id = 0x11064441, .name = "VT1828S",
3519 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003520 { .id = 0x11060433, .name = "VT1716S",
3521 .patch = patch_vt1716S},
3522 { .id = 0x1106a721, .name = "VT1716S",
3523 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003524 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3525 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003526 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003527 { .id = 0x11060440, .name = "VT1818S",
3528 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003529 { .id = 0x11060446, .name = "VT1802",
3530 .patch = patch_vt2002P},
3531 { .id = 0x11068446, .name = "VT1802",
3532 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003533 {} /* terminator */
3534};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003535
3536MODULE_ALIAS("snd-hda-codec-id:1106*");
3537
3538static struct hda_codec_preset_list via_list = {
3539 .preset = snd_hda_preset_via,
3540 .owner = THIS_MODULE,
3541};
3542
3543MODULE_LICENSE("GPL");
3544MODULE_DESCRIPTION("VIA HD-audio codec");
3545
3546static int __init patch_via_init(void)
3547{
3548 return snd_hda_add_codec_preset(&via_list);
3549}
3550
3551static void __exit patch_via_exit(void)
3552{
3553 snd_hda_delete_codec_preset(&via_list);
3554}
3555
3556module_init(patch_via_init)
3557module_exit(patch_via_exit)