blob: 997b7057a549ca208ca7e67069529527e358b1f9 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010052#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080053#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010054#include "hda_codec.h"
55#include "hda_local.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010056
Joseph Chanc577b8a2006-11-29 15:29:40 +010057/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080058#define VT1708_HP_PIN_NID 0x20
59#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010060
Harald Welted7426322008-09-15 22:43:23 +080061enum VIA_HDA_CODEC {
62 UNKNOWN = -1,
63 VT1708,
64 VT1709_10CH,
65 VT1709_6CH,
66 VT1708B_8CH,
67 VT1708B_4CH,
68 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080069 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080070 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080071 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080072 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080073 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080074 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080075 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080076 CODEC_TYPES,
77};
78
Lydia Wang118909562011-03-23 17:57:34 +080079#define VT2002P_COMPATIBLE(spec) \
80 ((spec)->codec_type == VT2002P ||\
81 (spec)->codec_type == VT1812 ||\
82 (spec)->codec_type == VT1802)
83
Takashi Iwai8e3679d2011-06-21 09:01:36 +020084#define MAX_NID_PATH_DEPTH 5
85
Takashi Iwai09a9ad62011-06-21 15:57:44 +020086/* output-path: DAC -> ... -> pin
87 * idx[] contains the source index number of the next widget;
88 * e.g. idx[0] is the index of the DAC selected by path[1] widget
89 * multi[] indicates whether it's a selector widget with multi-connectors
90 * (i.e. the connection selection is mandatory)
91 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
92 */
Takashi Iwai4a796162011-06-17 17:53:38 +020093struct nid_path {
94 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020095 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad62011-06-21 15:57:44 +020096 unsigned char idx[MAX_NID_PATH_DEPTH];
97 unsigned char multi[MAX_NID_PATH_DEPTH];
98 unsigned int vol_ctl;
99 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200100};
101
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200102/* input-path */
103struct via_input {
104 hda_nid_t pin; /* input-pin or aa-mix */
105 int adc_idx; /* ADC index to be used */
106 int mux_idx; /* MUX index (if any) */
107 const char *label; /* input-source label */
108};
109
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800110struct via_spec {
111 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200112 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800113 unsigned int num_mixers;
114
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200115 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800116 unsigned int num_iverbs;
117
Takashi Iwai82673bc2011-06-17 16:24:21 +0200118 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200119 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200120 const struct hda_pcm_stream *stream_analog_playback;
121 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800122
Takashi Iwai82673bc2011-06-17 16:24:21 +0200123 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200124 const struct hda_pcm_stream *stream_digital_playback;
125 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800126
127 /* playback */
128 struct hda_multi_out multiout;
129 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200130 hda_nid_t hp_dac_nid;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200131 int num_active_streams;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800132
Takashi Iwai4a796162011-06-17 17:53:38 +0200133 struct nid_path out_path[4];
134 struct nid_path hp_path;
135 struct nid_path hp_dep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200136 struct nid_path speaker_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200137
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800138 /* capture */
139 unsigned int num_adc_nids;
Takashi Iwaia766d0d2011-06-17 09:01:29 +0200140 hda_nid_t adc_nids[3];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800141 hda_nid_t mux_nids[3];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200142 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800143 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800144
145 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200146 bool dyn_adc_switch;
147 int num_inputs;
148 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800149 unsigned int cur_mux[3];
150
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200151 /* dynamic ADC switching */
152 hda_nid_t cur_adc;
153 unsigned int cur_adc_stream_tag;
154 unsigned int cur_adc_format;
155
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800156 /* PCM information */
157 struct hda_pcm pcm_rec[3];
158
159 /* dynamic controls, init_verbs and input_mux */
160 struct auto_pin_cfg autocfg;
161 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800162 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
163
164 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800165 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800166 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200167 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800168 enum VIA_HDA_CODEC codec_type;
169
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200170 /* smart51 setup */
171 unsigned int smart51_nums;
172 hda_nid_t smart51_pins[2];
173 int smart51_idxs[2];
174 const char *smart51_labels[2];
175 unsigned int smart51_enabled;
176
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800177 /* work to check hp jack state */
178 struct hda_codec *codec;
179 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200180 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800181 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800182
183 void (*set_widgets_power_state)(struct hda_codec *codec);
184
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800185 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200186 int num_loopbacks;
187 struct hda_amp_list loopback_list[8];
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200188
189 /* bind capture-volume */
190 struct hda_bind_ctls *bind_cap_vol;
191 struct hda_bind_ctls *bind_cap_sw;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800192};
193
Lydia Wang0341ccd2011-03-22 16:25:03 +0800194static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100195static struct via_spec * via_new_spec(struct hda_codec *codec)
196{
197 struct via_spec *spec;
198
199 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
200 if (spec == NULL)
201 return NULL;
202
203 codec->spec = spec;
204 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800205 spec->codec_type = get_codec_type(codec);
206 /* VT1708BCE & VT1708S are almost same */
207 if (spec->codec_type == VT1708BCE)
208 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100209 return spec;
210}
211
Lydia Wang744ff5f2009-10-10 19:07:26 +0800212static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800213{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800214 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800215 u16 ven_id = vendor_id >> 16;
216 u16 dev_id = vendor_id & 0xffff;
217 enum VIA_HDA_CODEC codec_type;
218
219 /* get codec type */
220 if (ven_id != 0x1106)
221 codec_type = UNKNOWN;
222 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
223 codec_type = VT1708;
224 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
225 codec_type = VT1709_10CH;
226 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
227 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800228 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800229 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800230 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
231 codec_type = VT1708BCE;
232 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800233 codec_type = VT1708B_4CH;
234 else if ((dev_id & 0xfff) == 0x397
235 && (dev_id >> 12) < 8)
236 codec_type = VT1708S;
237 else if ((dev_id & 0xfff) == 0x398
238 && (dev_id >> 12) < 8)
239 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800240 else if ((dev_id & 0xfff) == 0x428
241 && (dev_id >> 12) < 8)
242 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800243 else if (dev_id == 0x0433 || dev_id == 0xa721)
244 codec_type = VT1716S;
Lydia Wangbb3c6bf2009-10-10 19:08:39 +0800245 else if (dev_id == 0x0441 || dev_id == 0x4441)
246 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800247 else if (dev_id == 0x0438 || dev_id == 0x4438)
248 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800249 else if (dev_id == 0x0448)
250 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800251 else if (dev_id == 0x0440)
252 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800253 else if ((dev_id & 0xfff) == 0x446)
254 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800255 else
256 codec_type = UNKNOWN;
257 return codec_type;
258};
259
Lydia Wangec7e7e42011-03-24 12:43:44 +0800260#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800261#define VIA_HP_EVENT 0x01
262#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200263#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800264
Joseph Chanc577b8a2006-11-29 15:29:40 +0100265enum {
266 VIA_CTL_WIDGET_VOL,
267 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800268 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100269};
270
Takashi Iwaiada509e2011-06-20 15:40:19 +0200271static void analog_low_current_mode(struct hda_codec *codec);
272static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800273
274static void vt1708_start_hp_work(struct via_spec *spec)
275{
276 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
277 return;
278 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200279 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800280 if (!delayed_work_pending(&spec->vt1708_hp_work))
281 schedule_delayed_work(&spec->vt1708_hp_work,
282 msecs_to_jiffies(100));
283}
284
285static void vt1708_stop_hp_work(struct via_spec *spec)
286{
287 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
288 return;
289 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
290 && !is_aa_path_mute(spec->codec))
291 return;
292 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200293 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100294 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800295}
Lydia Wangf5271102009-10-10 19:07:35 +0800296
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800297static void set_widgets_power_state(struct hda_codec *codec)
298{
299 struct via_spec *spec = codec->spec;
300 if (spec->set_widgets_power_state)
301 spec->set_widgets_power_state(codec);
302}
Lydia Wang25eaba22009-10-10 19:08:43 +0800303
Lydia Wangf5271102009-10-10 19:07:35 +0800304static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
305 struct snd_ctl_elem_value *ucontrol)
306{
307 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
308 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
309
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800310 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200311 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800312 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
313 if (is_aa_path_mute(codec))
314 vt1708_start_hp_work(codec->spec);
315 else
316 vt1708_stop_hp_work(codec->spec);
317 }
Lydia Wangf5271102009-10-10 19:07:35 +0800318 return change;
319}
320
321/* modify .put = snd_hda_mixer_amp_switch_put */
322#define ANALOG_INPUT_MUTE \
323 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
324 .name = NULL, \
325 .index = 0, \
326 .info = snd_hda_mixer_amp_switch_info, \
327 .get = snd_hda_mixer_amp_switch_get, \
328 .put = analog_input_switch_put, \
329 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
330
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200331static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100332 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
333 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800334 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100335};
336
Lydia Wangab6734e2009-10-10 19:08:46 +0800337
Joseph Chanc577b8a2006-11-29 15:29:40 +0100338/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200339static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
340 const struct snd_kcontrol_new *tmpl,
341 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100342{
343 struct snd_kcontrol_new *knew;
344
Takashi Iwai603c4012008-07-30 15:01:44 +0200345 snd_array_init(&spec->kctls, sizeof(*knew), 32);
346 knew = snd_array_new(&spec->kctls);
347 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200348 return NULL;
349 *knew = *tmpl;
350 if (!name)
351 name = tmpl->name;
352 if (name) {
353 knew->name = kstrdup(name, GFP_KERNEL);
354 if (!knew->name)
355 return NULL;
356 }
357 return knew;
358}
359
360static int __via_add_control(struct via_spec *spec, int type, const char *name,
361 int idx, unsigned long val)
362{
363 struct snd_kcontrol_new *knew;
364
365 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
366 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100367 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200368 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100369 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100370 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100371 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100372 return 0;
373}
374
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200375#define via_add_control(spec, type, name, val) \
376 __via_add_control(spec, type, name, 0, val)
377
Takashi Iwai291c9e32011-06-17 16:15:26 +0200378#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100379
Takashi Iwai603c4012008-07-30 15:01:44 +0200380static void via_free_kctls(struct hda_codec *codec)
381{
382 struct via_spec *spec = codec->spec;
383
384 if (spec->kctls.list) {
385 struct snd_kcontrol_new *kctl = spec->kctls.list;
386 int i;
387 for (i = 0; i < spec->kctls.used; i++)
388 kfree(kctl[i].name);
389 }
390 snd_array_free(&spec->kctls);
391}
392
Joseph Chanc577b8a2006-11-29 15:29:40 +0100393/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800394static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200395 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100396{
397 char name[32];
398 int err;
399
400 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200401 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100402 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
403 if (err < 0)
404 return err;
405 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200406 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100407 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
408 if (err < 0)
409 return err;
410 return 0;
411}
412
Takashi Iwai5d417622011-06-20 11:32:27 +0200413/* return the index of the given widget nid as the source of mux;
414 * return -1 if not found;
415 * if num_conns is non-NULL, set the total number of connections
416 */
417static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux,
418 hda_nid_t nid, int *num_conns)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100419{
Takashi Iwai5d417622011-06-20 11:32:27 +0200420 hda_nid_t conn[HDA_MAX_NUM_INPUTS];
421 int i, nums;
422
423 nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
424 if (num_conns)
425 *num_conns = nums;
426 for (i = 0; i < nums; i++)
427 if (conn[i] == nid)
428 return i;
429 return -1;
430}
431
432#define get_connection_index(codec, mux, nid) \
433 __get_connection_index(codec, mux, nid, NULL)
434
Takashi Iwai8df2a312011-06-21 11:48:29 +0200435static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
436 unsigned int mask)
437{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200438 unsigned int caps;
439 if (!nid)
440 return false;
441 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200442 if (dir == HDA_INPUT)
443 caps &= AC_WCAP_IN_AMP;
444 else
445 caps &= AC_WCAP_OUT_AMP;
446 if (!caps)
447 return false;
448 if (query_amp_caps(codec, nid, dir) & mask)
449 return true;
450 return false;
451}
452
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200453#define have_mute(codec, nid, dir) \
454 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200455
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200456/* enable/disable the output-route */
457static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
458 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200459{
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200460 int i;
461 for (i = 0; i < path->depth; i++) {
462 hda_nid_t src, dst;
463 int idx = path->idx[i];
464 src = path->path[i];
465 if (i < path->depth - 1)
466 dst = path->path[i + 1];
467 else
468 dst = 0;
469 if (enable && path->multi[i])
470 snd_hda_codec_write(codec, dst, 0,
471 AC_VERB_SET_CONNECT_SEL, idx);
472 if (have_mute(codec, dst, HDA_INPUT)) {
473 int val = enable ? AMP_IN_UNMUTE(idx) :
474 AMP_IN_MUTE(idx);
475 snd_hda_codec_write(codec, dst, 0,
476 AC_VERB_SET_AMP_GAIN_MUTE, val);
477 }
478 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
479 continue;
480 if (have_mute(codec, src, HDA_OUTPUT)) {
481 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
482 snd_hda_codec_write(codec, src, 0,
483 AC_VERB_SET_AMP_GAIN_MUTE, val);
484 }
485 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200486}
487
488/* set the given pin as output */
489static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
490 int pin_type)
491{
492 if (!pin)
493 return;
494 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
495 pin_type);
496 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
497 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200498 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100499}
500
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200501static void via_auto_init_output(struct hda_codec *codec,
502 struct nid_path *path, int pin_type,
503 bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200504{
505 struct via_spec *spec = codec->spec;
506 unsigned int caps;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200507 hda_nid_t pin, nid;
508 int i, idx;
Takashi Iwai5d417622011-06-20 11:32:27 +0200509
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200510 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200511 return;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200512 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200513
514 init_output_pin(codec, pin, pin_type);
515 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
516 if (caps & AC_AMPCAP_MUTE) {
517 unsigned int val;
518 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
519 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
520 AMP_OUT_MUTE | val);
521 }
522
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200523 activate_output_path(codec, path, true, force);
524
525 /* initialize the AA-path */
526 if (!spec->aa_mix_nid)
527 return;
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200528 for (i = path->depth - 1; i > 0; i--) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200529 nid = path->path[i];
530 idx = get_connection_index(codec, nid, spec->aa_mix_nid);
531 if (idx >= 0) {
532 if (have_mute(codec, nid, HDA_INPUT))
533 snd_hda_codec_write(codec, nid, 0,
534 AC_VERB_SET_AMP_GAIN_MUTE,
535 AMP_IN_UNMUTE(idx));
536 break;
537 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200538 }
539}
540
Joseph Chanc577b8a2006-11-29 15:29:40 +0100541static void via_auto_init_multi_out(struct hda_codec *codec)
542{
543 struct via_spec *spec = codec->spec;
544 int i;
545
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200546 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200547 via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, true);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100548}
549
550static void via_auto_init_hp_out(struct hda_codec *codec)
551{
552 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100553
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200554 if (!spec->hp_dac_nid) {
555 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
556 return;
557 }
558 if (spec->hp_independent_mode) {
559 activate_output_path(codec, &spec->hp_dep_path, false, false);
560 via_auto_init_output(codec, &spec->hp_path, PIN_HP, true);
561 } else {
562 activate_output_path(codec, &spec->hp_path, false, false);
563 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
564 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100565}
566
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200567static void via_auto_init_speaker_out(struct hda_codec *codec)
568{
569 struct via_spec *spec = codec->spec;
570
571 if (spec->autocfg.speaker_outs)
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200572 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, true);
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200573}
574
Takashi Iwaif4a78282011-06-17 18:46:48 +0200575static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200576
Joseph Chanc577b8a2006-11-29 15:29:40 +0100577static void via_auto_init_analog_input(struct hda_codec *codec)
578{
579 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200580 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200581 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200582 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200583 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100584
Takashi Iwai096a8852011-06-20 12:09:02 +0200585 /* init ADCs */
586 for (i = 0; i < spec->num_adc_nids; i++) {
587 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
588 AC_VERB_SET_AMP_GAIN_MUTE,
589 AMP_IN_UNMUTE(0));
590 }
591
592 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200593 for (i = 0; i < cfg->num_inputs; i++) {
594 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200595 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200596 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100597 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200598 ctl = PIN_VREF50;
599 else
600 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100601 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200602 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100603 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200604
605 /* init input-src */
606 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200607 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
608 if (spec->mux_nids[adc_idx]) {
609 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
610 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
611 AC_VERB_SET_CONNECT_SEL,
612 mux_idx);
613 }
614 if (spec->dyn_adc_switch)
615 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200616 }
617
618 /* init aa-mixer */
619 if (!spec->aa_mix_nid)
620 return;
621 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
622 ARRAY_SIZE(conn));
623 for (i = 0; i < num_conns; i++) {
624 unsigned int caps = get_wcaps(codec, conn[i]);
625 if (get_wcaps_type(caps) == AC_WID_PIN)
626 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
627 AC_VERB_SET_AMP_GAIN_MUTE,
628 AMP_IN_MUTE(i));
629 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100630}
Lydia Wangf5271102009-10-10 19:07:35 +0800631
632static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
633 unsigned int *affected_parm)
634{
635 unsigned parm;
636 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
637 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
638 >> AC_DEFCFG_MISC_SHIFT
639 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800640 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200641 unsigned present = 0;
642
643 no_presence |= spec->no_pin_power_ctl;
644 if (!no_presence)
645 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200646 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800647 || ((no_presence || present)
648 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800649 *affected_parm = AC_PWRST_D0; /* if it's connected */
650 parm = AC_PWRST_D0;
651 } else
652 parm = AC_PWRST_D3;
653
654 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
655}
656
Takashi Iwai24088a52011-06-17 16:59:21 +0200657static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
658 struct snd_ctl_elem_info *uinfo)
659{
660 static const char * const texts[] = {
661 "Disabled", "Enabled"
662 };
663
664 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
665 uinfo->count = 1;
666 uinfo->value.enumerated.items = 2;
667 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
668 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
669 strcpy(uinfo->value.enumerated.name,
670 texts[uinfo->value.enumerated.item]);
671 return 0;
672}
673
674static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
675 struct snd_ctl_elem_value *ucontrol)
676{
677 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
678 struct via_spec *spec = codec->spec;
679 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
680 return 0;
681}
682
683static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
684 struct snd_ctl_elem_value *ucontrol)
685{
686 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
687 struct via_spec *spec = codec->spec;
688 unsigned int val = !ucontrol->value.enumerated.item[0];
689
690 if (val == spec->no_pin_power_ctl)
691 return 0;
692 spec->no_pin_power_ctl = val;
693 set_widgets_power_state(codec);
694 return 1;
695}
696
697static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
698 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
699 .name = "Dynamic Power-Control",
700 .info = via_pin_power_ctl_info,
701 .get = via_pin_power_ctl_get,
702 .put = via_pin_power_ctl_put,
703};
704
705
Harald Welte0aa62ae2008-09-09 15:58:27 +0800706static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
707 struct snd_ctl_elem_info *uinfo)
708{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200709 static const char * const texts[] = { "OFF", "ON" };
710
711 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
712 uinfo->count = 1;
713 uinfo->value.enumerated.items = 2;
714 if (uinfo->value.enumerated.item >= 2)
715 uinfo->value.enumerated.item = 1;
716 strcpy(uinfo->value.enumerated.name,
717 texts[uinfo->value.enumerated.item]);
718 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800719}
720
721static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
722 struct snd_ctl_elem_value *ucontrol)
723{
724 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800725 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800726
Takashi Iwaiece8d042011-06-19 16:24:21 +0200727 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800728 return 0;
729}
730
Harald Welte0aa62ae2008-09-09 15:58:27 +0800731static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
732 struct snd_ctl_elem_value *ucontrol)
733{
734 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
735 struct via_spec *spec = codec->spec;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200736
737 spec->hp_independent_mode = !!ucontrol->value.enumerated.item[0];
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200738 if (spec->hp_independent_mode) {
739 activate_output_path(codec, &spec->hp_dep_path, false, false);
740 activate_output_path(codec, &spec->hp_path, true, false);
741 } else {
742 activate_output_path(codec, &spec->hp_path, false, false);
743 activate_output_path(codec, &spec->hp_dep_path, true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200744 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800745
Lydia Wangce0e5a92011-03-22 16:22:37 +0800746 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800747 set_widgets_power_state(codec);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800748 return 0;
749}
750
Takashi Iwaiece8d042011-06-19 16:24:21 +0200751static const struct snd_kcontrol_new via_hp_mixer = {
752 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
753 .name = "Independent HP",
754 .info = via_independent_hp_info,
755 .get = via_independent_hp_get,
756 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800757};
758
Takashi Iwai3d83e572010-04-14 14:36:23 +0200759static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100760{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200761 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100762 struct snd_kcontrol_new *knew;
763 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100764
Takashi Iwaiece8d042011-06-19 16:24:21 +0200765 nid = spec->autocfg.hp_pins[0];
766 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200767 if (knew == NULL)
768 return -ENOMEM;
769
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100770 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100771
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100772 return 0;
773}
774
Lydia Wang1564b282009-10-10 19:07:52 +0800775static void notify_aa_path_ctls(struct hda_codec *codec)
776{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200777 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800778 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800779
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200780 for (i = 0; i < spec->smart51_nums; i++) {
781 struct snd_kcontrol *ctl;
782 struct snd_ctl_elem_id id;
783 memset(&id, 0, sizeof(id));
784 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
785 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800786 ctl = snd_hda_find_mixer_ctl(codec, id.name);
787 if (ctl)
788 snd_ctl_notify(codec->bus->card,
789 SNDRV_CTL_EVENT_MASK_VALUE,
790 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800791 }
792}
793
794static void mute_aa_path(struct hda_codec *codec, int mute)
795{
796 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200797 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800798 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200799
Lydia Wang1564b282009-10-10 19:07:52 +0800800 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200801 for (i = 0; i < spec->smart51_nums; i++) {
802 if (spec->smart51_idxs[i] < 0)
803 continue;
804 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
805 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800806 HDA_AMP_MUTE, val);
807 }
808}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200809
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200810static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
811{
812 struct via_spec *spec = codec->spec;
813 int i;
814
815 for (i = 0; i < spec->smart51_nums; i++)
816 if (spec->smart51_pins[i] == pin)
817 return true;
818 return false;
819}
820
Lydia Wang1564b282009-10-10 19:07:52 +0800821static int via_smart51_get(struct snd_kcontrol *kcontrol,
822 struct snd_ctl_elem_value *ucontrol)
823{
824 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
825 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800826
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200827 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800828 return 0;
829}
830
831static int via_smart51_put(struct snd_kcontrol *kcontrol,
832 struct snd_ctl_elem_value *ucontrol)
833{
834 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
835 struct via_spec *spec = codec->spec;
836 int out_in = *ucontrol->value.integer.value
837 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800838 int i;
839
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200840 for (i = 0; i < spec->smart51_nums; i++) {
841 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200842 unsigned int parm;
843
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200844 parm = snd_hda_codec_read(codec, nid, 0,
845 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
846 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
847 parm |= out_in;
848 snd_hda_codec_write(codec, nid, 0,
849 AC_VERB_SET_PIN_WIDGET_CONTROL,
850 parm);
851 if (out_in == AC_PINCTL_OUT_EN) {
852 mute_aa_path(codec, 1);
853 notify_aa_path_ctls(codec);
854 }
Lydia Wang1564b282009-10-10 19:07:52 +0800855 }
856 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800857 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800858 return 1;
859}
860
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200861static const struct snd_kcontrol_new via_smart51_mixer = {
862 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
863 .name = "Smart 5.1",
864 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200865 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200866 .get = via_smart51_get,
867 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800868};
869
Takashi Iwaif4a78282011-06-17 18:46:48 +0200870static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100871{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200872 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100873
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200874 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800875 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200876 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100877 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100878 return 0;
879}
880
Takashi Iwaiada509e2011-06-20 15:40:19 +0200881/* check AA path's mute status */
882static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800883{
Lydia Wangf5271102009-10-10 19:07:35 +0800884 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200885 const struct hda_amp_list *p;
886 int i, ch, v;
887
888 for (i = 0; i < spec->num_loopbacks; i++) {
889 p = &spec->loopback_list[i];
890 for (ch = 0; ch < 2; ch++) {
891 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
892 p->idx);
893 if (!(v & HDA_AMP_MUTE) && v > 0)
894 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800895 }
896 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200897 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800898}
899
900/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200901static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800902{
903 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200904 bool enable;
905 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800906
Takashi Iwaiada509e2011-06-20 15:40:19 +0200907 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800908
909 /* decide low current mode's verb & parameter */
910 switch (spec->codec_type) {
911 case VT1708B_8CH:
912 case VT1708B_4CH:
913 verb = 0xf70;
914 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
915 break;
916 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800917 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800918 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800919 verb = 0xf73;
920 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
921 break;
922 case VT1702:
923 verb = 0xf73;
924 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
925 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800926 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800927 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800928 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800929 verb = 0xf93;
930 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
931 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800932 default:
933 return; /* other codecs are not supported */
934 }
935 /* send verb */
936 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
937}
938
Joseph Chanc577b8a2006-11-29 15:29:40 +0100939/*
940 * generic initialization of ADC, input mixers and output mixers
941 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200942static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800943 /* power down jack detect function */
944 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +0100945 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100946};
947
Takashi Iwaiada509e2011-06-20 15:40:19 +0200948static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200949{
Takashi Iwaiada509e2011-06-20 15:40:19 +0200950 struct via_spec *spec = codec->spec;
951
952 if (active)
953 spec->num_active_streams++;
954 else
955 spec->num_active_streams--;
956 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200957}
958
Takashi Iwaiece8d042011-06-19 16:24:21 +0200959static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100960 struct hda_codec *codec,
961 struct snd_pcm_substream *substream)
962{
963 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200964 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +0200965
966 if (!spec->hp_independent_mode)
967 spec->multiout.hp_nid = spec->hp_dac_nid;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200968 set_stream_active(codec, true);
969 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
970 hinfo);
971 if (err < 0) {
972 spec->multiout.hp_nid = 0;
973 set_stream_active(codec, false);
974 return err;
975 }
976 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100977}
978
Takashi Iwaiece8d042011-06-19 16:24:21 +0200979static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +0200980 struct hda_codec *codec,
981 struct snd_pcm_substream *substream)
982{
Takashi Iwaiece8d042011-06-19 16:24:21 +0200983 struct via_spec *spec = codec->spec;
984
985 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200986 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +0200987 return 0;
988}
989
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200990static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
991 struct hda_codec *codec,
992 struct snd_pcm_substream *substream)
993{
994 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200995
Takashi Iwaiece8d042011-06-19 16:24:21 +0200996 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200997 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +0200998 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
999 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001000 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001001 return 0;
1002}
1003
1004static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1005 struct hda_codec *codec,
1006 struct snd_pcm_substream *substream)
1007{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001008 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001009 return 0;
1010}
1011
1012static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1013 struct hda_codec *codec,
1014 unsigned int stream_tag,
1015 unsigned int format,
1016 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001017{
1018 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001019
Takashi Iwaiece8d042011-06-19 16:24:21 +02001020 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1021 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001022 vt1708_start_hp_work(spec);
1023 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001024}
1025
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001026static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1027 struct hda_codec *codec,
1028 unsigned int stream_tag,
1029 unsigned int format,
1030 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001031{
1032 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001033
Takashi Iwaiece8d042011-06-19 16:24:21 +02001034 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1035 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001036 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001037 return 0;
1038}
1039
1040static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1041 struct hda_codec *codec,
1042 struct snd_pcm_substream *substream)
1043{
1044 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001045
Takashi Iwaiece8d042011-06-19 16:24:21 +02001046 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001047 vt1708_stop_hp_work(spec);
1048 return 0;
1049}
1050
1051static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1052 struct hda_codec *codec,
1053 struct snd_pcm_substream *substream)
1054{
1055 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001056
Takashi Iwaiece8d042011-06-19 16:24:21 +02001057 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001058 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001059 return 0;
1060}
1061
Joseph Chanc577b8a2006-11-29 15:29:40 +01001062/*
1063 * Digital out
1064 */
1065static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1066 struct hda_codec *codec,
1067 struct snd_pcm_substream *substream)
1068{
1069 struct via_spec *spec = codec->spec;
1070 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1071}
1072
1073static int via_dig_playback_pcm_close(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 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1079}
1080
Harald Welte5691ec72008-09-15 22:42:26 +08001081static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001082 struct hda_codec *codec,
1083 unsigned int stream_tag,
1084 unsigned int format,
1085 struct snd_pcm_substream *substream)
1086{
1087 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001088 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1089 stream_tag, format, substream);
1090}
Harald Welte5691ec72008-09-15 22:42:26 +08001091
Takashi Iwai9da29272009-05-07 16:31:14 +02001092static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1093 struct hda_codec *codec,
1094 struct snd_pcm_substream *substream)
1095{
1096 struct via_spec *spec = codec->spec;
1097 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001098 return 0;
1099}
1100
Joseph Chanc577b8a2006-11-29 15:29:40 +01001101/*
1102 * Analog capture
1103 */
1104static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1105 struct hda_codec *codec,
1106 unsigned int stream_tag,
1107 unsigned int format,
1108 struct snd_pcm_substream *substream)
1109{
1110 struct via_spec *spec = codec->spec;
1111
1112 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1113 stream_tag, 0, format);
1114 return 0;
1115}
1116
1117static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1118 struct hda_codec *codec,
1119 struct snd_pcm_substream *substream)
1120{
1121 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001122 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001123 return 0;
1124}
1125
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001126/* analog capture with dynamic ADC switching */
1127static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1128 struct hda_codec *codec,
1129 unsigned int stream_tag,
1130 unsigned int format,
1131 struct snd_pcm_substream *substream)
1132{
1133 struct via_spec *spec = codec->spec;
1134 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1135
1136 spec->cur_adc = spec->adc_nids[adc_idx];
1137 spec->cur_adc_stream_tag = stream_tag;
1138 spec->cur_adc_format = format;
1139 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
1140 return 0;
1141}
1142
1143static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1144 struct hda_codec *codec,
1145 struct snd_pcm_substream *substream)
1146{
1147 struct via_spec *spec = codec->spec;
1148
1149 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1150 spec->cur_adc = 0;
1151 return 0;
1152}
1153
1154/* re-setup the stream if running; called from input-src put */
1155static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1156{
1157 struct via_spec *spec = codec->spec;
1158 int adc_idx = spec->inputs[cur].adc_idx;
1159 hda_nid_t adc = spec->adc_nids[adc_idx];
1160
1161 if (spec->cur_adc && spec->cur_adc != adc) {
1162 /* stream is running, let's swap the current ADC */
1163 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1164 spec->cur_adc = adc;
1165 snd_hda_codec_setup_stream(codec, adc,
1166 spec->cur_adc_stream_tag, 0,
1167 spec->cur_adc_format);
1168 return true;
1169 }
1170 return false;
1171}
1172
Takashi Iwai9af74212011-06-18 16:17:45 +02001173static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001174 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001175 .channels_min = 2,
1176 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001177 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001178 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001179 .open = via_playback_multi_pcm_open,
1180 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001181 .prepare = via_playback_multi_pcm_prepare,
1182 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001183 },
1184};
1185
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001186static const struct hda_pcm_stream via_pcm_hp_playback = {
1187 .substreams = 1,
1188 .channels_min = 2,
1189 .channels_max = 2,
1190 /* NID is set in via_build_pcms */
1191 .ops = {
1192 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001193 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001194 .prepare = via_playback_hp_pcm_prepare,
1195 .cleanup = via_playback_hp_pcm_cleanup
1196 },
1197};
1198
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001199static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001200 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001201 .channels_min = 2,
1202 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001203 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001204 /* We got noisy outputs on the right channel on VT1708 when
1205 * 24bit samples are used. Until any workaround is found,
1206 * disable the 24bit format, so far.
1207 */
1208 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1209 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001210 .open = via_playback_multi_pcm_open,
1211 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001212 .prepare = via_playback_multi_pcm_prepare,
1213 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001214 },
1215};
1216
Takashi Iwai9af74212011-06-18 16:17:45 +02001217static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001218 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001219 .channels_min = 2,
1220 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001221 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001222 .ops = {
1223 .prepare = via_capture_pcm_prepare,
1224 .cleanup = via_capture_pcm_cleanup
1225 },
1226};
1227
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001228static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1229 .substreams = 1,
1230 .channels_min = 2,
1231 .channels_max = 2,
1232 /* NID is set in via_build_pcms */
1233 .ops = {
1234 .prepare = via_dyn_adc_capture_pcm_prepare,
1235 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1236 },
1237};
1238
Takashi Iwai9af74212011-06-18 16:17:45 +02001239static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001240 .substreams = 1,
1241 .channels_min = 2,
1242 .channels_max = 2,
1243 /* NID is set in via_build_pcms */
1244 .ops = {
1245 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001246 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001247 .prepare = via_dig_playback_pcm_prepare,
1248 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001249 },
1250};
1251
Takashi Iwai9af74212011-06-18 16:17:45 +02001252static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001253 .substreams = 1,
1254 .channels_min = 2,
1255 .channels_max = 2,
1256};
1257
Takashi Iwai370bafb2011-06-20 12:47:45 +02001258/*
1259 * slave controls for virtual master
1260 */
1261static const char * const via_slave_vols[] = {
1262 "Front Playback Volume",
1263 "Surround Playback Volume",
1264 "Center Playback Volume",
1265 "LFE Playback Volume",
1266 "Side Playback Volume",
1267 "Headphone Playback Volume",
1268 "Speaker Playback Volume",
1269 NULL,
1270};
1271
1272static const char * const via_slave_sws[] = {
1273 "Front Playback Switch",
1274 "Surround Playback Switch",
1275 "Center Playback Switch",
1276 "LFE Playback Switch",
1277 "Side Playback Switch",
1278 "Headphone Playback Switch",
1279 "Speaker Playback Switch",
1280 NULL,
1281};
1282
Joseph Chanc577b8a2006-11-29 15:29:40 +01001283static int via_build_controls(struct hda_codec *codec)
1284{
1285 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001286 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001287 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001288
Takashi Iwai24088a52011-06-17 16:59:21 +02001289 if (spec->set_widgets_power_state)
1290 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1291 return -ENOMEM;
1292
Joseph Chanc577b8a2006-11-29 15:29:40 +01001293 for (i = 0; i < spec->num_mixers; i++) {
1294 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1295 if (err < 0)
1296 return err;
1297 }
1298
1299 if (spec->multiout.dig_out_nid) {
1300 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001301 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001302 spec->multiout.dig_out_nid);
1303 if (err < 0)
1304 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001305 err = snd_hda_create_spdif_share_sw(codec,
1306 &spec->multiout);
1307 if (err < 0)
1308 return err;
1309 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001310 }
1311 if (spec->dig_in_nid) {
1312 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1313 if (err < 0)
1314 return err;
1315 }
Lydia Wang17314372009-10-10 19:07:37 +08001316
Takashi Iwai370bafb2011-06-20 12:47:45 +02001317 /* if we have no master control, let's create it */
1318 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1319 unsigned int vmaster_tlv[4];
1320 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1321 HDA_OUTPUT, vmaster_tlv);
1322 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1323 vmaster_tlv, via_slave_vols);
1324 if (err < 0)
1325 return err;
1326 }
1327 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1328 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1329 NULL, via_slave_sws);
1330 if (err < 0)
1331 return err;
1332 }
1333
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001334 /* assign Capture Source enums to NID */
1335 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1336 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001337 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001338 if (err < 0)
1339 return err;
1340 }
1341
Lydia Wang17314372009-10-10 19:07:37 +08001342 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001343 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001344 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001345
Takashi Iwai603c4012008-07-30 15:01:44 +02001346 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001347 return 0;
1348}
1349
1350static int via_build_pcms(struct hda_codec *codec)
1351{
1352 struct via_spec *spec = codec->spec;
1353 struct hda_pcm *info = spec->pcm_rec;
1354
1355 codec->num_pcms = 1;
1356 codec->pcm_info = info;
1357
Takashi Iwai82673bc2011-06-17 16:24:21 +02001358 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1359 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001360 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001361
1362 if (!spec->stream_analog_playback)
1363 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001364 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001365 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001366 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1367 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001368 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1369 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001370
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001371 if (!spec->stream_analog_capture) {
1372 if (spec->dyn_adc_switch)
1373 spec->stream_analog_capture =
1374 &via_pcm_dyn_adc_analog_capture;
1375 else
1376 spec->stream_analog_capture = &via_pcm_analog_capture;
1377 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001378 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1379 *spec->stream_analog_capture;
1380 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001381 if (!spec->dyn_adc_switch)
1382 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1383 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001384
1385 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1386 codec->num_pcms++;
1387 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001388 snprintf(spec->stream_name_digital,
1389 sizeof(spec->stream_name_digital),
1390 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001391 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001392 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001393 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001394 if (!spec->stream_digital_playback)
1395 spec->stream_digital_playback =
1396 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001397 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001398 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001399 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1400 spec->multiout.dig_out_nid;
1401 }
1402 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001403 if (!spec->stream_digital_capture)
1404 spec->stream_digital_capture =
1405 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001406 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001407 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001408 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1409 spec->dig_in_nid;
1410 }
1411 }
1412
Takashi Iwaiece8d042011-06-19 16:24:21 +02001413 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001414 codec->num_pcms++;
1415 info++;
1416 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1417 "%s HP", codec->chip_name);
1418 info->name = spec->stream_name_hp;
1419 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1420 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001421 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001422 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001423 return 0;
1424}
1425
1426static void via_free(struct hda_codec *codec)
1427{
1428 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001429
1430 if (!spec)
1431 return;
1432
Takashi Iwai603c4012008-07-30 15:01:44 +02001433 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001434 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001435 kfree(spec->bind_cap_vol);
1436 kfree(spec->bind_cap_sw);
1437 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001438}
1439
Takashi Iwai64be2852011-06-17 16:51:39 +02001440/* mute/unmute outputs */
1441static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1442 hda_nid_t *pins, bool mute)
1443{
1444 int i;
1445 for (i = 0; i < num_pins; i++)
1446 snd_hda_codec_write(codec, pins[i], 0,
1447 AC_VERB_SET_PIN_WIDGET_CONTROL,
1448 mute ? 0 : PIN_OUT);
1449}
1450
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001451/* mute internal speaker if line-out is plugged */
1452static void via_line_automute(struct hda_codec *codec, int present)
1453{
1454 struct via_spec *spec = codec->spec;
1455
1456 if (!spec->autocfg.speaker_outs)
1457 return;
1458 if (!present)
1459 present = snd_hda_jack_detect(codec,
1460 spec->autocfg.line_out_pins[0]);
1461 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1462 spec->autocfg.speaker_pins,
1463 present);
1464}
1465
Harald Welte69e52a82008-09-09 15:57:32 +08001466/* mute internal speaker if HP is plugged */
1467static void via_hp_automute(struct hda_codec *codec)
1468{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001469 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001470 struct via_spec *spec = codec->spec;
1471
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001472 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001473 int nums;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001474 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001475 if (spec->smart51_enabled)
1476 nums = spec->autocfg.line_outs + spec->smart51_nums;
1477 else
1478 nums = spec->autocfg.line_outs;
1479 toggle_output_mutes(codec, nums,
Takashi Iwai64be2852011-06-17 16:51:39 +02001480 spec->autocfg.line_out_pins,
1481 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001482 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001483 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001484}
1485
Harald Welte69e52a82008-09-09 15:57:32 +08001486static void via_gpio_control(struct hda_codec *codec)
1487{
1488 unsigned int gpio_data;
1489 unsigned int vol_counter;
1490 unsigned int vol;
1491 unsigned int master_vol;
1492
1493 struct via_spec *spec = codec->spec;
1494
1495 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1496 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1497
1498 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1499 0xF84, 0) & 0x3F0000) >> 16;
1500
1501 vol = vol_counter & 0x1F;
1502 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1503 AC_VERB_GET_AMP_GAIN_MUTE,
1504 AC_AMP_GET_INPUT);
1505
1506 if (gpio_data == 0x02) {
1507 /* unmute 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 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001511 if (vol_counter & 0x20) {
1512 /* decrease volume */
1513 if (vol > master_vol)
1514 vol = master_vol;
1515 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1516 0, HDA_AMP_VOLMASK,
1517 master_vol-vol);
1518 } else {
1519 /* increase volume */
1520 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1521 HDA_AMP_VOLMASK,
1522 ((master_vol+vol) > 0x2A) ? 0x2A :
1523 (master_vol+vol));
1524 }
1525 } else if (!(gpio_data & 0x02)) {
1526 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001527 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1528 AC_VERB_SET_PIN_WIDGET_CONTROL,
1529 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001530 }
1531}
1532
1533/* unsolicited event for jack sensing */
1534static void via_unsol_event(struct hda_codec *codec,
1535 unsigned int res)
1536{
1537 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001538
Lydia Wanga34df192009-10-10 19:08:01 +08001539 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001540 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001541
1542 res &= ~VIA_JACK_EVENT;
1543
1544 if (res == VIA_HP_EVENT)
1545 via_hp_automute(codec);
1546 else if (res == VIA_GPIO_EVENT)
1547 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001548 else if (res == VIA_LINE_EVENT)
1549 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001550}
1551
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001552#ifdef SND_HDA_NEEDS_RESUME
1553static int via_suspend(struct hda_codec *codec, pm_message_t state)
1554{
1555 struct via_spec *spec = codec->spec;
1556 vt1708_stop_hp_work(spec);
1557 return 0;
1558}
1559#endif
1560
Takashi Iwaicb53c622007-08-10 17:21:45 +02001561#ifdef CONFIG_SND_HDA_POWER_SAVE
1562static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1563{
1564 struct via_spec *spec = codec->spec;
1565 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1566}
1567#endif
1568
Joseph Chanc577b8a2006-11-29 15:29:40 +01001569/*
1570 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001571
1572static int via_init(struct hda_codec *codec);
1573
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001574static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001575 .build_controls = via_build_controls,
1576 .build_pcms = via_build_pcms,
1577 .init = via_init,
1578 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001579 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001580#ifdef SND_HDA_NEEDS_RESUME
1581 .suspend = via_suspend,
1582#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001583#ifdef CONFIG_SND_HDA_POWER_SAVE
1584 .check_power_status = via_check_power_status,
1585#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001586};
1587
Takashi Iwai4a796162011-06-17 17:53:38 +02001588static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001589{
Takashi Iwai4a796162011-06-17 17:53:38 +02001590 struct via_spec *spec = codec->spec;
1591 int i;
1592
1593 for (i = 0; i < spec->multiout.num_dacs; i++) {
1594 if (spec->multiout.dac_nids[i] == dac)
1595 return false;
1596 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001597 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001598 return false;
1599 return true;
1600}
1601
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001602static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001603 hda_nid_t target_dac, struct nid_path *path,
1604 int depth, int wid_type)
1605{
1606 hda_nid_t conn[8];
1607 int i, nums;
1608
1609 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1610 for (i = 0; i < nums; i++) {
1611 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1612 continue;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001613 if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
1614 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001615 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001616 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001617 return false;
1618 for (i = 0; i < nums; i++) {
1619 unsigned int type;
1620 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1621 if (type == AC_WID_AUD_OUT ||
1622 (wid_type != -1 && type != wid_type))
1623 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001624 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001625 path, depth + 1, AC_WID_AUD_SEL))
1626 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001627 }
1628 return false;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001629
1630 found:
1631 path->path[path->depth] = conn[i];
1632 path->idx[path->depth] = i;
1633 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1634 path->multi[path->depth] = 1;
1635 path->depth++;
1636 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001637}
1638
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001639static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1640 hda_nid_t target_dac, struct nid_path *path)
1641{
1642 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1643 path->path[path->depth] = nid;
1644 path->depth++;
1645 return true;
1646 }
1647 return false;
1648}
1649
Takashi Iwai4a796162011-06-17 17:53:38 +02001650static int via_auto_fill_dac_nids(struct hda_codec *codec)
1651{
1652 struct via_spec *spec = codec->spec;
1653 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001654 int i;
1655 hda_nid_t nid;
1656
Joseph Chanc577b8a2006-11-29 15:29:40 +01001657 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001658 spec->multiout.num_dacs = cfg->line_outs;
1659 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001660 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001661 if (!nid)
1662 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001663 if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
1664 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001665 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001666 return 0;
1667}
1668
Takashi Iwai4a796162011-06-17 17:53:38 +02001669static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001670 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001671{
Takashi Iwai4a796162011-06-17 17:53:38 +02001672 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001673 char name[32];
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001674 hda_nid_t dac, pin, sel, nid;
1675 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001676
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001677 dac = check_dac ? path->path[0] : 0;
1678 pin = path->path[path->depth - 1];
1679 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001680
Takashi Iwai8df2a312011-06-21 11:48:29 +02001681 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001682 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001683 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001684 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001685 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1686 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001687 else
1688 nid = 0;
1689 if (nid) {
1690 sprintf(name, "%s Playback Volume", pfx);
1691 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001692 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001693 if (err < 0)
1694 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001695 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001696 }
1697
Takashi Iwai8df2a312011-06-21 11:48:29 +02001698 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001699 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001700 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001701 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001702 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1703 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001704 else
1705 nid = 0;
1706 if (nid) {
1707 sprintf(name, "%s Playback Switch", pfx);
1708 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1709 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1710 if (err < 0)
1711 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001712 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001713 }
1714 return 0;
1715}
1716
Takashi Iwaif4a78282011-06-17 18:46:48 +02001717static void mangle_smart51(struct hda_codec *codec)
1718{
1719 struct via_spec *spec = codec->spec;
1720 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001721 struct auto_pin_cfg_item *ins = cfg->inputs;
1722 int i, j, nums, attr;
1723 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001724
Takashi Iwai0f98c242011-06-21 12:51:33 +02001725 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1726 nums = 0;
1727 for (i = 0; i < cfg->num_inputs; i++) {
1728 unsigned int def;
1729 if (ins[i].type > AUTO_PIN_LINE_IN)
1730 continue;
1731 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1732 if (snd_hda_get_input_pin_attr(def) != attr)
1733 continue;
1734 for (j = 0; j < nums; j++)
1735 if (ins[pins[j]].type < ins[i].type) {
1736 memmove(pins + j + 1, pins + j,
1737 (nums - j - 1) * sizeof(int));
1738 break;
1739 }
1740 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001741 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001742 }
1743 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001744 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001745 for (i = 0; i < nums; i++) {
1746 hda_nid_t pin = ins[pins[i]].pin;
1747 spec->smart51_pins[spec->smart51_nums++] = pin;
1748 cfg->line_out_pins[cfg->line_outs++] = pin;
1749 if (cfg->line_outs == 3)
1750 break;
1751 }
1752 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001753 }
1754}
1755
Takashi Iwai4a796162011-06-17 17:53:38 +02001756/* add playback controls from the parsed DAC table */
1757static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1758{
1759 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001760 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001761 static const char * const chname[4] = {
1762 "Front", "Surround", "C/LFE", "Side"
1763 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001764 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001765 int old_line_outs;
1766
1767 /* check smart51 */
1768 old_line_outs = cfg->line_outs;
1769 if (cfg->line_outs == 1)
1770 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001771
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001772 err = via_auto_fill_dac_nids(codec);
1773 if (err < 0)
1774 return err;
1775
Takashi Iwai4a796162011-06-17 17:53:38 +02001776 for (i = 0; i < cfg->line_outs; i++) {
1777 hda_nid_t pin, dac;
1778 pin = cfg->line_out_pins[i];
1779 dac = spec->multiout.dac_nids[i];
1780 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001781 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001782 if (i == HDA_CLFE) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001783 err = create_ch_ctls(codec, "Center", 1, true,
1784 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001785 if (err < 0)
1786 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001787 err = create_ch_ctls(codec, "LFE", 2, true,
1788 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001789 if (err < 0)
1790 return err;
1791 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001792 const char *pfx = chname[i];
1793 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1794 cfg->line_outs == 1)
1795 pfx = "Speaker";
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001796 err = create_ch_ctls(codec, pfx, 3, true,
1797 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001798 if (err < 0)
1799 return err;
1800 }
1801 }
1802
Takashi Iwai4a796162011-06-17 17:53:38 +02001803 idx = get_connection_index(codec, spec->aa_mix_nid,
1804 spec->multiout.dac_nids[0]);
1805 if (idx >= 0) {
1806 /* add control to mixer */
1807 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1808 "PCM Playback Volume",
1809 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1810 idx, HDA_INPUT));
1811 if (err < 0)
1812 return err;
1813 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1814 "PCM Playback Switch",
1815 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1816 idx, HDA_INPUT));
1817 if (err < 0)
1818 return err;
1819 }
1820
Takashi Iwaif4a78282011-06-17 18:46:48 +02001821 cfg->line_outs = old_line_outs;
1822
Joseph Chanc577b8a2006-11-29 15:29:40 +01001823 return 0;
1824}
1825
Takashi Iwai4a796162011-06-17 17:53:38 +02001826static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001827{
Takashi Iwai4a796162011-06-17 17:53:38 +02001828 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001829 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001830 int err;
1831
1832 if (!pin)
1833 return 0;
1834
Takashi Iwai8df2a312011-06-21 11:48:29 +02001835 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001836 spec->hp_dac_nid = spec->hp_path.path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001837
Takashi Iwaiece8d042011-06-19 16:24:21 +02001838 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001839 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001840 !spec->hp_dac_nid)
1841 return 0;
1842
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001843 if (spec->hp_dac_nid)
1844 path = &spec->hp_path;
1845 else
1846 path = &spec->hp_dep_path;
1847 err = create_ch_ctls(codec, "Headphone", 3, false, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001848 if (err < 0)
1849 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001850 if (spec->hp_dac_nid) {
1851 spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
1852 spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
1853 }
Harald Welte0aa62ae2008-09-09 15:58:27 +08001854
Joseph Chanc577b8a2006-11-29 15:29:40 +01001855 return 0;
1856}
1857
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001858static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1859{
1860 struct via_spec *spec = codec->spec;
1861 hda_nid_t pin, dac;
1862
1863 pin = spec->autocfg.speaker_pins[0];
1864 if (!spec->autocfg.speaker_outs || !pin)
1865 return 0;
1866
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001867 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1868 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001869 spec->multiout.extra_out_nid[0] = dac;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001870 return create_ch_ctls(codec, "Speaker", 3, true,
1871 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001872 }
1873 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001874 &spec->speaker_path))
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001875 return create_ch_ctls(codec, "Speaker", 3, false,
1876 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001877
1878 return 0;
1879}
1880
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001881/* look for ADCs */
1882static int via_fill_adcs(struct hda_codec *codec)
1883{
1884 struct via_spec *spec = codec->spec;
1885 hda_nid_t nid = codec->start_nid;
1886 int i;
1887
1888 for (i = 0; i < codec->num_nodes; i++, nid++) {
1889 unsigned int wcaps = get_wcaps(codec, nid);
1890 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1891 continue;
1892 if (wcaps & AC_WCAP_DIGITAL)
1893 continue;
1894 if (!(wcaps & AC_WCAP_CONN_LIST))
1895 continue;
1896 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1897 return -ENOMEM;
1898 spec->adc_nids[spec->num_adc_nids++] = nid;
1899 }
1900 return 0;
1901}
1902
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001903/* input-src control */
1904static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
1905 struct snd_ctl_elem_info *uinfo)
1906{
1907 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1908 struct via_spec *spec = codec->spec;
1909
1910 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1911 uinfo->count = 1;
1912 uinfo->value.enumerated.items = spec->num_inputs;
1913 if (uinfo->value.enumerated.item >= spec->num_inputs)
1914 uinfo->value.enumerated.item = spec->num_inputs - 1;
1915 strcpy(uinfo->value.enumerated.name,
1916 spec->inputs[uinfo->value.enumerated.item].label);
1917 return 0;
1918}
1919
1920static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
1921 struct snd_ctl_elem_value *ucontrol)
1922{
1923 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1924 struct via_spec *spec = codec->spec;
1925 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1926
1927 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
1928 return 0;
1929}
1930
1931static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
1932 struct snd_ctl_elem_value *ucontrol)
1933{
1934 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1935 struct via_spec *spec = codec->spec;
1936 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1937 hda_nid_t mux;
1938 int cur;
1939
1940 cur = ucontrol->value.enumerated.item[0];
1941 if (cur < 0 || cur >= spec->num_inputs)
1942 return -EINVAL;
1943 if (spec->cur_mux[idx] == cur)
1944 return 0;
1945 spec->cur_mux[idx] = cur;
1946 if (spec->dyn_adc_switch) {
1947 int adc_idx = spec->inputs[cur].adc_idx;
1948 mux = spec->mux_nids[adc_idx];
1949 via_dyn_adc_pcm_resetup(codec, cur);
1950 } else {
1951 mux = spec->mux_nids[idx];
1952 if (snd_BUG_ON(!mux))
1953 return -EINVAL;
1954 }
1955
1956 if (mux) {
1957 /* switch to D0 beofre change index */
1958 if (snd_hda_codec_read(codec, mux, 0,
1959 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
1960 snd_hda_codec_write(codec, mux, 0,
1961 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
1962 snd_hda_codec_write(codec, mux, 0,
1963 AC_VERB_SET_CONNECT_SEL,
1964 spec->inputs[cur].mux_idx);
1965 }
1966
1967 /* update jack power state */
1968 set_widgets_power_state(codec);
1969 return 0;
1970}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001971
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001972static const struct snd_kcontrol_new via_input_src_ctl = {
1973 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1974 /* The multiple "Capture Source" controls confuse alsamixer
1975 * So call somewhat different..
1976 */
1977 /* .name = "Capture Source", */
1978 .name = "Input Source",
1979 .info = via_mux_enum_info,
1980 .get = via_mux_enum_get,
1981 .put = via_mux_enum_put,
1982};
1983
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001984static int create_input_src_ctls(struct hda_codec *codec, int count)
1985{
1986 struct via_spec *spec = codec->spec;
1987 struct snd_kcontrol_new *knew;
1988
1989 if (spec->num_inputs <= 1 || !count)
1990 return 0; /* no need for single src */
1991
1992 knew = via_clone_control(spec, &via_input_src_ctl);
1993 if (!knew)
1994 return -ENOMEM;
1995 knew->count = count;
1996 return 0;
1997}
1998
1999/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002000static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2001{
2002 struct hda_amp_list *list;
2003
2004 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2005 return;
2006 list = spec->loopback_list + spec->num_loopbacks;
2007 list->nid = mix;
2008 list->dir = HDA_INPUT;
2009 list->idx = idx;
2010 spec->num_loopbacks++;
2011 spec->loopback.amplist = spec->loopback_list;
2012}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002013
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002014/* check whether the path from src to dst is reachable */
2015static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
2016 hda_nid_t dst, int depth)
2017{
2018 hda_nid_t conn[8];
2019 int i, nums;
2020
2021 nums = snd_hda_get_connections(codec, src, conn, ARRAY_SIZE(conn));
2022 for (i = 0; i < nums; i++)
2023 if (conn[i] == dst)
2024 return true;
2025 if (++depth > MAX_NID_PATH_DEPTH)
2026 return false;
2027 for (i = 0; i < nums; i++)
2028 if (is_reachable_nid(codec, conn[i], dst, depth))
2029 return true;
2030 return false;
2031}
2032
2033/* add the input-route to the given pin */
2034static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002035{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002036 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002037 int c, idx;
2038
2039 spec->inputs[spec->num_inputs].adc_idx = -1;
2040 spec->inputs[spec->num_inputs].pin = pin;
2041 for (c = 0; c < spec->num_adc_nids; c++) {
2042 if (spec->mux_nids[c]) {
2043 idx = get_connection_index(codec, spec->mux_nids[c],
2044 pin);
2045 if (idx < 0)
2046 continue;
2047 spec->inputs[spec->num_inputs].mux_idx = idx;
2048 } else {
2049 if (!is_reachable_nid(codec, spec->adc_nids[c], pin, 0))
2050 continue;
2051 }
2052 spec->inputs[spec->num_inputs].adc_idx = c;
2053 /* Can primary ADC satisfy all inputs? */
2054 if (!spec->dyn_adc_switch &&
2055 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2056 snd_printd(KERN_INFO
2057 "via: dynamic ADC switching enabled\n");
2058 spec->dyn_adc_switch = 1;
2059 }
2060 return true;
2061 }
2062 return false;
2063}
2064
2065static int get_mux_nids(struct hda_codec *codec);
2066
2067/* parse input-routes; fill ADCs, MUXs and input-src entries */
2068static int parse_analog_inputs(struct hda_codec *codec)
2069{
2070 struct via_spec *spec = codec->spec;
2071 const struct auto_pin_cfg *cfg = &spec->autocfg;
2072 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002073
2074 err = via_fill_adcs(codec);
2075 if (err < 0)
2076 return err;
2077 err = get_mux_nids(codec);
2078 if (err < 0)
2079 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002080
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002081 /* fill all input-routes */
2082 for (i = 0; i < cfg->num_inputs; i++) {
2083 if (add_input_route(codec, cfg->inputs[i].pin))
2084 spec->inputs[spec->num_inputs++].label =
2085 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002086 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002087
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002088 /* check for internal loopback recording */
2089 if (spec->aa_mix_nid &&
2090 add_input_route(codec, spec->aa_mix_nid))
2091 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2092
2093 return 0;
2094}
2095
2096/* create analog-loopback volume/switch controls */
2097static int create_loopback_ctls(struct hda_codec *codec)
2098{
2099 struct via_spec *spec = codec->spec;
2100 const struct auto_pin_cfg *cfg = &spec->autocfg;
2101 const char *prev_label = NULL;
2102 int type_idx = 0;
2103 int i, j, err, idx;
2104
2105 if (!spec->aa_mix_nid)
2106 return 0;
2107
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002108 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002109 hda_nid_t pin = cfg->inputs[i].pin;
2110 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2111
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002112 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002113 type_idx++;
2114 else
2115 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002116 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002117 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2118 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002119 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002120 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002121 if (err < 0)
2122 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002123 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002124 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002125
2126 /* remember the label for smart51 control */
2127 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002128 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002129 spec->smart51_idxs[j] = idx;
2130 spec->smart51_labels[j] = label;
2131 break;
2132 }
2133 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002134 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002135 return 0;
2136}
2137
2138/* create mic-boost controls (if present) */
2139static int create_mic_boost_ctls(struct hda_codec *codec)
2140{
2141 struct via_spec *spec = codec->spec;
2142 const struct auto_pin_cfg *cfg = &spec->autocfg;
2143 int i, err;
2144
2145 for (i = 0; i < cfg->num_inputs; i++) {
2146 hda_nid_t pin = cfg->inputs[i].pin;
2147 unsigned int caps;
2148 const char *label;
2149 char name[32];
2150
2151 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2152 continue;
2153 caps = query_amp_caps(codec, pin, HDA_INPUT);
2154 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2155 continue;
2156 label = hda_get_autocfg_input_label(codec, cfg, i);
2157 snprintf(name, sizeof(name), "%s Boost Volume", label);
2158 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2159 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2160 if (err < 0)
2161 return err;
2162 }
2163 return 0;
2164}
2165
2166/* create capture and input-src controls for multiple streams */
2167static int create_multi_adc_ctls(struct hda_codec *codec)
2168{
2169 struct via_spec *spec = codec->spec;
2170 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002171
2172 /* create capture mixer elements */
2173 for (i = 0; i < spec->num_adc_nids; i++) {
2174 hda_nid_t adc = spec->adc_nids[i];
2175 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2176 "Capture Volume", i,
2177 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2178 HDA_INPUT));
2179 if (err < 0)
2180 return err;
2181 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2182 "Capture Switch", i,
2183 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2184 HDA_INPUT));
2185 if (err < 0)
2186 return err;
2187 }
2188
2189 /* input-source control */
2190 for (i = 0; i < spec->num_adc_nids; i++)
2191 if (!spec->mux_nids[i])
2192 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002193 err = create_input_src_ctls(codec, i);
2194 if (err < 0)
2195 return err;
2196 return 0;
2197}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002198
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002199/* bind capture volume/switch */
2200static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2201 HDA_BIND_VOL("Capture Volume", 0);
2202static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2203 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002204
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002205static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2206 struct hda_ctl_ops *ops)
2207{
2208 struct hda_bind_ctls *ctl;
2209 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002210
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002211 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2212 if (!ctl)
2213 return -ENOMEM;
2214 ctl->ops = ops;
2215 for (i = 0; i < spec->num_adc_nids; i++)
2216 ctl->values[i] =
2217 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2218 *ctl_ret = ctl;
2219 return 0;
2220}
2221
2222/* create capture and input-src controls for dynamic ADC-switch case */
2223static int create_dyn_adc_ctls(struct hda_codec *codec)
2224{
2225 struct via_spec *spec = codec->spec;
2226 struct snd_kcontrol_new *knew;
2227 int err;
2228
2229 /* set up the bind capture ctls */
2230 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2231 if (err < 0)
2232 return err;
2233 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2234 if (err < 0)
2235 return err;
2236
2237 /* create capture mixer elements */
2238 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2239 if (!knew)
2240 return -ENOMEM;
2241 knew->private_value = (long)spec->bind_cap_vol;
2242
2243 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2244 if (!knew)
2245 return -ENOMEM;
2246 knew->private_value = (long)spec->bind_cap_sw;
2247
2248 /* input-source control */
2249 err = create_input_src_ctls(codec, 1);
2250 if (err < 0)
2251 return err;
2252 return 0;
2253}
2254
2255/* parse and create capture-related stuff */
2256static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2257{
2258 struct via_spec *spec = codec->spec;
2259 int err;
2260
2261 err = parse_analog_inputs(codec);
2262 if (err < 0)
2263 return err;
2264 if (spec->dyn_adc_switch)
2265 err = create_dyn_adc_ctls(codec);
2266 else
2267 err = create_multi_adc_ctls(codec);
2268 if (err < 0)
2269 return err;
2270 err = create_loopback_ctls(codec);
2271 if (err < 0)
2272 return err;
2273 err = create_mic_boost_ctls(codec);
2274 if (err < 0)
2275 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002276 return 0;
2277}
2278
Harald Welte76d9b0d2008-09-09 15:50:37 +08002279static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2280{
2281 unsigned int def_conf;
2282 unsigned char seqassoc;
2283
Takashi Iwai2f334f92009-02-20 14:37:42 +01002284 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002285 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2286 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002287 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2288 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2289 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2290 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002291 }
2292
2293 return;
2294}
2295
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002296static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002297 struct snd_ctl_elem_value *ucontrol)
2298{
2299 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2300 struct via_spec *spec = codec->spec;
2301
2302 if (spec->codec_type != VT1708)
2303 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002304 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002305 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002306 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002307 return 0;
2308}
2309
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002310static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002311 struct snd_ctl_elem_value *ucontrol)
2312{
2313 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2314 struct via_spec *spec = codec->spec;
2315 int change;
2316
2317 if (spec->codec_type != VT1708)
2318 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002319 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002320 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002321 == !spec->vt1708_jack_detect;
2322 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002323 mute_aa_path(codec, 1);
2324 notify_aa_path_ctls(codec);
2325 }
2326 return change;
2327}
2328
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002329static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2330 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2331 .name = "Jack Detect",
2332 .count = 1,
2333 .info = snd_ctl_boolean_mono_info,
2334 .get = vt1708_jack_detect_get,
2335 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002336};
2337
Takashi Iwai12daef62011-06-18 17:45:49 +02002338static void fill_dig_outs(struct hda_codec *codec);
2339static void fill_dig_in(struct hda_codec *codec);
2340
2341static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002342{
2343 struct via_spec *spec = codec->spec;
2344 int err;
2345
2346 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2347 if (err < 0)
2348 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002349 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002350 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002351
Takashi Iwai4a796162011-06-17 17:53:38 +02002352 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002353 if (err < 0)
2354 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002355 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002356 if (err < 0)
2357 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002358 err = via_auto_create_speaker_ctls(codec);
2359 if (err < 0)
2360 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002361 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002362 if (err < 0)
2363 return err;
2364
2365 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2366
Takashi Iwai12daef62011-06-18 17:45:49 +02002367 fill_dig_outs(codec);
2368 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002369
Takashi Iwai603c4012008-07-30 15:01:44 +02002370 if (spec->kctls.list)
2371 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002372
Takashi Iwai096a8852011-06-20 12:09:02 +02002373 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002374
Takashi Iwai8df2a312011-06-21 11:48:29 +02002375 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002376 err = via_hp_build(codec);
2377 if (err < 0)
2378 return err;
2379 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002380
Takashi Iwaif4a78282011-06-17 18:46:48 +02002381 err = via_smart51_build(codec);
2382 if (err < 0)
2383 return err;
2384
Takashi Iwai5d417622011-06-20 11:32:27 +02002385 /* assign slave outs */
2386 if (spec->slave_dig_outs[0])
2387 codec->slave_dig_outs = spec->slave_dig_outs;
2388
Joseph Chanc577b8a2006-11-29 15:29:40 +01002389 return 1;
2390}
2391
Takashi Iwai5d417622011-06-20 11:32:27 +02002392static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002393{
Lydia Wang25eaba22009-10-10 19:08:43 +08002394 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002395 if (spec->multiout.dig_out_nid)
2396 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2397 if (spec->slave_dig_outs[0])
2398 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2399}
Lydia Wang25eaba22009-10-10 19:08:43 +08002400
Takashi Iwai5d417622011-06-20 11:32:27 +02002401static void via_auto_init_dig_in(struct hda_codec *codec)
2402{
2403 struct via_spec *spec = codec->spec;
2404 if (!spec->dig_in_nid)
2405 return;
2406 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2407 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2408}
2409
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002410/* initialize the unsolicited events */
2411static void via_auto_init_unsol_event(struct hda_codec *codec)
2412{
2413 struct via_spec *spec = codec->spec;
2414 struct auto_pin_cfg *cfg = &spec->autocfg;
2415 unsigned int ev;
2416 int i;
2417
2418 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2419 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2420 AC_VERB_SET_UNSOLICITED_ENABLE,
2421 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2422
2423 if (cfg->speaker_pins[0])
2424 ev = VIA_LINE_EVENT;
2425 else
2426 ev = 0;
2427 for (i = 0; i < cfg->line_outs; i++) {
2428 if (cfg->line_out_pins[i] &&
2429 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002430 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002431 AC_VERB_SET_UNSOLICITED_ENABLE,
2432 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2433 }
2434
2435 for (i = 0; i < cfg->num_inputs; i++) {
2436 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2437 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2438 AC_VERB_SET_UNSOLICITED_ENABLE,
2439 AC_USRSP_EN | VIA_JACK_EVENT);
2440 }
2441}
2442
Takashi Iwai5d417622011-06-20 11:32:27 +02002443static int via_init(struct hda_codec *codec)
2444{
2445 struct via_spec *spec = codec->spec;
2446 int i;
2447
2448 for (i = 0; i < spec->num_iverbs; i++)
2449 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2450
Joseph Chanc577b8a2006-11-29 15:29:40 +01002451 via_auto_init_multi_out(codec);
2452 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002453 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002454 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002455 via_auto_init_dig_outs(codec);
2456 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002457
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002458 via_auto_init_unsol_event(codec);
2459
2460 via_hp_automute(codec);
2461 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002462
Joseph Chanc577b8a2006-11-29 15:29:40 +01002463 return 0;
2464}
2465
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002466static void vt1708_update_hp_jack_state(struct work_struct *work)
2467{
2468 struct via_spec *spec = container_of(work, struct via_spec,
2469 vt1708_hp_work.work);
2470 if (spec->codec_type != VT1708)
2471 return;
2472 /* if jack state toggled */
2473 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002474 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002475 spec->vt1708_hp_present ^= 1;
2476 via_hp_automute(spec->codec);
2477 }
2478 vt1708_start_hp_work(spec);
2479}
2480
Takashi Iwai337b9d02009-07-07 18:18:59 +02002481static int get_mux_nids(struct hda_codec *codec)
2482{
2483 struct via_spec *spec = codec->spec;
2484 hda_nid_t nid, conn[8];
2485 unsigned int type;
2486 int i, n;
2487
2488 for (i = 0; i < spec->num_adc_nids; i++) {
2489 nid = spec->adc_nids[i];
2490 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002491 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002492 if (type == AC_WID_PIN)
2493 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002494 n = snd_hda_get_connections(codec, nid, conn,
2495 ARRAY_SIZE(conn));
2496 if (n <= 0)
2497 break;
2498 if (n > 1) {
2499 spec->mux_nids[i] = nid;
2500 break;
2501 }
2502 nid = conn[0];
2503 }
2504 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002505 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002506}
2507
Joseph Chanc577b8a2006-11-29 15:29:40 +01002508static int patch_vt1708(struct hda_codec *codec)
2509{
2510 struct via_spec *spec;
2511 int err;
2512
2513 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002514 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002515 if (spec == NULL)
2516 return -ENOMEM;
2517
Takashi Iwai620e2b22011-06-17 17:19:19 +02002518 spec->aa_mix_nid = 0x17;
2519
Takashi Iwai12daef62011-06-18 17:45:49 +02002520 /* Add HP and CD pin config connect bit re-config action */
2521 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2522 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2523
Joseph Chanc577b8a2006-11-29 15:29:40 +01002524 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002525 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002526 if (err < 0) {
2527 via_free(codec);
2528 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002529 }
2530
Takashi Iwai12daef62011-06-18 17:45:49 +02002531 /* add jack detect on/off control */
2532 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2533 return -ENOMEM;
2534
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002535 /* disable 32bit format on VT1708 */
2536 if (codec->vendor_id == 0x11061708)
2537 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002538
Joseph Chanc577b8a2006-11-29 15:29:40 +01002539 codec->patch_ops = via_patch_ops;
2540
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002541 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002542 return 0;
2543}
2544
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002545static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002546{
2547 struct via_spec *spec;
2548 int err;
2549
2550 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002551 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002552 if (spec == NULL)
2553 return -ENOMEM;
2554
Takashi Iwai620e2b22011-06-17 17:19:19 +02002555 spec->aa_mix_nid = 0x18;
2556
Takashi Iwai12daef62011-06-18 17:45:49 +02002557 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002558 if (err < 0) {
2559 via_free(codec);
2560 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002561 }
2562
Joseph Chanc577b8a2006-11-29 15:29:40 +01002563 codec->patch_ops = via_patch_ops;
2564
Josepch Chanf7278fd2007-12-13 16:40:40 +01002565 return 0;
2566}
2567
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002568static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2569{
2570 struct via_spec *spec = codec->spec;
2571 int imux_is_smixer;
2572 unsigned int parm;
2573 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002574 if ((spec->codec_type != VT1708B_4CH) &&
2575 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002576 is_8ch = 1;
2577
2578 /* SW0 (17h) = stereo mixer */
2579 imux_is_smixer =
2580 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2581 == ((spec->codec_type == VT1708S) ? 5 : 0));
2582 /* inputs */
2583 /* PW 1/2/5 (1ah/1bh/1eh) */
2584 parm = AC_PWRST_D3;
2585 set_pin_power_state(codec, 0x1a, &parm);
2586 set_pin_power_state(codec, 0x1b, &parm);
2587 set_pin_power_state(codec, 0x1e, &parm);
2588 if (imux_is_smixer)
2589 parm = AC_PWRST_D0;
2590 /* SW0 (17h), AIW 0/1 (13h/14h) */
2591 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2592 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2593 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2594
2595 /* outputs */
2596 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2597 parm = AC_PWRST_D3;
2598 set_pin_power_state(codec, 0x19, &parm);
2599 if (spec->smart51_enabled)
2600 set_pin_power_state(codec, 0x1b, &parm);
2601 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2602 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2603
2604 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2605 if (is_8ch) {
2606 parm = AC_PWRST_D3;
2607 set_pin_power_state(codec, 0x22, &parm);
2608 if (spec->smart51_enabled)
2609 set_pin_power_state(codec, 0x1a, &parm);
2610 snd_hda_codec_write(codec, 0x26, 0,
2611 AC_VERB_SET_POWER_STATE, parm);
2612 snd_hda_codec_write(codec, 0x24, 0,
2613 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002614 } else if (codec->vendor_id == 0x11064397) {
2615 /* PW7(23h), SW2(27h), AOW2(25h) */
2616 parm = AC_PWRST_D3;
2617 set_pin_power_state(codec, 0x23, &parm);
2618 if (spec->smart51_enabled)
2619 set_pin_power_state(codec, 0x1a, &parm);
2620 snd_hda_codec_write(codec, 0x27, 0,
2621 AC_VERB_SET_POWER_STATE, parm);
2622 snd_hda_codec_write(codec, 0x25, 0,
2623 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002624 }
2625
2626 /* PW 3/4/7 (1ch/1dh/23h) */
2627 parm = AC_PWRST_D3;
2628 /* force to D0 for internal Speaker */
2629 set_pin_power_state(codec, 0x1c, &parm);
2630 set_pin_power_state(codec, 0x1d, &parm);
2631 if (is_8ch)
2632 set_pin_power_state(codec, 0x23, &parm);
2633
2634 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2635 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2636 imux_is_smixer ? AC_PWRST_D0 : parm);
2637 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2638 if (is_8ch) {
2639 snd_hda_codec_write(codec, 0x25, 0,
2640 AC_VERB_SET_POWER_STATE, parm);
2641 snd_hda_codec_write(codec, 0x27, 0,
2642 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002643 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2644 snd_hda_codec_write(codec, 0x25, 0,
2645 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002646}
2647
Lydia Wang518bf3b2009-10-10 19:07:29 +08002648static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002649static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002650{
2651 struct via_spec *spec;
2652 int err;
2653
Lydia Wang518bf3b2009-10-10 19:07:29 +08002654 if (get_codec_type(codec) == VT1708BCE)
2655 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002656
Josepch Chanf7278fd2007-12-13 16:40:40 +01002657 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002658 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002659 if (spec == NULL)
2660 return -ENOMEM;
2661
Takashi Iwai620e2b22011-06-17 17:19:19 +02002662 spec->aa_mix_nid = 0x16;
2663
Josepch Chanf7278fd2007-12-13 16:40:40 +01002664 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002665 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002666 if (err < 0) {
2667 via_free(codec);
2668 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002669 }
2670
Josepch Chanf7278fd2007-12-13 16:40:40 +01002671 codec->patch_ops = via_patch_ops;
2672
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002673 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2674
Josepch Chanf7278fd2007-12-13 16:40:40 +01002675 return 0;
2676}
2677
Harald Welted949cac2008-09-09 15:56:01 +08002678/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002679static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002680 /* Enable Mic Boost Volume backdoor */
2681 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002682 /* don't bybass mixer */
2683 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002684 { }
2685};
2686
Takashi Iwai9da29272009-05-07 16:31:14 +02002687/* fill out digital output widgets; one for master and one for slave outputs */
2688static void fill_dig_outs(struct hda_codec *codec)
2689{
2690 struct via_spec *spec = codec->spec;
2691 int i;
2692
2693 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2694 hda_nid_t nid;
2695 int conn;
2696
2697 nid = spec->autocfg.dig_out_pins[i];
2698 if (!nid)
2699 continue;
2700 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2701 if (conn < 1)
2702 continue;
2703 if (!spec->multiout.dig_out_nid)
2704 spec->multiout.dig_out_nid = nid;
2705 else {
2706 spec->slave_dig_outs[0] = nid;
2707 break; /* at most two dig outs */
2708 }
2709 }
2710}
2711
Takashi Iwai12daef62011-06-18 17:45:49 +02002712static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002713{
2714 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002715 hda_nid_t dig_nid;
2716 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002717
Takashi Iwai12daef62011-06-18 17:45:49 +02002718 if (!spec->autocfg.dig_in_pin)
2719 return;
Harald Welted949cac2008-09-09 15:56:01 +08002720
Takashi Iwai12daef62011-06-18 17:45:49 +02002721 dig_nid = codec->start_nid;
2722 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2723 unsigned int wcaps = get_wcaps(codec, dig_nid);
2724 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2725 continue;
2726 if (!(wcaps & AC_WCAP_DIGITAL))
2727 continue;
2728 if (!(wcaps & AC_WCAP_CONN_LIST))
2729 continue;
2730 err = get_connection_index(codec, dig_nid,
2731 spec->autocfg.dig_in_pin);
2732 if (err >= 0) {
2733 spec->dig_in_nid = dig_nid;
2734 break;
2735 }
2736 }
Harald Welted949cac2008-09-09 15:56:01 +08002737}
2738
Lydia Wang6369bcf2009-10-10 19:08:31 +08002739static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2740 int offset, int num_steps, int step_size)
2741{
2742 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2743 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2744 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2745 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2746 (0 << AC_AMPCAP_MUTE_SHIFT));
2747}
2748
Harald Welted949cac2008-09-09 15:56:01 +08002749static int patch_vt1708S(struct hda_codec *codec)
2750{
2751 struct via_spec *spec;
2752 int err;
2753
2754 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002755 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002756 if (spec == NULL)
2757 return -ENOMEM;
2758
Takashi Iwai620e2b22011-06-17 17:19:19 +02002759 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002760 override_mic_boost(codec, 0x1a, 0, 3, 40);
2761 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002762
Harald Welted949cac2008-09-09 15:56:01 +08002763 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002764 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002765 if (err < 0) {
2766 via_free(codec);
2767 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002768 }
2769
Takashi Iwai096a8852011-06-20 12:09:02 +02002770 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002771
Harald Welted949cac2008-09-09 15:56:01 +08002772 codec->patch_ops = via_patch_ops;
2773
Lydia Wang518bf3b2009-10-10 19:07:29 +08002774 /* correct names for VT1708BCE */
2775 if (get_codec_type(codec) == VT1708BCE) {
2776 kfree(codec->chip_name);
2777 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2778 snprintf(codec->bus->card->mixername,
2779 sizeof(codec->bus->card->mixername),
2780 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002781 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002782 /* correct names for VT1705 */
2783 if (codec->vendor_id == 0x11064397) {
2784 kfree(codec->chip_name);
2785 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2786 snprintf(codec->bus->card->mixername,
2787 sizeof(codec->bus->card->mixername),
2788 "%s %s", codec->vendor_name, codec->chip_name);
2789 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002790 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002791 return 0;
2792}
2793
2794/* Patch for VT1702 */
2795
Takashi Iwai096a8852011-06-20 12:09:02 +02002796static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002797 /* mixer enable */
2798 {0x1, 0xF88, 0x3},
2799 /* GPIO 0~2 */
2800 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002801 { }
2802};
2803
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002804static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2805{
2806 int imux_is_smixer =
2807 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2808 unsigned int parm;
2809 /* inputs */
2810 /* PW 1/2/5 (14h/15h/18h) */
2811 parm = AC_PWRST_D3;
2812 set_pin_power_state(codec, 0x14, &parm);
2813 set_pin_power_state(codec, 0x15, &parm);
2814 set_pin_power_state(codec, 0x18, &parm);
2815 if (imux_is_smixer)
2816 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2817 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2818 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2819 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2820 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2821 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2822
2823 /* outputs */
2824 /* PW 3/4 (16h/17h) */
2825 parm = AC_PWRST_D3;
2826 set_pin_power_state(codec, 0x17, &parm);
2827 set_pin_power_state(codec, 0x16, &parm);
2828 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2829 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2830 imux_is_smixer ? AC_PWRST_D0 : parm);
2831 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2832 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2833}
2834
Harald Welted949cac2008-09-09 15:56:01 +08002835static int patch_vt1702(struct hda_codec *codec)
2836{
2837 struct via_spec *spec;
2838 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002839
2840 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002841 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002842 if (spec == NULL)
2843 return -ENOMEM;
2844
Takashi Iwai620e2b22011-06-17 17:19:19 +02002845 spec->aa_mix_nid = 0x1a;
2846
Takashi Iwai12daef62011-06-18 17:45:49 +02002847 /* limit AA path volume to 0 dB */
2848 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2849 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2850 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2851 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2852 (1 << AC_AMPCAP_MUTE_SHIFT));
2853
Harald Welted949cac2008-09-09 15:56:01 +08002854 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002855 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002856 if (err < 0) {
2857 via_free(codec);
2858 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002859 }
2860
Takashi Iwai096a8852011-06-20 12:09:02 +02002861 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002862
Harald Welted949cac2008-09-09 15:56:01 +08002863 codec->patch_ops = via_patch_ops;
2864
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002865 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002866 return 0;
2867}
2868
Lydia Wangeb7188c2009-10-10 19:08:34 +08002869/* Patch for VT1718S */
2870
Takashi Iwai096a8852011-06-20 12:09:02 +02002871static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002872 /* Enable MW0 adjust Gain 5 */
2873 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002874 /* Enable Boost Volume backdoor */
2875 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002876
Lydia Wangeb7188c2009-10-10 19:08:34 +08002877 { }
2878};
2879
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002880static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2881{
2882 struct via_spec *spec = codec->spec;
2883 int imux_is_smixer;
2884 unsigned int parm;
2885 /* MUX6 (1eh) = stereo mixer */
2886 imux_is_smixer =
2887 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2888 /* inputs */
2889 /* PW 5/6/7 (29h/2ah/2bh) */
2890 parm = AC_PWRST_D3;
2891 set_pin_power_state(codec, 0x29, &parm);
2892 set_pin_power_state(codec, 0x2a, &parm);
2893 set_pin_power_state(codec, 0x2b, &parm);
2894 if (imux_is_smixer)
2895 parm = AC_PWRST_D0;
2896 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2897 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2898 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2899 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2900 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2901
2902 /* outputs */
2903 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2904 parm = AC_PWRST_D3;
2905 set_pin_power_state(codec, 0x27, &parm);
2906 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2907 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2908
2909 /* PW2 (26h), AOW2 (ah) */
2910 parm = AC_PWRST_D3;
2911 set_pin_power_state(codec, 0x26, &parm);
2912 if (spec->smart51_enabled)
2913 set_pin_power_state(codec, 0x2b, &parm);
2914 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2915
2916 /* PW0 (24h), AOW0 (8h) */
2917 parm = AC_PWRST_D3;
2918 set_pin_power_state(codec, 0x24, &parm);
2919 if (!spec->hp_independent_mode) /* check for redirected HP */
2920 set_pin_power_state(codec, 0x28, &parm);
2921 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2922 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2923 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2924 imux_is_smixer ? AC_PWRST_D0 : parm);
2925
2926 /* PW1 (25h), AOW1 (9h) */
2927 parm = AC_PWRST_D3;
2928 set_pin_power_state(codec, 0x25, &parm);
2929 if (spec->smart51_enabled)
2930 set_pin_power_state(codec, 0x2a, &parm);
2931 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2932
2933 if (spec->hp_independent_mode) {
2934 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2935 parm = AC_PWRST_D3;
2936 set_pin_power_state(codec, 0x28, &parm);
2937 snd_hda_codec_write(codec, 0x1b, 0,
2938 AC_VERB_SET_POWER_STATE, parm);
2939 snd_hda_codec_write(codec, 0x34, 0,
2940 AC_VERB_SET_POWER_STATE, parm);
2941 snd_hda_codec_write(codec, 0xc, 0,
2942 AC_VERB_SET_POWER_STATE, parm);
2943 }
2944}
2945
Lydia Wangeb7188c2009-10-10 19:08:34 +08002946static int patch_vt1718S(struct hda_codec *codec)
2947{
2948 struct via_spec *spec;
2949 int err;
2950
2951 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002952 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002953 if (spec == NULL)
2954 return -ENOMEM;
2955
Takashi Iwai620e2b22011-06-17 17:19:19 +02002956 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002957 override_mic_boost(codec, 0x2b, 0, 3, 40);
2958 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002959
Lydia Wangeb7188c2009-10-10 19:08:34 +08002960 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002961 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002962 if (err < 0) {
2963 via_free(codec);
2964 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002965 }
2966
Takashi Iwai096a8852011-06-20 12:09:02 +02002967 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002968
Lydia Wangeb7188c2009-10-10 19:08:34 +08002969 codec->patch_ops = via_patch_ops;
2970
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002971 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2972
Lydia Wangeb7188c2009-10-10 19:08:34 +08002973 return 0;
2974}
Lydia Wangf3db4232009-10-10 19:08:41 +08002975
2976/* Patch for VT1716S */
2977
2978static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2979 struct snd_ctl_elem_info *uinfo)
2980{
2981 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2982 uinfo->count = 1;
2983 uinfo->value.integer.min = 0;
2984 uinfo->value.integer.max = 1;
2985 return 0;
2986}
2987
2988static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2989 struct snd_ctl_elem_value *ucontrol)
2990{
2991 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2992 int index = 0;
2993
2994 index = snd_hda_codec_read(codec, 0x26, 0,
2995 AC_VERB_GET_CONNECT_SEL, 0);
2996 if (index != -1)
2997 *ucontrol->value.integer.value = index;
2998
2999 return 0;
3000}
3001
3002static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3003 struct snd_ctl_elem_value *ucontrol)
3004{
3005 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3006 struct via_spec *spec = codec->spec;
3007 int index = *ucontrol->value.integer.value;
3008
3009 snd_hda_codec_write(codec, 0x26, 0,
3010 AC_VERB_SET_CONNECT_SEL, index);
3011 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003012 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003013 return 1;
3014}
3015
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003016static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003017 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3018 {
3019 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3020 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003021 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003022 .count = 1,
3023 .info = vt1716s_dmic_info,
3024 .get = vt1716s_dmic_get,
3025 .put = vt1716s_dmic_put,
3026 },
3027 {} /* end */
3028};
3029
3030
3031/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003032static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003033 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3034 { } /* end */
3035};
3036
Takashi Iwai096a8852011-06-20 12:09:02 +02003037static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003038 /* Enable Boost Volume backdoor */
3039 {0x1, 0xf8a, 0x80},
3040 /* don't bybass mixer */
3041 {0x1, 0xf88, 0xc0},
3042 /* Enable mono output */
3043 {0x1, 0xf90, 0x08},
3044 { }
3045};
3046
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003047static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3048{
3049 struct via_spec *spec = codec->spec;
3050 int imux_is_smixer;
3051 unsigned int parm;
3052 unsigned int mono_out, present;
3053 /* SW0 (17h) = stereo mixer */
3054 imux_is_smixer =
3055 (snd_hda_codec_read(codec, 0x17, 0,
3056 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3057 /* inputs */
3058 /* PW 1/2/5 (1ah/1bh/1eh) */
3059 parm = AC_PWRST_D3;
3060 set_pin_power_state(codec, 0x1a, &parm);
3061 set_pin_power_state(codec, 0x1b, &parm);
3062 set_pin_power_state(codec, 0x1e, &parm);
3063 if (imux_is_smixer)
3064 parm = AC_PWRST_D0;
3065 /* SW0 (17h), AIW0(13h) */
3066 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3067 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3068
3069 parm = AC_PWRST_D3;
3070 set_pin_power_state(codec, 0x1e, &parm);
3071 /* PW11 (22h) */
3072 if (spec->dmic_enabled)
3073 set_pin_power_state(codec, 0x22, &parm);
3074 else
3075 snd_hda_codec_write(codec, 0x22, 0,
3076 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3077
3078 /* SW2(26h), AIW1(14h) */
3079 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3080 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3081
3082 /* outputs */
3083 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3084 parm = AC_PWRST_D3;
3085 set_pin_power_state(codec, 0x19, &parm);
3086 /* Smart 5.1 PW2(1bh) */
3087 if (spec->smart51_enabled)
3088 set_pin_power_state(codec, 0x1b, &parm);
3089 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3090 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3091
3092 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3093 parm = AC_PWRST_D3;
3094 set_pin_power_state(codec, 0x23, &parm);
3095 /* Smart 5.1 PW1(1ah) */
3096 if (spec->smart51_enabled)
3097 set_pin_power_state(codec, 0x1a, &parm);
3098 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3099
3100 /* Smart 5.1 PW5(1eh) */
3101 if (spec->smart51_enabled)
3102 set_pin_power_state(codec, 0x1e, &parm);
3103 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3104
3105 /* Mono out */
3106 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3107 present = snd_hda_jack_detect(codec, 0x1c);
3108
3109 if (present)
3110 mono_out = 0;
3111 else {
3112 present = snd_hda_jack_detect(codec, 0x1d);
3113 if (!spec->hp_independent_mode && present)
3114 mono_out = 0;
3115 else
3116 mono_out = 1;
3117 }
3118 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3119 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3120 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3121 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3122
3123 /* PW 3/4 (1ch/1dh) */
3124 parm = AC_PWRST_D3;
3125 set_pin_power_state(codec, 0x1c, &parm);
3126 set_pin_power_state(codec, 0x1d, &parm);
3127 /* HP Independent Mode, power on AOW3 */
3128 if (spec->hp_independent_mode)
3129 snd_hda_codec_write(codec, 0x25, 0,
3130 AC_VERB_SET_POWER_STATE, parm);
3131
3132 /* force to D0 for internal Speaker */
3133 /* MW0 (16h), AOW0 (10h) */
3134 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3135 imux_is_smixer ? AC_PWRST_D0 : parm);
3136 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3137 mono_out ? AC_PWRST_D0 : parm);
3138}
3139
Lydia Wangf3db4232009-10-10 19:08:41 +08003140static int patch_vt1716S(struct hda_codec *codec)
3141{
3142 struct via_spec *spec;
3143 int err;
3144
3145 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003146 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003147 if (spec == NULL)
3148 return -ENOMEM;
3149
Takashi Iwai620e2b22011-06-17 17:19:19 +02003150 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003151 override_mic_boost(codec, 0x1a, 0, 3, 40);
3152 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003153
Lydia Wangf3db4232009-10-10 19:08:41 +08003154 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003155 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003156 if (err < 0) {
3157 via_free(codec);
3158 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003159 }
3160
Takashi Iwai096a8852011-06-20 12:09:02 +02003161 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003162
Lydia Wangf3db4232009-10-10 19:08:41 +08003163 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3164 spec->num_mixers++;
3165
3166 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3167
3168 codec->patch_ops = via_patch_ops;
3169
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003170 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003171 return 0;
3172}
Lydia Wang25eaba22009-10-10 19:08:43 +08003173
3174/* for vt2002P */
3175
Takashi Iwai096a8852011-06-20 12:09:02 +02003176static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003177 /* Class-D speaker related verbs */
3178 {0x1, 0xfe0, 0x4},
3179 {0x1, 0xfe9, 0x80},
3180 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003181 /* Enable Boost Volume backdoor */
3182 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003183 /* Enable AOW0 to MW9 */
3184 {0x1, 0xfb8, 0x88},
3185 { }
3186};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003187
Takashi Iwai096a8852011-06-20 12:09:02 +02003188static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003189 /* Enable Boost Volume backdoor */
3190 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003191 /* Enable AOW0 to MW9 */
3192 {0x1, 0xfb8, 0x88},
3193 { }
3194};
Lydia Wang25eaba22009-10-10 19:08:43 +08003195
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003196static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3197{
3198 struct via_spec *spec = codec->spec;
3199 int imux_is_smixer;
3200 unsigned int parm;
3201 unsigned int present;
3202 /* MUX9 (1eh) = stereo mixer */
3203 imux_is_smixer =
3204 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3205 /* inputs */
3206 /* PW 5/6/7 (29h/2ah/2bh) */
3207 parm = AC_PWRST_D3;
3208 set_pin_power_state(codec, 0x29, &parm);
3209 set_pin_power_state(codec, 0x2a, &parm);
3210 set_pin_power_state(codec, 0x2b, &parm);
3211 parm = AC_PWRST_D0;
3212 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3213 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3214 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3215 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3216 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3217
3218 /* outputs */
3219 /* AOW0 (8h)*/
3220 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3221
Lydia Wang118909562011-03-23 17:57:34 +08003222 if (spec->codec_type == VT1802) {
3223 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3224 parm = AC_PWRST_D3;
3225 set_pin_power_state(codec, 0x28, &parm);
3226 snd_hda_codec_write(codec, 0x18, 0,
3227 AC_VERB_SET_POWER_STATE, parm);
3228 snd_hda_codec_write(codec, 0x38, 0,
3229 AC_VERB_SET_POWER_STATE, parm);
3230 } else {
3231 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3232 parm = AC_PWRST_D3;
3233 set_pin_power_state(codec, 0x26, &parm);
3234 snd_hda_codec_write(codec, 0x1c, 0,
3235 AC_VERB_SET_POWER_STATE, parm);
3236 snd_hda_codec_write(codec, 0x37, 0,
3237 AC_VERB_SET_POWER_STATE, parm);
3238 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003239
Lydia Wang118909562011-03-23 17:57:34 +08003240 if (spec->codec_type == VT1802) {
3241 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3242 parm = AC_PWRST_D3;
3243 set_pin_power_state(codec, 0x25, &parm);
3244 snd_hda_codec_write(codec, 0x15, 0,
3245 AC_VERB_SET_POWER_STATE, parm);
3246 snd_hda_codec_write(codec, 0x35, 0,
3247 AC_VERB_SET_POWER_STATE, parm);
3248 } else {
3249 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3250 parm = AC_PWRST_D3;
3251 set_pin_power_state(codec, 0x25, &parm);
3252 snd_hda_codec_write(codec, 0x19, 0,
3253 AC_VERB_SET_POWER_STATE, parm);
3254 snd_hda_codec_write(codec, 0x35, 0,
3255 AC_VERB_SET_POWER_STATE, parm);
3256 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003257
3258 if (spec->hp_independent_mode)
3259 snd_hda_codec_write(codec, 0x9, 0,
3260 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3261
3262 /* Class-D */
3263 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3264 present = snd_hda_jack_detect(codec, 0x25);
3265
3266 parm = AC_PWRST_D3;
3267 set_pin_power_state(codec, 0x24, &parm);
3268 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003269 if (spec->codec_type == VT1802)
3270 snd_hda_codec_write(codec, 0x14, 0,
3271 AC_VERB_SET_POWER_STATE, parm);
3272 else
3273 snd_hda_codec_write(codec, 0x18, 0,
3274 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003275 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3276
3277 /* Mono Out */
3278 present = snd_hda_jack_detect(codec, 0x26);
3279
3280 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003281 if (spec->codec_type == VT1802) {
3282 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3283 snd_hda_codec_write(codec, 0x33, 0,
3284 AC_VERB_SET_POWER_STATE, parm);
3285 snd_hda_codec_write(codec, 0x1c, 0,
3286 AC_VERB_SET_POWER_STATE, parm);
3287 snd_hda_codec_write(codec, 0x3c, 0,
3288 AC_VERB_SET_POWER_STATE, parm);
3289 } else {
3290 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3291 snd_hda_codec_write(codec, 0x31, 0,
3292 AC_VERB_SET_POWER_STATE, parm);
3293 snd_hda_codec_write(codec, 0x17, 0,
3294 AC_VERB_SET_POWER_STATE, parm);
3295 snd_hda_codec_write(codec, 0x3b, 0,
3296 AC_VERB_SET_POWER_STATE, parm);
3297 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003298 /* MW9 (21h) */
3299 if (imux_is_smixer || !is_aa_path_mute(codec))
3300 snd_hda_codec_write(codec, 0x21, 0,
3301 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3302 else
3303 snd_hda_codec_write(codec, 0x21, 0,
3304 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3305}
Lydia Wang25eaba22009-10-10 19:08:43 +08003306
3307/* patch for vt2002P */
3308static int patch_vt2002P(struct hda_codec *codec)
3309{
3310 struct via_spec *spec;
3311 int err;
3312
3313 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003314 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003315 if (spec == NULL)
3316 return -ENOMEM;
3317
Takashi Iwai620e2b22011-06-17 17:19:19 +02003318 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003319 override_mic_boost(codec, 0x2b, 0, 3, 40);
3320 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003321
Lydia Wang25eaba22009-10-10 19:08:43 +08003322 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003323 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003324 if (err < 0) {
3325 via_free(codec);
3326 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003327 }
3328
Lydia Wang118909562011-03-23 17:57:34 +08003329 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003330 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003331 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003332 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003333
Lydia Wang25eaba22009-10-10 19:08:43 +08003334 codec->patch_ops = via_patch_ops;
3335
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003336 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003337 return 0;
3338}
Lydia Wangab6734e2009-10-10 19:08:46 +08003339
3340/* for vt1812 */
3341
Takashi Iwai096a8852011-06-20 12:09:02 +02003342static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003343 /* Enable Boost Volume backdoor */
3344 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003345 /* Enable AOW0 to MW9 */
3346 {0x1, 0xfb8, 0xa8},
3347 { }
3348};
3349
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003350static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3351{
3352 struct via_spec *spec = codec->spec;
3353 int imux_is_smixer =
3354 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3355 unsigned int parm;
3356 unsigned int present;
3357 /* MUX10 (1eh) = stereo mixer */
3358 imux_is_smixer =
3359 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3360 /* inputs */
3361 /* PW 5/6/7 (29h/2ah/2bh) */
3362 parm = AC_PWRST_D3;
3363 set_pin_power_state(codec, 0x29, &parm);
3364 set_pin_power_state(codec, 0x2a, &parm);
3365 set_pin_power_state(codec, 0x2b, &parm);
3366 parm = AC_PWRST_D0;
3367 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3368 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3369 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3370 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3371 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3372
3373 /* outputs */
3374 /* AOW0 (8h)*/
3375 snd_hda_codec_write(codec, 0x8, 0,
3376 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3377
3378 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3379 parm = AC_PWRST_D3;
3380 set_pin_power_state(codec, 0x28, &parm);
3381 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3382 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3383
3384 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3385 parm = AC_PWRST_D3;
3386 set_pin_power_state(codec, 0x25, &parm);
3387 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3388 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3389 if (spec->hp_independent_mode)
3390 snd_hda_codec_write(codec, 0x9, 0,
3391 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3392
3393 /* Internal Speaker */
3394 /* PW0 (24h), MW0(14h), MUX0(34h) */
3395 present = snd_hda_jack_detect(codec, 0x25);
3396
3397 parm = AC_PWRST_D3;
3398 set_pin_power_state(codec, 0x24, &parm);
3399 if (present) {
3400 snd_hda_codec_write(codec, 0x14, 0,
3401 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3402 snd_hda_codec_write(codec, 0x34, 0,
3403 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3404 } else {
3405 snd_hda_codec_write(codec, 0x14, 0,
3406 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3407 snd_hda_codec_write(codec, 0x34, 0,
3408 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3409 }
3410
3411
3412 /* Mono Out */
3413 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3414 present = snd_hda_jack_detect(codec, 0x28);
3415
3416 parm = AC_PWRST_D3;
3417 set_pin_power_state(codec, 0x31, &parm);
3418 if (present) {
3419 snd_hda_codec_write(codec, 0x1c, 0,
3420 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3421 snd_hda_codec_write(codec, 0x3c, 0,
3422 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3423 snd_hda_codec_write(codec, 0x3e, 0,
3424 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3425 } else {
3426 snd_hda_codec_write(codec, 0x1c, 0,
3427 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3428 snd_hda_codec_write(codec, 0x3c, 0,
3429 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3430 snd_hda_codec_write(codec, 0x3e, 0,
3431 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3432 }
3433
3434 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3435 parm = AC_PWRST_D3;
3436 set_pin_power_state(codec, 0x33, &parm);
3437 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3438 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3439
3440}
Lydia Wangab6734e2009-10-10 19:08:46 +08003441
3442/* patch for vt1812 */
3443static int patch_vt1812(struct hda_codec *codec)
3444{
3445 struct via_spec *spec;
3446 int err;
3447
3448 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003449 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003450 if (spec == NULL)
3451 return -ENOMEM;
3452
Takashi Iwai620e2b22011-06-17 17:19:19 +02003453 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003454 override_mic_boost(codec, 0x2b, 0, 3, 40);
3455 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003456
Lydia Wangab6734e2009-10-10 19:08:46 +08003457 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003458 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003459 if (err < 0) {
3460 via_free(codec);
3461 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003462 }
3463
Takashi Iwai096a8852011-06-20 12:09:02 +02003464 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003465
Lydia Wangab6734e2009-10-10 19:08:46 +08003466 codec->patch_ops = via_patch_ops;
3467
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003468 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003469 return 0;
3470}
3471
Joseph Chanc577b8a2006-11-29 15:29:40 +01003472/*
3473 * patch entries
3474 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003475static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003476 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3477 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3478 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3479 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3480 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003481 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003482 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003483 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003484 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003485 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003486 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003487 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003488 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003489 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003490 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003491 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003492 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003493 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003494 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003495 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003496 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003497 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003498 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003499 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003500 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003501 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003502 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003503 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003504 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003505 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003506 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003507 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003508 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003509 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003510 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003511 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003512 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003513 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003514 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003515 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003516 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003517 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003518 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003519 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003520 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003521 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003522 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003523 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003524 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003525 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003526 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003527 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003528 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003529 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003530 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003531 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003532 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003533 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003534 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003535 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003536 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003537 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003538 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003539 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003540 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003541 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003542 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003543 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003544 { .id = 0x11060428, .name = "VT1718S",
3545 .patch = patch_vt1718S},
3546 { .id = 0x11064428, .name = "VT1718S",
3547 .patch = patch_vt1718S},
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08003548 { .id = 0x11060441, .name = "VT2020",
3549 .patch = patch_vt1718S},
3550 { .id = 0x11064441, .name = "VT1828S",
3551 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003552 { .id = 0x11060433, .name = "VT1716S",
3553 .patch = patch_vt1716S},
3554 { .id = 0x1106a721, .name = "VT1716S",
3555 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003556 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3557 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003558 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003559 { .id = 0x11060440, .name = "VT1818S",
3560 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003561 { .id = 0x11060446, .name = "VT1802",
3562 .patch = patch_vt2002P},
3563 { .id = 0x11068446, .name = "VT1802",
3564 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003565 {} /* terminator */
3566};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003567
3568MODULE_ALIAS("snd-hda-codec-id:1106*");
3569
3570static struct hda_codec_preset_list via_list = {
3571 .preset = snd_hda_preset_via,
3572 .owner = THIS_MODULE,
3573};
3574
3575MODULE_LICENSE("GPL");
3576MODULE_DESCRIPTION("VIA HD-audio codec");
3577
3578static int __init patch_via_init(void)
3579{
3580 return snd_hda_add_codec_preset(&via_list);
3581}
3582
3583static void __exit patch_via_exit(void)
3584{
3585 snd_hda_delete_codec_preset(&via_list);
3586}
3587
3588module_init(patch_via_init)
3589module_exit(patch_via_exit)