blob: 93fcea045e3b971e0946ae64672af4f1a2fa7708 [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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Wangbb3c6bfc2009-10-10 19:08:39 +0800245 else if (dev_id == 0x0441 || dev_id == 0x4441)
246 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800247 else if (dev_id == 0x0438 || dev_id == 0x4438)
248 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800249 else if (dev_id == 0x0448)
250 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800251 else if (dev_id == 0x0440)
252 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800253 else if ((dev_id & 0xfff) == 0x446)
254 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800255 else
256 codec_type = UNKNOWN;
257 return codec_type;
258};
259
Lydia Wangec7e7e42011-03-24 12:43:44 +0800260#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800261#define VIA_HP_EVENT 0x01
262#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200263#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800264
Joseph Chanc577b8a2006-11-29 15:29:40 +0100265enum {
266 VIA_CTL_WIDGET_VOL,
267 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800268 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100269};
270
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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-06-21 15:57:44 +0200488 hda_nid_t pin, nid;
489 int i, idx;
Takashi Iwai5d417622011-06-20 11:32:27 +0200490
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200491 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200492 return;
Takashi Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwaibc9b562382008-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 Iwaibc9b562382008-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 Iwaibc9b562382008-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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
Joseph Chanc577b8a2006-11-29 15:29:40 +01002341
Takashi Iwai8df2a312011-06-21 11:48:29 +02002342 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002343 err = via_hp_build(codec);
2344 if (err < 0)
2345 return err;
2346 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002347
Takashi Iwaif4a78282011-06-17 18:46:48 +02002348 err = via_smart51_build(codec);
2349 if (err < 0)
2350 return err;
2351
Takashi Iwai5d417622011-06-20 11:32:27 +02002352 /* assign slave outs */
2353 if (spec->slave_dig_outs[0])
2354 codec->slave_dig_outs = spec->slave_dig_outs;
2355
Joseph Chanc577b8a2006-11-29 15:29:40 +01002356 return 1;
2357}
2358
Takashi Iwai5d417622011-06-20 11:32:27 +02002359static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002360{
Lydia Wang25eaba22009-10-10 19:08:43 +08002361 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002362 if (spec->multiout.dig_out_nid)
2363 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2364 if (spec->slave_dig_outs[0])
2365 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2366}
Lydia Wang25eaba22009-10-10 19:08:43 +08002367
Takashi Iwai5d417622011-06-20 11:32:27 +02002368static void via_auto_init_dig_in(struct hda_codec *codec)
2369{
2370 struct via_spec *spec = codec->spec;
2371 if (!spec->dig_in_nid)
2372 return;
2373 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2374 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2375}
2376
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002377/* initialize the unsolicited events */
2378static void via_auto_init_unsol_event(struct hda_codec *codec)
2379{
2380 struct via_spec *spec = codec->spec;
2381 struct auto_pin_cfg *cfg = &spec->autocfg;
2382 unsigned int ev;
2383 int i;
2384
2385 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2386 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2387 AC_VERB_SET_UNSOLICITED_ENABLE,
2388 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2389
2390 if (cfg->speaker_pins[0])
2391 ev = VIA_LINE_EVENT;
2392 else
2393 ev = 0;
2394 for (i = 0; i < cfg->line_outs; i++) {
2395 if (cfg->line_out_pins[i] &&
2396 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002397 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002398 AC_VERB_SET_UNSOLICITED_ENABLE,
2399 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2400 }
2401
2402 for (i = 0; i < cfg->num_inputs; i++) {
2403 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2404 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2405 AC_VERB_SET_UNSOLICITED_ENABLE,
2406 AC_USRSP_EN | VIA_JACK_EVENT);
2407 }
2408}
2409
Takashi Iwai5d417622011-06-20 11:32:27 +02002410static int via_init(struct hda_codec *codec)
2411{
2412 struct via_spec *spec = codec->spec;
2413 int i;
2414
2415 for (i = 0; i < spec->num_iverbs; i++)
2416 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2417
Joseph Chanc577b8a2006-11-29 15:29:40 +01002418 via_auto_init_multi_out(codec);
2419 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002420 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002421 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002422 via_auto_init_dig_outs(codec);
2423 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002424
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002425 via_auto_init_unsol_event(codec);
2426
2427 via_hp_automute(codec);
2428 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002429
Joseph Chanc577b8a2006-11-29 15:29:40 +01002430 return 0;
2431}
2432
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002433static void vt1708_update_hp_jack_state(struct work_struct *work)
2434{
2435 struct via_spec *spec = container_of(work, struct via_spec,
2436 vt1708_hp_work.work);
2437 if (spec->codec_type != VT1708)
2438 return;
2439 /* if jack state toggled */
2440 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002441 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002442 spec->vt1708_hp_present ^= 1;
2443 via_hp_automute(spec->codec);
2444 }
2445 vt1708_start_hp_work(spec);
2446}
2447
Takashi Iwai337b9d02009-07-07 18:18:59 +02002448static int get_mux_nids(struct hda_codec *codec)
2449{
2450 struct via_spec *spec = codec->spec;
2451 hda_nid_t nid, conn[8];
2452 unsigned int type;
2453 int i, n;
2454
2455 for (i = 0; i < spec->num_adc_nids; i++) {
2456 nid = spec->adc_nids[i];
2457 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002458 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002459 if (type == AC_WID_PIN)
2460 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002461 n = snd_hda_get_connections(codec, nid, conn,
2462 ARRAY_SIZE(conn));
2463 if (n <= 0)
2464 break;
2465 if (n > 1) {
2466 spec->mux_nids[i] = nid;
2467 break;
2468 }
2469 nid = conn[0];
2470 }
2471 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002472 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002473}
2474
Joseph Chanc577b8a2006-11-29 15:29:40 +01002475static int patch_vt1708(struct hda_codec *codec)
2476{
2477 struct via_spec *spec;
2478 int err;
2479
2480 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002481 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002482 if (spec == NULL)
2483 return -ENOMEM;
2484
Takashi Iwai620e2b22011-06-17 17:19:19 +02002485 spec->aa_mix_nid = 0x17;
2486
Takashi Iwai12daef62011-06-18 17:45:49 +02002487 /* Add HP and CD pin config connect bit re-config action */
2488 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2489 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2490
Joseph Chanc577b8a2006-11-29 15:29:40 +01002491 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002492 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002493 if (err < 0) {
2494 via_free(codec);
2495 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002496 }
2497
Takashi Iwai12daef62011-06-18 17:45:49 +02002498 /* add jack detect on/off control */
2499 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2500 return -ENOMEM;
2501
Takashi Iwaibc9b562382008-05-23 17:50:27 +02002502 /* disable 32bit format on VT1708 */
2503 if (codec->vendor_id == 0x11061708)
2504 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002505
Lydia Wange322a362011-06-29 13:52:02 +08002506 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2507
Joseph Chanc577b8a2006-11-29 15:29:40 +01002508 codec->patch_ops = via_patch_ops;
2509
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002510 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002511 return 0;
2512}
2513
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002514static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002515{
2516 struct via_spec *spec;
2517 int err;
2518
2519 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002520 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002521 if (spec == NULL)
2522 return -ENOMEM;
2523
Takashi Iwai620e2b22011-06-17 17:19:19 +02002524 spec->aa_mix_nid = 0x18;
2525
Takashi Iwai12daef62011-06-18 17:45:49 +02002526 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002527 if (err < 0) {
2528 via_free(codec);
2529 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002530 }
2531
Joseph Chanc577b8a2006-11-29 15:29:40 +01002532 codec->patch_ops = via_patch_ops;
2533
Josepch Chanf7278fd2007-12-13 16:40:40 +01002534 return 0;
2535}
2536
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002537static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2538{
2539 struct via_spec *spec = codec->spec;
2540 int imux_is_smixer;
2541 unsigned int parm;
2542 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002543 if ((spec->codec_type != VT1708B_4CH) &&
2544 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002545 is_8ch = 1;
2546
2547 /* SW0 (17h) = stereo mixer */
2548 imux_is_smixer =
2549 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2550 == ((spec->codec_type == VT1708S) ? 5 : 0));
2551 /* inputs */
2552 /* PW 1/2/5 (1ah/1bh/1eh) */
2553 parm = AC_PWRST_D3;
2554 set_pin_power_state(codec, 0x1a, &parm);
2555 set_pin_power_state(codec, 0x1b, &parm);
2556 set_pin_power_state(codec, 0x1e, &parm);
2557 if (imux_is_smixer)
2558 parm = AC_PWRST_D0;
2559 /* SW0 (17h), AIW 0/1 (13h/14h) */
2560 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2561 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2562 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2563
2564 /* outputs */
2565 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2566 parm = AC_PWRST_D3;
2567 set_pin_power_state(codec, 0x19, &parm);
2568 if (spec->smart51_enabled)
2569 set_pin_power_state(codec, 0x1b, &parm);
2570 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2571 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2572
2573 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2574 if (is_8ch) {
2575 parm = AC_PWRST_D3;
2576 set_pin_power_state(codec, 0x22, &parm);
2577 if (spec->smart51_enabled)
2578 set_pin_power_state(codec, 0x1a, &parm);
2579 snd_hda_codec_write(codec, 0x26, 0,
2580 AC_VERB_SET_POWER_STATE, parm);
2581 snd_hda_codec_write(codec, 0x24, 0,
2582 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002583 } else if (codec->vendor_id == 0x11064397) {
2584 /* PW7(23h), SW2(27h), AOW2(25h) */
2585 parm = AC_PWRST_D3;
2586 set_pin_power_state(codec, 0x23, &parm);
2587 if (spec->smart51_enabled)
2588 set_pin_power_state(codec, 0x1a, &parm);
2589 snd_hda_codec_write(codec, 0x27, 0,
2590 AC_VERB_SET_POWER_STATE, parm);
2591 snd_hda_codec_write(codec, 0x25, 0,
2592 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002593 }
2594
2595 /* PW 3/4/7 (1ch/1dh/23h) */
2596 parm = AC_PWRST_D3;
2597 /* force to D0 for internal Speaker */
2598 set_pin_power_state(codec, 0x1c, &parm);
2599 set_pin_power_state(codec, 0x1d, &parm);
2600 if (is_8ch)
2601 set_pin_power_state(codec, 0x23, &parm);
2602
2603 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2604 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2605 imux_is_smixer ? AC_PWRST_D0 : parm);
2606 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2607 if (is_8ch) {
2608 snd_hda_codec_write(codec, 0x25, 0,
2609 AC_VERB_SET_POWER_STATE, parm);
2610 snd_hda_codec_write(codec, 0x27, 0,
2611 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002612 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2613 snd_hda_codec_write(codec, 0x25, 0,
2614 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002615}
2616
Lydia Wang518bf3b2009-10-10 19:07:29 +08002617static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002618static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002619{
2620 struct via_spec *spec;
2621 int err;
2622
Lydia Wang518bf3b2009-10-10 19:07:29 +08002623 if (get_codec_type(codec) == VT1708BCE)
2624 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002625
Josepch Chanf7278fd2007-12-13 16:40:40 +01002626 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002627 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002628 if (spec == NULL)
2629 return -ENOMEM;
2630
Takashi Iwai620e2b22011-06-17 17:19:19 +02002631 spec->aa_mix_nid = 0x16;
2632
Josepch Chanf7278fd2007-12-13 16:40:40 +01002633 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002634 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002635 if (err < 0) {
2636 via_free(codec);
2637 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002638 }
2639
Josepch Chanf7278fd2007-12-13 16:40:40 +01002640 codec->patch_ops = via_patch_ops;
2641
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002642 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2643
Josepch Chanf7278fd2007-12-13 16:40:40 +01002644 return 0;
2645}
2646
Harald Welted949cac2008-09-09 15:56:01 +08002647/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002648static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002649 /* Enable Mic Boost Volume backdoor */
2650 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002651 /* don't bybass mixer */
2652 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002653 { }
2654};
2655
Takashi Iwai9da29272009-05-07 16:31:14 +02002656/* fill out digital output widgets; one for master and one for slave outputs */
2657static void fill_dig_outs(struct hda_codec *codec)
2658{
2659 struct via_spec *spec = codec->spec;
2660 int i;
2661
2662 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2663 hda_nid_t nid;
2664 int conn;
2665
2666 nid = spec->autocfg.dig_out_pins[i];
2667 if (!nid)
2668 continue;
2669 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2670 if (conn < 1)
2671 continue;
2672 if (!spec->multiout.dig_out_nid)
2673 spec->multiout.dig_out_nid = nid;
2674 else {
2675 spec->slave_dig_outs[0] = nid;
2676 break; /* at most two dig outs */
2677 }
2678 }
2679}
2680
Takashi Iwai12daef62011-06-18 17:45:49 +02002681static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002682{
2683 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002684 hda_nid_t dig_nid;
2685 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002686
Takashi Iwai12daef62011-06-18 17:45:49 +02002687 if (!spec->autocfg.dig_in_pin)
2688 return;
Harald Welted949cac2008-09-09 15:56:01 +08002689
Takashi Iwai12daef62011-06-18 17:45:49 +02002690 dig_nid = codec->start_nid;
2691 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2692 unsigned int wcaps = get_wcaps(codec, dig_nid);
2693 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2694 continue;
2695 if (!(wcaps & AC_WCAP_DIGITAL))
2696 continue;
2697 if (!(wcaps & AC_WCAP_CONN_LIST))
2698 continue;
2699 err = get_connection_index(codec, dig_nid,
2700 spec->autocfg.dig_in_pin);
2701 if (err >= 0) {
2702 spec->dig_in_nid = dig_nid;
2703 break;
2704 }
2705 }
Harald Welted949cac2008-09-09 15:56:01 +08002706}
2707
Lydia Wang6369bcf2009-10-10 19:08:31 +08002708static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2709 int offset, int num_steps, int step_size)
2710{
2711 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2712 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2713 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2714 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2715 (0 << AC_AMPCAP_MUTE_SHIFT));
2716}
2717
Harald Welted949cac2008-09-09 15:56:01 +08002718static int patch_vt1708S(struct hda_codec *codec)
2719{
2720 struct via_spec *spec;
2721 int err;
2722
2723 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002724 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002725 if (spec == NULL)
2726 return -ENOMEM;
2727
Takashi Iwai620e2b22011-06-17 17:19:19 +02002728 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002729 override_mic_boost(codec, 0x1a, 0, 3, 40);
2730 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002731
Harald Welted949cac2008-09-09 15:56:01 +08002732 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002733 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002734 if (err < 0) {
2735 via_free(codec);
2736 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002737 }
2738
Takashi Iwai096a8852011-06-20 12:09:02 +02002739 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002740
Harald Welted949cac2008-09-09 15:56:01 +08002741 codec->patch_ops = via_patch_ops;
2742
Lydia Wang518bf3b2009-10-10 19:07:29 +08002743 /* correct names for VT1708BCE */
2744 if (get_codec_type(codec) == VT1708BCE) {
2745 kfree(codec->chip_name);
2746 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2747 snprintf(codec->bus->card->mixername,
2748 sizeof(codec->bus->card->mixername),
2749 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f630f2011-03-22 16:25:56 +08002750 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002751 /* correct names for VT1705 */
2752 if (codec->vendor_id == 0x11064397) {
2753 kfree(codec->chip_name);
2754 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2755 snprintf(codec->bus->card->mixername,
2756 sizeof(codec->bus->card->mixername),
2757 "%s %s", codec->vendor_name, codec->chip_name);
2758 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002759 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002760 return 0;
2761}
2762
2763/* Patch for VT1702 */
2764
Takashi Iwai096a8852011-06-20 12:09:02 +02002765static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002766 /* mixer enable */
2767 {0x1, 0xF88, 0x3},
2768 /* GPIO 0~2 */
2769 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002770 { }
2771};
2772
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002773static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2774{
2775 int imux_is_smixer =
2776 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2777 unsigned int parm;
2778 /* inputs */
2779 /* PW 1/2/5 (14h/15h/18h) */
2780 parm = AC_PWRST_D3;
2781 set_pin_power_state(codec, 0x14, &parm);
2782 set_pin_power_state(codec, 0x15, &parm);
2783 set_pin_power_state(codec, 0x18, &parm);
2784 if (imux_is_smixer)
2785 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2786 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2787 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2788 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2789 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2790 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2791
2792 /* outputs */
2793 /* PW 3/4 (16h/17h) */
2794 parm = AC_PWRST_D3;
2795 set_pin_power_state(codec, 0x17, &parm);
2796 set_pin_power_state(codec, 0x16, &parm);
2797 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2798 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2799 imux_is_smixer ? AC_PWRST_D0 : parm);
2800 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2801 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2802}
2803
Harald Welted949cac2008-09-09 15:56:01 +08002804static int patch_vt1702(struct hda_codec *codec)
2805{
2806 struct via_spec *spec;
2807 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002808
2809 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002810 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002811 if (spec == NULL)
2812 return -ENOMEM;
2813
Takashi Iwai620e2b22011-06-17 17:19:19 +02002814 spec->aa_mix_nid = 0x1a;
2815
Takashi Iwai12daef62011-06-18 17:45:49 +02002816 /* limit AA path volume to 0 dB */
2817 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2818 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2819 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2820 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2821 (1 << AC_AMPCAP_MUTE_SHIFT));
2822
Harald Welted949cac2008-09-09 15:56:01 +08002823 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002824 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002825 if (err < 0) {
2826 via_free(codec);
2827 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002828 }
2829
Takashi Iwai096a8852011-06-20 12:09:02 +02002830 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002831
Harald Welted949cac2008-09-09 15:56:01 +08002832 codec->patch_ops = via_patch_ops;
2833
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002834 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002835 return 0;
2836}
2837
Lydia Wangeb7188c2009-10-10 19:08:34 +08002838/* Patch for VT1718S */
2839
Takashi Iwai096a8852011-06-20 12:09:02 +02002840static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002841 /* Enable MW0 adjust Gain 5 */
2842 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002843 /* Enable Boost Volume backdoor */
2844 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002845
Lydia Wangeb7188c2009-10-10 19:08:34 +08002846 { }
2847};
2848
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002849static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2850{
2851 struct via_spec *spec = codec->spec;
2852 int imux_is_smixer;
2853 unsigned int parm;
2854 /* MUX6 (1eh) = stereo mixer */
2855 imux_is_smixer =
2856 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2857 /* inputs */
2858 /* PW 5/6/7 (29h/2ah/2bh) */
2859 parm = AC_PWRST_D3;
2860 set_pin_power_state(codec, 0x29, &parm);
2861 set_pin_power_state(codec, 0x2a, &parm);
2862 set_pin_power_state(codec, 0x2b, &parm);
2863 if (imux_is_smixer)
2864 parm = AC_PWRST_D0;
2865 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2866 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2867 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2868 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2869 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2870
2871 /* outputs */
2872 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2873 parm = AC_PWRST_D3;
2874 set_pin_power_state(codec, 0x27, &parm);
2875 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2876 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2877
2878 /* PW2 (26h), AOW2 (ah) */
2879 parm = AC_PWRST_D3;
2880 set_pin_power_state(codec, 0x26, &parm);
2881 if (spec->smart51_enabled)
2882 set_pin_power_state(codec, 0x2b, &parm);
2883 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2884
2885 /* PW0 (24h), AOW0 (8h) */
2886 parm = AC_PWRST_D3;
2887 set_pin_power_state(codec, 0x24, &parm);
2888 if (!spec->hp_independent_mode) /* check for redirected HP */
2889 set_pin_power_state(codec, 0x28, &parm);
2890 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2891 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2892 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2893 imux_is_smixer ? AC_PWRST_D0 : parm);
2894
2895 /* PW1 (25h), AOW1 (9h) */
2896 parm = AC_PWRST_D3;
2897 set_pin_power_state(codec, 0x25, &parm);
2898 if (spec->smart51_enabled)
2899 set_pin_power_state(codec, 0x2a, &parm);
2900 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2901
2902 if (spec->hp_independent_mode) {
2903 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2904 parm = AC_PWRST_D3;
2905 set_pin_power_state(codec, 0x28, &parm);
2906 snd_hda_codec_write(codec, 0x1b, 0,
2907 AC_VERB_SET_POWER_STATE, parm);
2908 snd_hda_codec_write(codec, 0x34, 0,
2909 AC_VERB_SET_POWER_STATE, parm);
2910 snd_hda_codec_write(codec, 0xc, 0,
2911 AC_VERB_SET_POWER_STATE, parm);
2912 }
2913}
2914
Lydia Wangeb7188c2009-10-10 19:08:34 +08002915static int patch_vt1718S(struct hda_codec *codec)
2916{
2917 struct via_spec *spec;
2918 int err;
2919
2920 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002921 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002922 if (spec == NULL)
2923 return -ENOMEM;
2924
Takashi Iwai620e2b22011-06-17 17:19:19 +02002925 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002926 override_mic_boost(codec, 0x2b, 0, 3, 40);
2927 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002928
Lydia Wangeb7188c2009-10-10 19:08:34 +08002929 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002930 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002931 if (err < 0) {
2932 via_free(codec);
2933 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002934 }
2935
Takashi Iwai096a8852011-06-20 12:09:02 +02002936 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002937
Lydia Wangeb7188c2009-10-10 19:08:34 +08002938 codec->patch_ops = via_patch_ops;
2939
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002940 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2941
Lydia Wangeb7188c2009-10-10 19:08:34 +08002942 return 0;
2943}
Lydia Wangf3db4232009-10-10 19:08:41 +08002944
2945/* Patch for VT1716S */
2946
2947static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2948 struct snd_ctl_elem_info *uinfo)
2949{
2950 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2951 uinfo->count = 1;
2952 uinfo->value.integer.min = 0;
2953 uinfo->value.integer.max = 1;
2954 return 0;
2955}
2956
2957static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2958 struct snd_ctl_elem_value *ucontrol)
2959{
2960 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2961 int index = 0;
2962
2963 index = snd_hda_codec_read(codec, 0x26, 0,
2964 AC_VERB_GET_CONNECT_SEL, 0);
2965 if (index != -1)
2966 *ucontrol->value.integer.value = index;
2967
2968 return 0;
2969}
2970
2971static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2972 struct snd_ctl_elem_value *ucontrol)
2973{
2974 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2975 struct via_spec *spec = codec->spec;
2976 int index = *ucontrol->value.integer.value;
2977
2978 snd_hda_codec_write(codec, 0x26, 0,
2979 AC_VERB_SET_CONNECT_SEL, index);
2980 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002981 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002982 return 1;
2983}
2984
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002985static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002986 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2987 {
2988 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2989 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002990 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08002991 .count = 1,
2992 .info = vt1716s_dmic_info,
2993 .get = vt1716s_dmic_get,
2994 .put = vt1716s_dmic_put,
2995 },
2996 {} /* end */
2997};
2998
2999
3000/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003001static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003002 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3003 { } /* end */
3004};
3005
Takashi Iwai096a8852011-06-20 12:09:02 +02003006static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003007 /* Enable Boost Volume backdoor */
3008 {0x1, 0xf8a, 0x80},
3009 /* don't bybass mixer */
3010 {0x1, 0xf88, 0xc0},
3011 /* Enable mono output */
3012 {0x1, 0xf90, 0x08},
3013 { }
3014};
3015
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003016static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3017{
3018 struct via_spec *spec = codec->spec;
3019 int imux_is_smixer;
3020 unsigned int parm;
3021 unsigned int mono_out, present;
3022 /* SW0 (17h) = stereo mixer */
3023 imux_is_smixer =
3024 (snd_hda_codec_read(codec, 0x17, 0,
3025 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3026 /* inputs */
3027 /* PW 1/2/5 (1ah/1bh/1eh) */
3028 parm = AC_PWRST_D3;
3029 set_pin_power_state(codec, 0x1a, &parm);
3030 set_pin_power_state(codec, 0x1b, &parm);
3031 set_pin_power_state(codec, 0x1e, &parm);
3032 if (imux_is_smixer)
3033 parm = AC_PWRST_D0;
3034 /* SW0 (17h), AIW0(13h) */
3035 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3036 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3037
3038 parm = AC_PWRST_D3;
3039 set_pin_power_state(codec, 0x1e, &parm);
3040 /* PW11 (22h) */
3041 if (spec->dmic_enabled)
3042 set_pin_power_state(codec, 0x22, &parm);
3043 else
3044 snd_hda_codec_write(codec, 0x22, 0,
3045 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3046
3047 /* SW2(26h), AIW1(14h) */
3048 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3049 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3050
3051 /* outputs */
3052 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3053 parm = AC_PWRST_D3;
3054 set_pin_power_state(codec, 0x19, &parm);
3055 /* Smart 5.1 PW2(1bh) */
3056 if (spec->smart51_enabled)
3057 set_pin_power_state(codec, 0x1b, &parm);
3058 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3059 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3060
3061 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3062 parm = AC_PWRST_D3;
3063 set_pin_power_state(codec, 0x23, &parm);
3064 /* Smart 5.1 PW1(1ah) */
3065 if (spec->smart51_enabled)
3066 set_pin_power_state(codec, 0x1a, &parm);
3067 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3068
3069 /* Smart 5.1 PW5(1eh) */
3070 if (spec->smart51_enabled)
3071 set_pin_power_state(codec, 0x1e, &parm);
3072 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3073
3074 /* Mono out */
3075 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3076 present = snd_hda_jack_detect(codec, 0x1c);
3077
3078 if (present)
3079 mono_out = 0;
3080 else {
3081 present = snd_hda_jack_detect(codec, 0x1d);
3082 if (!spec->hp_independent_mode && present)
3083 mono_out = 0;
3084 else
3085 mono_out = 1;
3086 }
3087 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3088 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3089 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3090 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3091
3092 /* PW 3/4 (1ch/1dh) */
3093 parm = AC_PWRST_D3;
3094 set_pin_power_state(codec, 0x1c, &parm);
3095 set_pin_power_state(codec, 0x1d, &parm);
3096 /* HP Independent Mode, power on AOW3 */
3097 if (spec->hp_independent_mode)
3098 snd_hda_codec_write(codec, 0x25, 0,
3099 AC_VERB_SET_POWER_STATE, parm);
3100
3101 /* force to D0 for internal Speaker */
3102 /* MW0 (16h), AOW0 (10h) */
3103 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3104 imux_is_smixer ? AC_PWRST_D0 : parm);
3105 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3106 mono_out ? AC_PWRST_D0 : parm);
3107}
3108
Lydia Wangf3db4232009-10-10 19:08:41 +08003109static int patch_vt1716S(struct hda_codec *codec)
3110{
3111 struct via_spec *spec;
3112 int err;
3113
3114 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003115 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003116 if (spec == NULL)
3117 return -ENOMEM;
3118
Takashi Iwai620e2b22011-06-17 17:19:19 +02003119 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003120 override_mic_boost(codec, 0x1a, 0, 3, 40);
3121 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003122
Lydia Wangf3db4232009-10-10 19:08:41 +08003123 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003124 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003125 if (err < 0) {
3126 via_free(codec);
3127 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003128 }
3129
Takashi Iwai096a8852011-06-20 12:09:02 +02003130 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003131
Lydia Wangf3db4232009-10-10 19:08:41 +08003132 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3133 spec->num_mixers++;
3134
3135 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3136
3137 codec->patch_ops = via_patch_ops;
3138
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003139 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003140 return 0;
3141}
Lydia Wang25eaba22009-10-10 19:08:43 +08003142
3143/* for vt2002P */
3144
Takashi Iwai096a8852011-06-20 12:09:02 +02003145static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003146 /* Class-D speaker related verbs */
3147 {0x1, 0xfe0, 0x4},
3148 {0x1, 0xfe9, 0x80},
3149 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003150 /* Enable Boost Volume backdoor */
3151 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003152 /* Enable AOW0 to MW9 */
3153 {0x1, 0xfb8, 0x88},
3154 { }
3155};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003156
Takashi Iwai096a8852011-06-20 12:09:02 +02003157static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003158 /* Enable Boost Volume backdoor */
3159 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003160 /* Enable AOW0 to MW9 */
3161 {0x1, 0xfb8, 0x88},
3162 { }
3163};
Lydia Wang25eaba22009-10-10 19:08:43 +08003164
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003165static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3166{
3167 struct via_spec *spec = codec->spec;
3168 int imux_is_smixer;
3169 unsigned int parm;
3170 unsigned int present;
3171 /* MUX9 (1eh) = stereo mixer */
3172 imux_is_smixer =
3173 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3174 /* inputs */
3175 /* PW 5/6/7 (29h/2ah/2bh) */
3176 parm = AC_PWRST_D3;
3177 set_pin_power_state(codec, 0x29, &parm);
3178 set_pin_power_state(codec, 0x2a, &parm);
3179 set_pin_power_state(codec, 0x2b, &parm);
3180 parm = AC_PWRST_D0;
3181 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3182 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3183 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3184 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3185 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3186
3187 /* outputs */
3188 /* AOW0 (8h)*/
3189 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3190
Lydia Wang118909562011-03-23 17:57:34 +08003191 if (spec->codec_type == VT1802) {
3192 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3193 parm = AC_PWRST_D3;
3194 set_pin_power_state(codec, 0x28, &parm);
3195 snd_hda_codec_write(codec, 0x18, 0,
3196 AC_VERB_SET_POWER_STATE, parm);
3197 snd_hda_codec_write(codec, 0x38, 0,
3198 AC_VERB_SET_POWER_STATE, parm);
3199 } else {
3200 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3201 parm = AC_PWRST_D3;
3202 set_pin_power_state(codec, 0x26, &parm);
3203 snd_hda_codec_write(codec, 0x1c, 0,
3204 AC_VERB_SET_POWER_STATE, parm);
3205 snd_hda_codec_write(codec, 0x37, 0,
3206 AC_VERB_SET_POWER_STATE, parm);
3207 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003208
Lydia Wang118909562011-03-23 17:57:34 +08003209 if (spec->codec_type == VT1802) {
3210 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3211 parm = AC_PWRST_D3;
3212 set_pin_power_state(codec, 0x25, &parm);
3213 snd_hda_codec_write(codec, 0x15, 0,
3214 AC_VERB_SET_POWER_STATE, parm);
3215 snd_hda_codec_write(codec, 0x35, 0,
3216 AC_VERB_SET_POWER_STATE, parm);
3217 } else {
3218 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3219 parm = AC_PWRST_D3;
3220 set_pin_power_state(codec, 0x25, &parm);
3221 snd_hda_codec_write(codec, 0x19, 0,
3222 AC_VERB_SET_POWER_STATE, parm);
3223 snd_hda_codec_write(codec, 0x35, 0,
3224 AC_VERB_SET_POWER_STATE, parm);
3225 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003226
3227 if (spec->hp_independent_mode)
3228 snd_hda_codec_write(codec, 0x9, 0,
3229 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3230
3231 /* Class-D */
3232 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3233 present = snd_hda_jack_detect(codec, 0x25);
3234
3235 parm = AC_PWRST_D3;
3236 set_pin_power_state(codec, 0x24, &parm);
3237 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003238 if (spec->codec_type == VT1802)
3239 snd_hda_codec_write(codec, 0x14, 0,
3240 AC_VERB_SET_POWER_STATE, parm);
3241 else
3242 snd_hda_codec_write(codec, 0x18, 0,
3243 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003244 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3245
3246 /* Mono Out */
3247 present = snd_hda_jack_detect(codec, 0x26);
3248
3249 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003250 if (spec->codec_type == VT1802) {
3251 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3252 snd_hda_codec_write(codec, 0x33, 0,
3253 AC_VERB_SET_POWER_STATE, parm);
3254 snd_hda_codec_write(codec, 0x1c, 0,
3255 AC_VERB_SET_POWER_STATE, parm);
3256 snd_hda_codec_write(codec, 0x3c, 0,
3257 AC_VERB_SET_POWER_STATE, parm);
3258 } else {
3259 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3260 snd_hda_codec_write(codec, 0x31, 0,
3261 AC_VERB_SET_POWER_STATE, parm);
3262 snd_hda_codec_write(codec, 0x17, 0,
3263 AC_VERB_SET_POWER_STATE, parm);
3264 snd_hda_codec_write(codec, 0x3b, 0,
3265 AC_VERB_SET_POWER_STATE, parm);
3266 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003267 /* MW9 (21h) */
3268 if (imux_is_smixer || !is_aa_path_mute(codec))
3269 snd_hda_codec_write(codec, 0x21, 0,
3270 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3271 else
3272 snd_hda_codec_write(codec, 0x21, 0,
3273 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3274}
Lydia Wang25eaba22009-10-10 19:08:43 +08003275
3276/* patch for vt2002P */
3277static int patch_vt2002P(struct hda_codec *codec)
3278{
3279 struct via_spec *spec;
3280 int err;
3281
3282 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003283 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003284 if (spec == NULL)
3285 return -ENOMEM;
3286
Takashi Iwai620e2b22011-06-17 17:19:19 +02003287 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003288 override_mic_boost(codec, 0x2b, 0, 3, 40);
3289 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003290
Lydia Wang25eaba22009-10-10 19:08:43 +08003291 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003292 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003293 if (err < 0) {
3294 via_free(codec);
3295 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003296 }
3297
Lydia Wang118909562011-03-23 17:57:34 +08003298 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003299 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003300 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003301 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003302
Lydia Wang25eaba22009-10-10 19:08:43 +08003303 codec->patch_ops = via_patch_ops;
3304
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003305 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003306 return 0;
3307}
Lydia Wangab6734e2009-10-10 19:08:46 +08003308
3309/* for vt1812 */
3310
Takashi Iwai096a8852011-06-20 12:09:02 +02003311static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003312 /* Enable Boost Volume backdoor */
3313 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003314 /* Enable AOW0 to MW9 */
3315 {0x1, 0xfb8, 0xa8},
3316 { }
3317};
3318
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003319static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3320{
3321 struct via_spec *spec = codec->spec;
3322 int imux_is_smixer =
3323 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3324 unsigned int parm;
3325 unsigned int present;
3326 /* MUX10 (1eh) = stereo mixer */
3327 imux_is_smixer =
3328 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3329 /* inputs */
3330 /* PW 5/6/7 (29h/2ah/2bh) */
3331 parm = AC_PWRST_D3;
3332 set_pin_power_state(codec, 0x29, &parm);
3333 set_pin_power_state(codec, 0x2a, &parm);
3334 set_pin_power_state(codec, 0x2b, &parm);
3335 parm = AC_PWRST_D0;
3336 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3337 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3338 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3339 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3340 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3341
3342 /* outputs */
3343 /* AOW0 (8h)*/
3344 snd_hda_codec_write(codec, 0x8, 0,
3345 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3346
3347 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3348 parm = AC_PWRST_D3;
3349 set_pin_power_state(codec, 0x28, &parm);
3350 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3351 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3352
3353 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3354 parm = AC_PWRST_D3;
3355 set_pin_power_state(codec, 0x25, &parm);
3356 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3357 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3358 if (spec->hp_independent_mode)
3359 snd_hda_codec_write(codec, 0x9, 0,
3360 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3361
3362 /* Internal Speaker */
3363 /* PW0 (24h), MW0(14h), MUX0(34h) */
3364 present = snd_hda_jack_detect(codec, 0x25);
3365
3366 parm = AC_PWRST_D3;
3367 set_pin_power_state(codec, 0x24, &parm);
3368 if (present) {
3369 snd_hda_codec_write(codec, 0x14, 0,
3370 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3371 snd_hda_codec_write(codec, 0x34, 0,
3372 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3373 } else {
3374 snd_hda_codec_write(codec, 0x14, 0,
3375 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3376 snd_hda_codec_write(codec, 0x34, 0,
3377 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3378 }
3379
3380
3381 /* Mono Out */
3382 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3383 present = snd_hda_jack_detect(codec, 0x28);
3384
3385 parm = AC_PWRST_D3;
3386 set_pin_power_state(codec, 0x31, &parm);
3387 if (present) {
3388 snd_hda_codec_write(codec, 0x1c, 0,
3389 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3390 snd_hda_codec_write(codec, 0x3c, 0,
3391 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3392 snd_hda_codec_write(codec, 0x3e, 0,
3393 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3394 } else {
3395 snd_hda_codec_write(codec, 0x1c, 0,
3396 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3397 snd_hda_codec_write(codec, 0x3c, 0,
3398 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3399 snd_hda_codec_write(codec, 0x3e, 0,
3400 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3401 }
3402
3403 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3404 parm = AC_PWRST_D3;
3405 set_pin_power_state(codec, 0x33, &parm);
3406 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3407 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3408
3409}
Lydia Wangab6734e2009-10-10 19:08:46 +08003410
3411/* patch for vt1812 */
3412static int patch_vt1812(struct hda_codec *codec)
3413{
3414 struct via_spec *spec;
3415 int err;
3416
3417 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003418 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003419 if (spec == NULL)
3420 return -ENOMEM;
3421
Takashi Iwai620e2b22011-06-17 17:19:19 +02003422 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003423 override_mic_boost(codec, 0x2b, 0, 3, 40);
3424 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003425
Lydia Wangab6734e2009-10-10 19:08:46 +08003426 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003427 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003428 if (err < 0) {
3429 via_free(codec);
3430 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003431 }
3432
Takashi Iwai096a8852011-06-20 12:09:02 +02003433 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003434
Lydia Wangab6734e2009-10-10 19:08:46 +08003435 codec->patch_ops = via_patch_ops;
3436
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003437 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003438 return 0;
3439}
3440
Joseph Chanc577b8a2006-11-29 15:29:40 +01003441/*
3442 * patch entries
3443 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003444static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003445 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3446 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3447 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3448 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3449 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003450 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003451 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003452 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003453 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003454 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003455 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003456 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003457 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003458 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003459 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003460 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003461 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003462 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003463 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003464 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003465 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003466 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003467 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003468 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003469 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003470 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003471 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003472 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003473 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003474 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003475 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003476 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003477 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003478 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003479 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003480 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003481 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003482 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003483 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003484 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003485 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003486 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003487 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003488 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003489 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003490 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003491 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003492 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003493 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003494 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003495 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003496 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003497 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003498 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003499 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003500 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003501 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003502 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003503 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003504 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003505 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003506 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003507 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003508 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003509 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003510 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003511 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003512 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003513 { .id = 0x11060428, .name = "VT1718S",
3514 .patch = patch_vt1718S},
3515 { .id = 0x11064428, .name = "VT1718S",
3516 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003517 { .id = 0x11060441, .name = "VT2020",
3518 .patch = patch_vt1718S},
3519 { .id = 0x11064441, .name = "VT1828S",
3520 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003521 { .id = 0x11060433, .name = "VT1716S",
3522 .patch = patch_vt1716S},
3523 { .id = 0x1106a721, .name = "VT1716S",
3524 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003525 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3526 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003527 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003528 { .id = 0x11060440, .name = "VT1818S",
3529 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003530 { .id = 0x11060446, .name = "VT1802",
3531 .patch = patch_vt2002P},
3532 { .id = 0x11068446, .name = "VT1802",
3533 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003534 {} /* terminator */
3535};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003536
3537MODULE_ALIAS("snd-hda-codec-id:1106*");
3538
3539static struct hda_codec_preset_list via_list = {
3540 .preset = snd_hda_preset_via,
3541 .owner = THIS_MODULE,
3542};
3543
3544MODULE_LICENSE("GPL");
3545MODULE_DESCRIPTION("VIA HD-audio codec");
3546
3547static int __init patch_via_init(void)
3548{
3549 return snd_hda_add_codec_preset(&via_list);
3550}
3551
3552static void __exit patch_via_exit(void)
3553{
3554 snd_hda_delete_codec_preset(&via_list);
3555}
3556
3557module_init(patch_via_init)
3558module_exit(patch_via_exit)