blob: 27de53fb331e6d7b5ef286eb3b7949da2317d7a7 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010052#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080053#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010054#include "hda_codec.h"
55#include "hda_local.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010056
Joseph Chanc577b8a2006-11-29 15:29:40 +010057/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080058#define VT1708_HP_PIN_NID 0x20
59#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010060
Harald Welted7426322008-09-15 22:43:23 +080061enum VIA_HDA_CODEC {
62 UNKNOWN = -1,
63 VT1708,
64 VT1709_10CH,
65 VT1709_6CH,
66 VT1708B_8CH,
67 VT1708B_4CH,
68 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080069 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080070 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080071 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080072 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080073 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080074 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080075 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080076 CODEC_TYPES,
77};
78
Lydia Wang118909562011-03-23 17:57:34 +080079#define VT2002P_COMPATIBLE(spec) \
80 ((spec)->codec_type == VT2002P ||\
81 (spec)->codec_type == VT1812 ||\
82 (spec)->codec_type == VT1802)
83
Takashi Iwai8e3679d2011-06-21 09:01:36 +020084#define MAX_NID_PATH_DEPTH 5
85
Takashi Iwai09a9ad692011-06-21 15:57:44 +020086/* output-path: DAC -> ... -> pin
87 * idx[] contains the source index number of the next widget;
88 * e.g. idx[0] is the index of the DAC selected by path[1] widget
89 * multi[] indicates whether it's a selector widget with multi-connectors
90 * (i.e. the connection selection is mandatory)
91 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
92 */
Takashi Iwai4a796162011-06-17 17:53:38 +020093struct nid_path {
94 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020095 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad692011-06-21 15:57:44 +020096 unsigned char idx[MAX_NID_PATH_DEPTH];
97 unsigned char multi[MAX_NID_PATH_DEPTH];
98 unsigned int vol_ctl;
99 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200100};
101
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200102/* input-path */
103struct via_input {
104 hda_nid_t pin; /* input-pin or aa-mix */
105 int adc_idx; /* ADC index to be used */
106 int mux_idx; /* MUX index (if any) */
107 const char *label; /* input-source label */
108};
109
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200110#define VIA_MAX_ADCS 3
111
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800112struct via_spec {
113 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200114 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800115 unsigned int num_mixers;
116
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200117 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800118 unsigned int num_iverbs;
119
Takashi Iwai82673bc2011-06-17 16:24:21 +0200120 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200121 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200122 const struct hda_pcm_stream *stream_analog_playback;
123 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800124
Takashi Iwai82673bc2011-06-17 16:24:21 +0200125 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200126 const struct hda_pcm_stream *stream_digital_playback;
127 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800128
129 /* playback */
130 struct hda_multi_out multiout;
131 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200132 hda_nid_t hp_dac_nid;
Takashi Iwai25250502011-06-30 17:24:47 +0200133 bool hp_indep_shared; /* indep HP-DAC is shared with side ch */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200134 int num_active_streams;
Lydia Wangc4394f52011-07-04 16:54:15 +0800135 int dac_mixer_idx;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800136
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200137 struct nid_path out_path[HDA_SIDE + 1];
Takashi Iwai4a796162011-06-17 17:53:38 +0200138 struct nid_path hp_path;
139 struct nid_path hp_dep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200140 struct nid_path speaker_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200141
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800142 /* capture */
143 unsigned int num_adc_nids;
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200144 hda_nid_t adc_nids[VIA_MAX_ADCS];
145 hda_nid_t mux_nids[VIA_MAX_ADCS];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200146 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800147 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800148
149 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200150 bool dyn_adc_switch;
151 int num_inputs;
152 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200153 unsigned int cur_mux[VIA_MAX_ADCS];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800154
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200155 /* dynamic ADC switching */
156 hda_nid_t cur_adc;
157 unsigned int cur_adc_stream_tag;
158 unsigned int cur_adc_format;
159
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800160 /* PCM information */
161 struct hda_pcm pcm_rec[3];
162
163 /* dynamic controls, init_verbs and input_mux */
164 struct auto_pin_cfg autocfg;
165 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800166 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
167
168 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800169 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800170 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200171 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800172 enum VIA_HDA_CODEC codec_type;
173
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200174 /* smart51 setup */
175 unsigned int smart51_nums;
176 hda_nid_t smart51_pins[2];
177 int smart51_idxs[2];
178 const char *smart51_labels[2];
179 unsigned int smart51_enabled;
180
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800181 /* work to check hp jack state */
182 struct hda_codec *codec;
183 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200184 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800185 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800186
187 void (*set_widgets_power_state)(struct hda_codec *codec);
188
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800189 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200190 int num_loopbacks;
191 struct hda_amp_list loopback_list[8];
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200192
193 /* bind capture-volume */
194 struct hda_bind_ctls *bind_cap_vol;
195 struct hda_bind_ctls *bind_cap_sw;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800196};
197
Lydia Wang0341ccd2011-03-22 16:25:03 +0800198static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100199static struct via_spec * via_new_spec(struct hda_codec *codec)
200{
201 struct via_spec *spec;
202
203 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
204 if (spec == NULL)
205 return NULL;
206
207 codec->spec = spec;
208 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800209 spec->codec_type = get_codec_type(codec);
210 /* VT1708BCE & VT1708S are almost same */
211 if (spec->codec_type == VT1708BCE)
212 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100213 return spec;
214}
215
Lydia Wang744ff5f2009-10-10 19:07:26 +0800216static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800217{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800218 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800219 u16 ven_id = vendor_id >> 16;
220 u16 dev_id = vendor_id & 0xffff;
221 enum VIA_HDA_CODEC codec_type;
222
223 /* get codec type */
224 if (ven_id != 0x1106)
225 codec_type = UNKNOWN;
226 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
227 codec_type = VT1708;
228 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
229 codec_type = VT1709_10CH;
230 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
231 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800232 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800233 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800234 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
235 codec_type = VT1708BCE;
236 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800237 codec_type = VT1708B_4CH;
238 else if ((dev_id & 0xfff) == 0x397
239 && (dev_id >> 12) < 8)
240 codec_type = VT1708S;
241 else if ((dev_id & 0xfff) == 0x398
242 && (dev_id >> 12) < 8)
243 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800244 else if ((dev_id & 0xfff) == 0x428
245 && (dev_id >> 12) < 8)
246 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800247 else if (dev_id == 0x0433 || dev_id == 0xa721)
248 codec_type = VT1716S;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +0800249 else if (dev_id == 0x0441 || dev_id == 0x4441)
250 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800251 else if (dev_id == 0x0438 || dev_id == 0x4438)
252 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800253 else if (dev_id == 0x0448)
254 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800255 else if (dev_id == 0x0440)
256 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800257 else if ((dev_id & 0xfff) == 0x446)
258 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800259 else
260 codec_type = UNKNOWN;
261 return codec_type;
262};
263
Lydia Wangec7e7e42011-03-24 12:43:44 +0800264#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800265#define VIA_HP_EVENT 0x01
266#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200267#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800268
Joseph Chanc577b8a2006-11-29 15:29:40 +0100269enum {
270 VIA_CTL_WIDGET_VOL,
271 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800272 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100273};
274
Takashi Iwaiada509e2011-06-20 15:40:19 +0200275static void analog_low_current_mode(struct hda_codec *codec);
276static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800277
278static void vt1708_start_hp_work(struct via_spec *spec)
279{
280 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
281 return;
282 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200283 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800284 if (!delayed_work_pending(&spec->vt1708_hp_work))
285 schedule_delayed_work(&spec->vt1708_hp_work,
286 msecs_to_jiffies(100));
287}
288
289static void vt1708_stop_hp_work(struct via_spec *spec)
290{
291 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
292 return;
293 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
294 && !is_aa_path_mute(spec->codec))
295 return;
296 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200297 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100298 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800299}
Lydia Wangf5271102009-10-10 19:07:35 +0800300
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800301static void set_widgets_power_state(struct hda_codec *codec)
302{
303 struct via_spec *spec = codec->spec;
304 if (spec->set_widgets_power_state)
305 spec->set_widgets_power_state(codec);
306}
Lydia Wang25eaba22009-10-10 19:08:43 +0800307
Lydia Wangf5271102009-10-10 19:07:35 +0800308static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
309 struct snd_ctl_elem_value *ucontrol)
310{
311 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
312 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
313
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800314 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200315 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800316 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
317 if (is_aa_path_mute(codec))
318 vt1708_start_hp_work(codec->spec);
319 else
320 vt1708_stop_hp_work(codec->spec);
321 }
Lydia Wangf5271102009-10-10 19:07:35 +0800322 return change;
323}
324
325/* modify .put = snd_hda_mixer_amp_switch_put */
326#define ANALOG_INPUT_MUTE \
327 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
328 .name = NULL, \
329 .index = 0, \
330 .info = snd_hda_mixer_amp_switch_info, \
331 .get = snd_hda_mixer_amp_switch_get, \
332 .put = analog_input_switch_put, \
333 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
334
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200335static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100336 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
337 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800338 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100339};
340
Lydia Wangab6734e2009-10-10 19:08:46 +0800341
Joseph Chanc577b8a2006-11-29 15:29:40 +0100342/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200343static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
344 const struct snd_kcontrol_new *tmpl,
345 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100346{
347 struct snd_kcontrol_new *knew;
348
Takashi Iwai603c4012008-07-30 15:01:44 +0200349 snd_array_init(&spec->kctls, sizeof(*knew), 32);
350 knew = snd_array_new(&spec->kctls);
351 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200352 return NULL;
353 *knew = *tmpl;
354 if (!name)
355 name = tmpl->name;
356 if (name) {
357 knew->name = kstrdup(name, GFP_KERNEL);
358 if (!knew->name)
359 return NULL;
360 }
361 return knew;
362}
363
364static int __via_add_control(struct via_spec *spec, int type, const char *name,
365 int idx, unsigned long val)
366{
367 struct snd_kcontrol_new *knew;
368
369 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
370 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100371 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200372 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100373 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100374 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100375 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100376 return 0;
377}
378
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200379#define via_add_control(spec, type, name, val) \
380 __via_add_control(spec, type, name, 0, val)
381
Takashi Iwai291c9e32011-06-17 16:15:26 +0200382#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100383
Takashi Iwai603c4012008-07-30 15:01:44 +0200384static void via_free_kctls(struct hda_codec *codec)
385{
386 struct via_spec *spec = codec->spec;
387
388 if (spec->kctls.list) {
389 struct snd_kcontrol_new *kctl = spec->kctls.list;
390 int i;
391 for (i = 0; i < spec->kctls.used; i++)
392 kfree(kctl[i].name);
393 }
394 snd_array_free(&spec->kctls);
395}
396
Joseph Chanc577b8a2006-11-29 15:29:40 +0100397/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800398static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200399 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100400{
401 char name[32];
402 int err;
403
404 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200405 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100406 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
407 if (err < 0)
408 return err;
409 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200410 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100411 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
412 if (err < 0)
413 return err;
414 return 0;
415}
416
Takashi Iwai5d417622011-06-20 11:32:27 +0200417#define get_connection_index(codec, mux, nid) \
Takashi Iwai8d087c72011-06-28 12:45:47 +0200418 snd_hda_get_conn_index(codec, mux, nid, 0)
Takashi Iwai5d417622011-06-20 11:32:27 +0200419
Takashi Iwai8df2a312011-06-21 11:48:29 +0200420static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
421 unsigned int mask)
422{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200423 unsigned int caps;
424 if (!nid)
425 return false;
426 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200427 if (dir == HDA_INPUT)
428 caps &= AC_WCAP_IN_AMP;
429 else
430 caps &= AC_WCAP_OUT_AMP;
431 if (!caps)
432 return false;
433 if (query_amp_caps(codec, nid, dir) & mask)
434 return true;
435 return false;
436}
437
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200438#define have_mute(codec, nid, dir) \
439 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200440
Lydia Wangd69607b2011-07-08 14:02:52 +0800441static bool is_node_in_path(struct nid_path *path, hda_nid_t nid)
442{
443 int i;
444 if (!nid)
445 return false;
446 for (i = 0; i < path->depth; i++) {
447 if (path->path[i] == nid)
448 return true;
449 }
450 return false;
451}
452
453/* enable/disable the output-route mixers */
454static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
455 hda_nid_t mix_nid, int aa_mix_idx, bool enable)
456{
457 int i, num, val;
458 bool hp_path, front_path;
459 struct via_spec *spec = codec->spec;
460
461 if (!path)
462 return;
463 num = snd_hda_get_conn_list(codec, mix_nid, NULL);
464 hp_path = is_node_in_path(path, spec->hp_dac_nid);
465 front_path = is_node_in_path(path, spec->multiout.dac_nids[0]);
466
467 for (i = 0; i < num; i++) {
468 if (i == aa_mix_idx) {
469 if (hp_path)
470 val = enable ? AMP_IN_MUTE(i) :
471 AMP_IN_UNMUTE(i);
472 else if (front_path)
473 val = AMP_IN_UNMUTE(i);
474 else
475 val = AMP_IN_MUTE(i);
476 } else {
477 if (hp_path)
478 val = enable ? AMP_IN_UNMUTE(i) :
479 AMP_IN_MUTE(i);
480 else if (front_path)
481 val = AMP_IN_MUTE(i);
482 else
483 val = AMP_IN_UNMUTE(i);
484 }
485 snd_hda_codec_write(codec, mix_nid, 0,
486 AC_VERB_SET_AMP_GAIN_MUTE, val);
487 }
488}
489
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200490/* enable/disable the output-route */
491static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
492 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200493{
Lydia Wangd69607b2011-07-08 14:02:52 +0800494 int i, val;
495 struct via_spec *spec = codec->spec;
496 hda_nid_t aa_mix_nid = spec->aa_mix_nid;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200497 for (i = 0; i < path->depth; i++) {
498 hda_nid_t src, dst;
499 int idx = path->idx[i];
500 src = path->path[i];
501 if (i < path->depth - 1)
502 dst = path->path[i + 1];
503 else
504 dst = 0;
505 if (enable && path->multi[i])
506 snd_hda_codec_write(codec, dst, 0,
507 AC_VERB_SET_CONNECT_SEL, idx);
Lydia Wangb89596a2011-07-04 17:01:33 +0800508 if (!force
509 && get_wcaps_type(get_wcaps(codec, src)) == AC_WID_AUD_OUT
510 && get_wcaps_type(get_wcaps(codec, dst)) == AC_WID_AUD_MIX)
Lydia Wange5e14682011-07-01 10:55:07 +0800511 continue;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200512 if (have_mute(codec, dst, HDA_INPUT)) {
Lydia Wangd69607b2011-07-08 14:02:52 +0800513 if (dst == aa_mix_nid) {
514 val = enable ? AMP_IN_UNMUTE(idx) :
515 AMP_IN_MUTE(idx);
516 snd_hda_codec_write(codec, dst, 0,
517 AC_VERB_SET_AMP_GAIN_MUTE, val);
518 } else {
519 idx = get_connection_index(codec, dst,
520 aa_mix_nid);
521 if (idx >= 0) {
522 activate_output_mix(codec, path,
523 dst, idx, enable);
524 }
525 }
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200526 }
527 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
528 continue;
529 if (have_mute(codec, src, HDA_OUTPUT)) {
530 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
531 snd_hda_codec_write(codec, src, 0,
532 AC_VERB_SET_AMP_GAIN_MUTE, val);
533 }
534 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200535}
536
537/* set the given pin as output */
538static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
539 int pin_type)
540{
541 if (!pin)
542 return;
543 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
544 pin_type);
545 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
546 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200547 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100548}
549
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200550static void via_auto_init_output(struct hda_codec *codec,
551 struct nid_path *path, int pin_type,
Takashi Iwaibac4b922011-07-04 17:35:51 +0200552 bool with_aa_mix, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200553{
554 struct via_spec *spec = codec->spec;
555 unsigned int caps;
Lydia Wangd69607b2011-07-08 14:02:52 +0800556 hda_nid_t pin;
Takashi Iwai5d417622011-06-20 11:32:27 +0200557
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200558 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200559 return;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200560 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200561
562 init_output_pin(codec, pin, pin_type);
563 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
564 if (caps & AC_AMPCAP_MUTE) {
565 unsigned int val;
566 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
567 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
568 AMP_OUT_MUTE | val);
569 }
570
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200571 /* initialize the AA-path */
572 if (!spec->aa_mix_nid)
573 return;
Lydia Wangd69607b2011-07-08 14:02:52 +0800574 activate_output_path(codec, path, true, force);
Takashi Iwai5d417622011-06-20 11:32:27 +0200575}
576
Joseph Chanc577b8a2006-11-29 15:29:40 +0100577static void via_auto_init_multi_out(struct hda_codec *codec)
578{
579 struct via_spec *spec = codec->spec;
580 int i;
581
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200582 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwaibac4b922011-07-04 17:35:51 +0200583 /* enable aa-mute only for the front channel */
584 via_auto_init_output(codec, &spec->out_path[i], PIN_OUT,
585 i == 0, true);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100586}
587
588static void via_auto_init_hp_out(struct hda_codec *codec)
589{
590 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100591
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200592 if (!spec->hp_dac_nid) {
Takashi Iwaibac4b922011-07-04 17:35:51 +0200593 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP,
594 true, true);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200595 return;
596 }
597 if (spec->hp_independent_mode) {
598 activate_output_path(codec, &spec->hp_dep_path, false, false);
Takashi Iwaibac4b922011-07-04 17:35:51 +0200599 via_auto_init_output(codec, &spec->hp_path, PIN_HP,
600 true, true);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200601 } else {
602 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwaibac4b922011-07-04 17:35:51 +0200603 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP,
604 true, true);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200605 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100606}
607
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200608static void via_auto_init_speaker_out(struct hda_codec *codec)
609{
610 struct via_spec *spec = codec->spec;
611
612 if (spec->autocfg.speaker_outs)
Takashi Iwaibac4b922011-07-04 17:35:51 +0200613 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT,
614 true, true);
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200615}
616
Takashi Iwaif4a78282011-06-17 18:46:48 +0200617static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200618static void via_hp_automute(struct hda_codec *codec);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200619
Joseph Chanc577b8a2006-11-29 15:29:40 +0100620static void via_auto_init_analog_input(struct hda_codec *codec)
621{
622 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200623 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200624 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200625 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200626 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100627
Takashi Iwai096a8852011-06-20 12:09:02 +0200628 /* init ADCs */
629 for (i = 0; i < spec->num_adc_nids; i++) {
630 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
631 AC_VERB_SET_AMP_GAIN_MUTE,
632 AMP_IN_UNMUTE(0));
633 }
634
635 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200636 for (i = 0; i < cfg->num_inputs; i++) {
637 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200638 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200639 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100640 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200641 ctl = PIN_VREF50;
642 else
643 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100644 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200645 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100646 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200647
648 /* init input-src */
649 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200650 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
651 if (spec->mux_nids[adc_idx]) {
652 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
653 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
654 AC_VERB_SET_CONNECT_SEL,
655 mux_idx);
656 }
657 if (spec->dyn_adc_switch)
658 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200659 }
660
661 /* init aa-mixer */
662 if (!spec->aa_mix_nid)
663 return;
664 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
665 ARRAY_SIZE(conn));
666 for (i = 0; i < num_conns; i++) {
667 unsigned int caps = get_wcaps(codec, conn[i]);
668 if (get_wcaps_type(caps) == AC_WID_PIN)
669 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
670 AC_VERB_SET_AMP_GAIN_MUTE,
671 AMP_IN_MUTE(i));
672 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100673}
Lydia Wangf5271102009-10-10 19:07:35 +0800674
675static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
676 unsigned int *affected_parm)
677{
678 unsigned parm;
679 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
680 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
681 >> AC_DEFCFG_MISC_SHIFT
682 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800683 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200684 unsigned present = 0;
685
686 no_presence |= spec->no_pin_power_ctl;
687 if (!no_presence)
688 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200689 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800690 || ((no_presence || present)
691 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800692 *affected_parm = AC_PWRST_D0; /* if it's connected */
693 parm = AC_PWRST_D0;
694 } else
695 parm = AC_PWRST_D3;
696
697 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
698}
699
Takashi Iwai24088a52011-06-17 16:59:21 +0200700static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
701 struct snd_ctl_elem_info *uinfo)
702{
703 static const char * const texts[] = {
704 "Disabled", "Enabled"
705 };
706
707 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
708 uinfo->count = 1;
709 uinfo->value.enumerated.items = 2;
710 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
711 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
712 strcpy(uinfo->value.enumerated.name,
713 texts[uinfo->value.enumerated.item]);
714 return 0;
715}
716
717static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
718 struct snd_ctl_elem_value *ucontrol)
719{
720 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
721 struct via_spec *spec = codec->spec;
722 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
723 return 0;
724}
725
726static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
727 struct snd_ctl_elem_value *ucontrol)
728{
729 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
730 struct via_spec *spec = codec->spec;
731 unsigned int val = !ucontrol->value.enumerated.item[0];
732
733 if (val == spec->no_pin_power_ctl)
734 return 0;
735 spec->no_pin_power_ctl = val;
736 set_widgets_power_state(codec);
737 return 1;
738}
739
740static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
741 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
742 .name = "Dynamic Power-Control",
743 .info = via_pin_power_ctl_info,
744 .get = via_pin_power_ctl_get,
745 .put = via_pin_power_ctl_put,
746};
747
748
Harald Welte0aa62ae2008-09-09 15:58:27 +0800749static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
750 struct snd_ctl_elem_info *uinfo)
751{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200752 static const char * const texts[] = { "OFF", "ON" };
753
754 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
755 uinfo->count = 1;
756 uinfo->value.enumerated.items = 2;
757 if (uinfo->value.enumerated.item >= 2)
758 uinfo->value.enumerated.item = 1;
759 strcpy(uinfo->value.enumerated.name,
760 texts[uinfo->value.enumerated.item]);
761 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800762}
763
764static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
765 struct snd_ctl_elem_value *ucontrol)
766{
767 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800768 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800769
Takashi Iwaiece8d042011-06-19 16:24:21 +0200770 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800771 return 0;
772}
773
Harald Welte0aa62ae2008-09-09 15:58:27 +0800774static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
775 struct snd_ctl_elem_value *ucontrol)
776{
777 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
778 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +0200779 int cur;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200780
Takashi Iwai25250502011-06-30 17:24:47 +0200781 /* no independent-hp status change during PCM playback is running */
782 if (spec->num_active_streams)
783 return -EBUSY;
784
785 cur = !!ucontrol->value.enumerated.item[0];
786 if (spec->hp_independent_mode == cur)
787 return 0;
788 spec->hp_independent_mode = cur;
789 if (cur) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200790 activate_output_path(codec, &spec->hp_dep_path, false, false);
791 activate_output_path(codec, &spec->hp_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200792 if (spec->hp_indep_shared)
793 activate_output_path(codec, &spec->out_path[HDA_SIDE],
794 false, false);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200795 } else {
796 activate_output_path(codec, &spec->hp_path, false, false);
797 activate_output_path(codec, &spec->hp_dep_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200798 if (spec->hp_indep_shared)
799 activate_output_path(codec, &spec->out_path[HDA_SIDE],
800 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200801 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800802
Lydia Wangce0e5a92011-03-22 16:22:37 +0800803 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800804 set_widgets_power_state(codec);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200805 via_hp_automute(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200806 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800807}
808
Takashi Iwaiece8d042011-06-19 16:24:21 +0200809static const struct snd_kcontrol_new via_hp_mixer = {
810 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
811 .name = "Independent HP",
812 .info = via_independent_hp_info,
813 .get = via_independent_hp_get,
814 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800815};
816
Takashi Iwai3d83e572010-04-14 14:36:23 +0200817static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100818{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200819 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100820 struct snd_kcontrol_new *knew;
821 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100822
Takashi Iwaiece8d042011-06-19 16:24:21 +0200823 nid = spec->autocfg.hp_pins[0];
824 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200825 if (knew == NULL)
826 return -ENOMEM;
827
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100828 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100829
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100830 return 0;
831}
832
Lydia Wang1564b282009-10-10 19:07:52 +0800833static void notify_aa_path_ctls(struct hda_codec *codec)
834{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200835 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800836 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800837
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200838 for (i = 0; i < spec->smart51_nums; i++) {
839 struct snd_kcontrol *ctl;
840 struct snd_ctl_elem_id id;
841 memset(&id, 0, sizeof(id));
842 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
843 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800844 ctl = snd_hda_find_mixer_ctl(codec, id.name);
845 if (ctl)
846 snd_ctl_notify(codec->bus->card,
847 SNDRV_CTL_EVENT_MASK_VALUE,
848 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800849 }
850}
851
852static void mute_aa_path(struct hda_codec *codec, int mute)
853{
854 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200855 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800856 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200857
Lydia Wang1564b282009-10-10 19:07:52 +0800858 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200859 for (i = 0; i < spec->smart51_nums; i++) {
860 if (spec->smart51_idxs[i] < 0)
861 continue;
862 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
863 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800864 HDA_AMP_MUTE, val);
865 }
866}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200867
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200868static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
869{
870 struct via_spec *spec = codec->spec;
871 int i;
872
873 for (i = 0; i < spec->smart51_nums; i++)
874 if (spec->smart51_pins[i] == pin)
875 return true;
876 return false;
877}
878
Lydia Wang1564b282009-10-10 19:07:52 +0800879static int via_smart51_get(struct snd_kcontrol *kcontrol,
880 struct snd_ctl_elem_value *ucontrol)
881{
882 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
883 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800884
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200885 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800886 return 0;
887}
888
889static int via_smart51_put(struct snd_kcontrol *kcontrol,
890 struct snd_ctl_elem_value *ucontrol)
891{
892 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
893 struct via_spec *spec = codec->spec;
894 int out_in = *ucontrol->value.integer.value
895 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800896 int i;
897
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200898 for (i = 0; i < spec->smart51_nums; i++) {
899 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200900 unsigned int parm;
901
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200902 parm = snd_hda_codec_read(codec, nid, 0,
903 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
904 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
905 parm |= out_in;
906 snd_hda_codec_write(codec, nid, 0,
907 AC_VERB_SET_PIN_WIDGET_CONTROL,
908 parm);
909 if (out_in == AC_PINCTL_OUT_EN) {
910 mute_aa_path(codec, 1);
911 notify_aa_path_ctls(codec);
912 }
Lydia Wang1564b282009-10-10 19:07:52 +0800913 }
914 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800915 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800916 return 1;
917}
918
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200919static const struct snd_kcontrol_new via_smart51_mixer = {
920 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
921 .name = "Smart 5.1",
922 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200923 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200924 .get = via_smart51_get,
925 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800926};
927
Takashi Iwaif4a78282011-06-17 18:46:48 +0200928static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100929{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200930 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100931
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200932 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800933 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200934 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100935 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100936 return 0;
937}
938
Takashi Iwaiada509e2011-06-20 15:40:19 +0200939/* check AA path's mute status */
940static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800941{
Lydia Wangf5271102009-10-10 19:07:35 +0800942 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200943 const struct hda_amp_list *p;
944 int i, ch, v;
945
946 for (i = 0; i < spec->num_loopbacks; i++) {
947 p = &spec->loopback_list[i];
948 for (ch = 0; ch < 2; ch++) {
949 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
950 p->idx);
951 if (!(v & HDA_AMP_MUTE) && v > 0)
952 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800953 }
954 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200955 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800956}
957
958/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200959static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800960{
961 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200962 bool enable;
963 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800964
Takashi Iwaiada509e2011-06-20 15:40:19 +0200965 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800966
967 /* decide low current mode's verb & parameter */
968 switch (spec->codec_type) {
969 case VT1708B_8CH:
970 case VT1708B_4CH:
971 verb = 0xf70;
972 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
973 break;
974 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800975 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800976 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800977 verb = 0xf73;
978 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
979 break;
980 case VT1702:
981 verb = 0xf73;
982 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
983 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800984 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800985 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800986 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800987 verb = 0xf93;
988 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
989 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800990 default:
991 return; /* other codecs are not supported */
992 }
993 /* send verb */
994 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
995}
996
Joseph Chanc577b8a2006-11-29 15:29:40 +0100997/*
998 * generic initialization of ADC, input mixers and output mixers
999 */
Takashi Iwai096a8852011-06-20 12:09:02 +02001000static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001001 /* power down jack detect function */
1002 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001003 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001004};
1005
Takashi Iwaiada509e2011-06-20 15:40:19 +02001006static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001007{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001008 struct via_spec *spec = codec->spec;
1009
1010 if (active)
1011 spec->num_active_streams++;
1012 else
1013 spec->num_active_streams--;
1014 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001015}
1016
Takashi Iwaiece8d042011-06-19 16:24:21 +02001017static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001018 struct hda_codec *codec,
1019 struct snd_pcm_substream *substream)
1020{
1021 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001022 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001023 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001024
Takashi Iwai25250502011-06-30 17:24:47 +02001025 spec->multiout.hp_nid = 0;
1026 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
1027 if (!spec->hp_independent_mode) {
1028 if (!spec->hp_indep_shared)
1029 spec->multiout.hp_nid = spec->hp_dac_nid;
1030 } else {
1031 if (spec->hp_indep_shared)
1032 spec->multiout.num_dacs = cfg->line_outs - 1;
1033 }
1034 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001035 set_stream_active(codec, true);
1036 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1037 hinfo);
1038 if (err < 0) {
1039 spec->multiout.hp_nid = 0;
1040 set_stream_active(codec, false);
1041 return err;
1042 }
1043 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001044}
1045
Takashi Iwaiece8d042011-06-19 16:24:21 +02001046static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001047 struct hda_codec *codec,
1048 struct snd_pcm_substream *substream)
1049{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001050 struct via_spec *spec = codec->spec;
1051
1052 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001053 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001054 return 0;
1055}
1056
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001057static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1058 struct hda_codec *codec,
1059 struct snd_pcm_substream *substream)
1060{
1061 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001062
Takashi Iwaiece8d042011-06-19 16:24:21 +02001063 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001064 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001065 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1066 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001067 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001068 return 0;
1069}
1070
1071static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1072 struct hda_codec *codec,
1073 struct snd_pcm_substream *substream)
1074{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001075 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001076 return 0;
1077}
1078
1079static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1080 struct hda_codec *codec,
1081 unsigned int stream_tag,
1082 unsigned int format,
1083 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001084{
1085 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001086
Takashi Iwaiece8d042011-06-19 16:24:21 +02001087 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1088 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001089 vt1708_start_hp_work(spec);
1090 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001091}
1092
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001093static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1094 struct hda_codec *codec,
1095 unsigned int stream_tag,
1096 unsigned int format,
1097 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001098{
1099 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001100
Takashi Iwaiece8d042011-06-19 16:24:21 +02001101 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1102 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001103 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001104 return 0;
1105}
1106
1107static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1108 struct hda_codec *codec,
1109 struct snd_pcm_substream *substream)
1110{
1111 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001112
Takashi Iwaiece8d042011-06-19 16:24:21 +02001113 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001114 vt1708_stop_hp_work(spec);
1115 return 0;
1116}
1117
1118static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1119 struct hda_codec *codec,
1120 struct snd_pcm_substream *substream)
1121{
1122 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001123
Takashi Iwaiece8d042011-06-19 16:24:21 +02001124 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001125 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001126 return 0;
1127}
1128
Joseph Chanc577b8a2006-11-29 15:29:40 +01001129/*
1130 * Digital out
1131 */
1132static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1133 struct hda_codec *codec,
1134 struct snd_pcm_substream *substream)
1135{
1136 struct via_spec *spec = codec->spec;
1137 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1138}
1139
1140static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1141 struct hda_codec *codec,
1142 struct snd_pcm_substream *substream)
1143{
1144 struct via_spec *spec = codec->spec;
1145 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1146}
1147
Harald Welte5691ec72008-09-15 22:42:26 +08001148static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001149 struct hda_codec *codec,
1150 unsigned int stream_tag,
1151 unsigned int format,
1152 struct snd_pcm_substream *substream)
1153{
1154 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001155 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1156 stream_tag, format, substream);
1157}
Harald Welte5691ec72008-09-15 22:42:26 +08001158
Takashi Iwai9da29272009-05-07 16:31:14 +02001159static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1160 struct hda_codec *codec,
1161 struct snd_pcm_substream *substream)
1162{
1163 struct via_spec *spec = codec->spec;
1164 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001165 return 0;
1166}
1167
Joseph Chanc577b8a2006-11-29 15:29:40 +01001168/*
1169 * Analog capture
1170 */
1171static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1172 struct hda_codec *codec,
1173 unsigned int stream_tag,
1174 unsigned int format,
1175 struct snd_pcm_substream *substream)
1176{
1177 struct via_spec *spec = codec->spec;
1178
1179 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1180 stream_tag, 0, format);
1181 return 0;
1182}
1183
1184static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1185 struct hda_codec *codec,
1186 struct snd_pcm_substream *substream)
1187{
1188 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001189 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001190 return 0;
1191}
1192
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001193/* analog capture with dynamic ADC switching */
1194static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1195 struct hda_codec *codec,
1196 unsigned int stream_tag,
1197 unsigned int format,
1198 struct snd_pcm_substream *substream)
1199{
1200 struct via_spec *spec = codec->spec;
1201 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1202
1203 spec->cur_adc = spec->adc_nids[adc_idx];
1204 spec->cur_adc_stream_tag = stream_tag;
1205 spec->cur_adc_format = format;
1206 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
1207 return 0;
1208}
1209
1210static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1211 struct hda_codec *codec,
1212 struct snd_pcm_substream *substream)
1213{
1214 struct via_spec *spec = codec->spec;
1215
1216 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1217 spec->cur_adc = 0;
1218 return 0;
1219}
1220
1221/* re-setup the stream if running; called from input-src put */
1222static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1223{
1224 struct via_spec *spec = codec->spec;
1225 int adc_idx = spec->inputs[cur].adc_idx;
1226 hda_nid_t adc = spec->adc_nids[adc_idx];
1227
1228 if (spec->cur_adc && spec->cur_adc != adc) {
1229 /* stream is running, let's swap the current ADC */
1230 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1231 spec->cur_adc = adc;
1232 snd_hda_codec_setup_stream(codec, adc,
1233 spec->cur_adc_stream_tag, 0,
1234 spec->cur_adc_format);
1235 return true;
1236 }
1237 return false;
1238}
1239
Takashi Iwai9af74212011-06-18 16:17:45 +02001240static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001241 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001242 .channels_min = 2,
1243 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001244 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001245 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001246 .open = via_playback_multi_pcm_open,
1247 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001248 .prepare = via_playback_multi_pcm_prepare,
1249 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001250 },
1251};
1252
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001253static const struct hda_pcm_stream via_pcm_hp_playback = {
1254 .substreams = 1,
1255 .channels_min = 2,
1256 .channels_max = 2,
1257 /* NID is set in via_build_pcms */
1258 .ops = {
1259 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001260 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001261 .prepare = via_playback_hp_pcm_prepare,
1262 .cleanup = via_playback_hp_pcm_cleanup
1263 },
1264};
1265
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001266static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001267 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001268 .channels_min = 2,
1269 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001270 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001271 /* We got noisy outputs on the right channel on VT1708 when
1272 * 24bit samples are used. Until any workaround is found,
1273 * disable the 24bit format, so far.
1274 */
1275 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1276 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001277 .open = via_playback_multi_pcm_open,
1278 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001279 .prepare = via_playback_multi_pcm_prepare,
1280 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001281 },
1282};
1283
Takashi Iwai9af74212011-06-18 16:17:45 +02001284static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001285 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001286 .channels_min = 2,
1287 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001288 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001289 .ops = {
1290 .prepare = via_capture_pcm_prepare,
1291 .cleanup = via_capture_pcm_cleanup
1292 },
1293};
1294
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001295static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1296 .substreams = 1,
1297 .channels_min = 2,
1298 .channels_max = 2,
1299 /* NID is set in via_build_pcms */
1300 .ops = {
1301 .prepare = via_dyn_adc_capture_pcm_prepare,
1302 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1303 },
1304};
1305
Takashi Iwai9af74212011-06-18 16:17:45 +02001306static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001307 .substreams = 1,
1308 .channels_min = 2,
1309 .channels_max = 2,
1310 /* NID is set in via_build_pcms */
1311 .ops = {
1312 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001313 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001314 .prepare = via_dig_playback_pcm_prepare,
1315 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001316 },
1317};
1318
Takashi Iwai9af74212011-06-18 16:17:45 +02001319static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001320 .substreams = 1,
1321 .channels_min = 2,
1322 .channels_max = 2,
1323};
1324
Takashi Iwai370bafb2011-06-20 12:47:45 +02001325/*
1326 * slave controls for virtual master
1327 */
1328static const char * const via_slave_vols[] = {
1329 "Front Playback Volume",
1330 "Surround Playback Volume",
1331 "Center Playback Volume",
1332 "LFE Playback Volume",
1333 "Side Playback Volume",
1334 "Headphone Playback Volume",
1335 "Speaker Playback Volume",
1336 NULL,
1337};
1338
1339static const char * const via_slave_sws[] = {
1340 "Front Playback Switch",
1341 "Surround Playback Switch",
1342 "Center Playback Switch",
1343 "LFE Playback Switch",
1344 "Side Playback Switch",
1345 "Headphone Playback Switch",
1346 "Speaker Playback Switch",
1347 NULL,
1348};
1349
Joseph Chanc577b8a2006-11-29 15:29:40 +01001350static int via_build_controls(struct hda_codec *codec)
1351{
1352 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001353 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001354 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001355
Takashi Iwai24088a52011-06-17 16:59:21 +02001356 if (spec->set_widgets_power_state)
1357 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1358 return -ENOMEM;
1359
Joseph Chanc577b8a2006-11-29 15:29:40 +01001360 for (i = 0; i < spec->num_mixers; i++) {
1361 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1362 if (err < 0)
1363 return err;
1364 }
1365
1366 if (spec->multiout.dig_out_nid) {
1367 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001368 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001369 spec->multiout.dig_out_nid);
1370 if (err < 0)
1371 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001372 err = snd_hda_create_spdif_share_sw(codec,
1373 &spec->multiout);
1374 if (err < 0)
1375 return err;
1376 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001377 }
1378 if (spec->dig_in_nid) {
1379 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1380 if (err < 0)
1381 return err;
1382 }
Lydia Wang17314372009-10-10 19:07:37 +08001383
Takashi Iwai370bafb2011-06-20 12:47:45 +02001384 /* if we have no master control, let's create it */
1385 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1386 unsigned int vmaster_tlv[4];
1387 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1388 HDA_OUTPUT, vmaster_tlv);
1389 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1390 vmaster_tlv, via_slave_vols);
1391 if (err < 0)
1392 return err;
1393 }
1394 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1395 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1396 NULL, via_slave_sws);
1397 if (err < 0)
1398 return err;
1399 }
1400
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001401 /* assign Capture Source enums to NID */
1402 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1403 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001404 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001405 if (err < 0)
1406 return err;
1407 }
1408
Lydia Wang17314372009-10-10 19:07:37 +08001409 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001410 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001411 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001412
Takashi Iwai603c4012008-07-30 15:01:44 +02001413 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001414 return 0;
1415}
1416
1417static int via_build_pcms(struct hda_codec *codec)
1418{
1419 struct via_spec *spec = codec->spec;
1420 struct hda_pcm *info = spec->pcm_rec;
1421
1422 codec->num_pcms = 1;
1423 codec->pcm_info = info;
1424
Takashi Iwai82673bc2011-06-17 16:24:21 +02001425 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1426 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001427 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001428
1429 if (!spec->stream_analog_playback)
1430 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001431 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001432 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001433 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1434 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001435 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1436 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001437
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001438 if (!spec->stream_analog_capture) {
1439 if (spec->dyn_adc_switch)
1440 spec->stream_analog_capture =
1441 &via_pcm_dyn_adc_analog_capture;
1442 else
1443 spec->stream_analog_capture = &via_pcm_analog_capture;
1444 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001445 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1446 *spec->stream_analog_capture;
1447 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001448 if (!spec->dyn_adc_switch)
1449 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1450 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001451
1452 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1453 codec->num_pcms++;
1454 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001455 snprintf(spec->stream_name_digital,
1456 sizeof(spec->stream_name_digital),
1457 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001458 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001459 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001460 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001461 if (!spec->stream_digital_playback)
1462 spec->stream_digital_playback =
1463 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001464 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001465 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001466 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1467 spec->multiout.dig_out_nid;
1468 }
1469 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001470 if (!spec->stream_digital_capture)
1471 spec->stream_digital_capture =
1472 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001473 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001474 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001475 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1476 spec->dig_in_nid;
1477 }
1478 }
1479
Takashi Iwaiece8d042011-06-19 16:24:21 +02001480 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001481 codec->num_pcms++;
1482 info++;
1483 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1484 "%s HP", codec->chip_name);
1485 info->name = spec->stream_name_hp;
1486 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1487 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001488 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001489 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001490 return 0;
1491}
1492
1493static void via_free(struct hda_codec *codec)
1494{
1495 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001496
1497 if (!spec)
1498 return;
1499
Takashi Iwai603c4012008-07-30 15:01:44 +02001500 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001501 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001502 kfree(spec->bind_cap_vol);
1503 kfree(spec->bind_cap_sw);
1504 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001505}
1506
Takashi Iwai64be2852011-06-17 16:51:39 +02001507/* mute/unmute outputs */
1508static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1509 hda_nid_t *pins, bool mute)
1510{
1511 int i;
1512 for (i = 0; i < num_pins; i++)
1513 snd_hda_codec_write(codec, pins[i], 0,
1514 AC_VERB_SET_PIN_WIDGET_CONTROL,
1515 mute ? 0 : PIN_OUT);
1516}
1517
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001518/* mute internal speaker if line-out is plugged */
1519static void via_line_automute(struct hda_codec *codec, int present)
1520{
1521 struct via_spec *spec = codec->spec;
1522
1523 if (!spec->autocfg.speaker_outs)
1524 return;
1525 if (!present)
1526 present = snd_hda_jack_detect(codec,
1527 spec->autocfg.line_out_pins[0]);
1528 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1529 spec->autocfg.speaker_pins,
1530 present);
1531}
1532
Harald Welte69e52a82008-09-09 15:57:32 +08001533/* mute internal speaker if HP is plugged */
1534static void via_hp_automute(struct hda_codec *codec)
1535{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001536 int present = 0;
Takashi Iwai6e969d92011-07-11 11:28:13 +02001537 int nums;
Harald Welte69e52a82008-09-09 15:57:32 +08001538 struct via_spec *spec = codec->spec;
1539
Takashi Iwai6e969d92011-07-11 11:28:13 +02001540 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0])
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001541 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai6e969d92011-07-11 11:28:13 +02001542
1543 if (spec->smart51_enabled)
1544 nums = spec->autocfg.line_outs + spec->smart51_nums;
1545 else
1546 nums = spec->autocfg.line_outs;
1547 toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
1548
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001549 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001550}
1551
Harald Welte69e52a82008-09-09 15:57:32 +08001552static void via_gpio_control(struct hda_codec *codec)
1553{
1554 unsigned int gpio_data;
1555 unsigned int vol_counter;
1556 unsigned int vol;
1557 unsigned int master_vol;
1558
1559 struct via_spec *spec = codec->spec;
1560
1561 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1562 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1563
1564 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1565 0xF84, 0) & 0x3F0000) >> 16;
1566
1567 vol = vol_counter & 0x1F;
1568 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1569 AC_VERB_GET_AMP_GAIN_MUTE,
1570 AC_AMP_GET_INPUT);
1571
1572 if (gpio_data == 0x02) {
1573 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001574 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1575 AC_VERB_SET_PIN_WIDGET_CONTROL,
1576 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001577 if (vol_counter & 0x20) {
1578 /* decrease volume */
1579 if (vol > master_vol)
1580 vol = master_vol;
1581 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1582 0, HDA_AMP_VOLMASK,
1583 master_vol-vol);
1584 } else {
1585 /* increase volume */
1586 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1587 HDA_AMP_VOLMASK,
1588 ((master_vol+vol) > 0x2A) ? 0x2A :
1589 (master_vol+vol));
1590 }
1591 } else if (!(gpio_data & 0x02)) {
1592 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001593 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1594 AC_VERB_SET_PIN_WIDGET_CONTROL,
1595 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001596 }
1597}
1598
1599/* unsolicited event for jack sensing */
1600static void via_unsol_event(struct hda_codec *codec,
1601 unsigned int res)
1602{
1603 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001604
Lydia Wanga34df192009-10-10 19:08:01 +08001605 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001606 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001607
1608 res &= ~VIA_JACK_EVENT;
1609
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001610 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001611 via_hp_automute(codec);
1612 else if (res == VIA_GPIO_EVENT)
1613 via_gpio_control(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001614}
1615
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001616#ifdef SND_HDA_NEEDS_RESUME
1617static int via_suspend(struct hda_codec *codec, pm_message_t state)
1618{
1619 struct via_spec *spec = codec->spec;
1620 vt1708_stop_hp_work(spec);
1621 return 0;
1622}
1623#endif
1624
Takashi Iwaicb53c622007-08-10 17:21:45 +02001625#ifdef CONFIG_SND_HDA_POWER_SAVE
1626static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1627{
1628 struct via_spec *spec = codec->spec;
1629 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1630}
1631#endif
1632
Joseph Chanc577b8a2006-11-29 15:29:40 +01001633/*
1634 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001635
1636static int via_init(struct hda_codec *codec);
1637
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001638static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001639 .build_controls = via_build_controls,
1640 .build_pcms = via_build_pcms,
1641 .init = via_init,
1642 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001643 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001644#ifdef SND_HDA_NEEDS_RESUME
1645 .suspend = via_suspend,
1646#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001647#ifdef CONFIG_SND_HDA_POWER_SAVE
1648 .check_power_status = via_check_power_status,
1649#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001650};
1651
Takashi Iwai4a796162011-06-17 17:53:38 +02001652static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001653{
Takashi Iwai4a796162011-06-17 17:53:38 +02001654 struct via_spec *spec = codec->spec;
1655 int i;
1656
1657 for (i = 0; i < spec->multiout.num_dacs; i++) {
1658 if (spec->multiout.dac_nids[i] == dac)
1659 return false;
1660 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001661 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001662 return false;
1663 return true;
1664}
1665
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001666static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001667 hda_nid_t target_dac, struct nid_path *path,
1668 int depth, int wid_type)
1669{
1670 hda_nid_t conn[8];
1671 int i, nums;
1672
1673 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1674 for (i = 0; i < nums; i++) {
1675 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1676 continue;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001677 if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
1678 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001679 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001680 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001681 return false;
1682 for (i = 0; i < nums; i++) {
1683 unsigned int type;
1684 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1685 if (type == AC_WID_AUD_OUT ||
1686 (wid_type != -1 && type != wid_type))
1687 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001688 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001689 path, depth + 1, AC_WID_AUD_SEL))
1690 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001691 }
1692 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001693
1694 found:
1695 path->path[path->depth] = conn[i];
1696 path->idx[path->depth] = i;
1697 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1698 path->multi[path->depth] = 1;
1699 path->depth++;
1700 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001701}
1702
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001703static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1704 hda_nid_t target_dac, struct nid_path *path)
1705{
1706 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1707 path->path[path->depth] = nid;
1708 path->depth++;
1709 return true;
1710 }
1711 return false;
1712}
1713
Takashi Iwai4a796162011-06-17 17:53:38 +02001714static int via_auto_fill_dac_nids(struct hda_codec *codec)
1715{
1716 struct via_spec *spec = codec->spec;
1717 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001718 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001719 hda_nid_t nid;
1720
Joseph Chanc577b8a2006-11-29 15:29:40 +01001721 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001722 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001723 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001724 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001725 if (!nid)
1726 continue;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001727 if (parse_output_path(codec, nid, 0, &spec->out_path[i])) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001728 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Lydia Wang5c9a5612011-07-08 14:03:43 +08001729 dac_num++;
1730 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001731 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001732 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001733 return 0;
1734}
1735
Takashi Iwai4a796162011-06-17 17:53:38 +02001736static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001737 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001738{
Takashi Iwai4a796162011-06-17 17:53:38 +02001739 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001740 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001741 hda_nid_t dac, pin, sel, nid;
1742 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001743
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001744 dac = check_dac ? path->path[0] : 0;
1745 pin = path->path[path->depth - 1];
1746 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001747
Takashi Iwai8df2a312011-06-21 11:48:29 +02001748 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001749 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001750 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001751 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001752 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1753 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001754 else
1755 nid = 0;
1756 if (nid) {
1757 sprintf(name, "%s Playback Volume", pfx);
1758 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001759 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001760 if (err < 0)
1761 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001762 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001763 }
1764
Takashi Iwai8df2a312011-06-21 11:48:29 +02001765 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001766 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001767 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001768 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001769 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1770 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001771 else
1772 nid = 0;
1773 if (nid) {
1774 sprintf(name, "%s Playback Switch", pfx);
1775 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1776 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1777 if (err < 0)
1778 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001779 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001780 }
1781 return 0;
1782}
1783
Takashi Iwaif4a78282011-06-17 18:46:48 +02001784static void mangle_smart51(struct hda_codec *codec)
1785{
1786 struct via_spec *spec = codec->spec;
1787 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001788 struct auto_pin_cfg_item *ins = cfg->inputs;
1789 int i, j, nums, attr;
1790 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001791
Takashi Iwai0f98c242011-06-21 12:51:33 +02001792 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1793 nums = 0;
1794 for (i = 0; i < cfg->num_inputs; i++) {
1795 unsigned int def;
1796 if (ins[i].type > AUTO_PIN_LINE_IN)
1797 continue;
1798 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1799 if (snd_hda_get_input_pin_attr(def) != attr)
1800 continue;
1801 for (j = 0; j < nums; j++)
1802 if (ins[pins[j]].type < ins[i].type) {
1803 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001804 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001805 break;
1806 }
1807 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001808 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001809 }
1810 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001811 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001812 for (i = 0; i < nums; i++) {
1813 hda_nid_t pin = ins[pins[i]].pin;
1814 spec->smart51_pins[spec->smart51_nums++] = pin;
1815 cfg->line_out_pins[cfg->line_outs++] = pin;
1816 if (cfg->line_outs == 3)
1817 break;
1818 }
1819 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001820 }
1821}
1822
Takashi Iwai4a796162011-06-17 17:53:38 +02001823/* add playback controls from the parsed DAC table */
1824static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1825{
1826 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001827 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001828 static const char * const chname[4] = {
1829 "Front", "Surround", "C/LFE", "Side"
1830 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001831 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001832 int old_line_outs;
1833
1834 /* check smart51 */
1835 old_line_outs = cfg->line_outs;
1836 if (cfg->line_outs == 1)
1837 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001838
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001839 err = via_auto_fill_dac_nids(codec);
1840 if (err < 0)
1841 return err;
1842
Lydia Wang5c9a5612011-07-08 14:03:43 +08001843 if (spec->multiout.num_dacs < 3) {
1844 spec->smart51_nums = 0;
1845 cfg->line_outs = old_line_outs;
1846 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001847 for (i = 0; i < cfg->line_outs; i++) {
1848 hda_nid_t pin, dac;
1849 pin = cfg->line_out_pins[i];
1850 dac = spec->multiout.dac_nids[i];
1851 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001852 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001853 if (i == HDA_CLFE) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001854 err = create_ch_ctls(codec, "Center", 1, true,
1855 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001856 if (err < 0)
1857 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001858 err = create_ch_ctls(codec, "LFE", 2, true,
1859 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001860 if (err < 0)
1861 return err;
1862 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001863 const char *pfx = chname[i];
1864 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1865 cfg->line_outs == 1)
1866 pfx = "Speaker";
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001867 err = create_ch_ctls(codec, pfx, 3, true,
1868 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001869 if (err < 0)
1870 return err;
1871 }
1872 }
1873
Takashi Iwai4a796162011-06-17 17:53:38 +02001874 idx = get_connection_index(codec, spec->aa_mix_nid,
1875 spec->multiout.dac_nids[0]);
Lydia Wangc4394f52011-07-04 16:54:15 +08001876 if (idx < 0 && spec->dac_mixer_idx)
1877 idx = spec->dac_mixer_idx;
Takashi Iwai4a796162011-06-17 17:53:38 +02001878 if (idx >= 0) {
1879 /* add control to mixer */
1880 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1881 "PCM Playback Volume",
1882 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1883 idx, HDA_INPUT));
1884 if (err < 0)
1885 return err;
1886 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1887 "PCM Playback Switch",
1888 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1889 idx, HDA_INPUT));
1890 if (err < 0)
1891 return err;
1892 }
1893
Takashi Iwaif4a78282011-06-17 18:46:48 +02001894 cfg->line_outs = old_line_outs;
1895
Joseph Chanc577b8a2006-11-29 15:29:40 +01001896 return 0;
1897}
1898
Takashi Iwai4a796162011-06-17 17:53:38 +02001899static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001900{
Takashi Iwai4a796162011-06-17 17:53:38 +02001901 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001902 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001903 bool check_dac;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001904 int err;
1905
1906 if (!pin)
1907 return 0;
1908
Takashi Iwai8df2a312011-06-21 11:48:29 +02001909 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001910 spec->hp_dac_nid = spec->hp_path.path[0];
Takashi Iwai25250502011-06-30 17:24:47 +02001911 else if (spec->multiout.dac_nids[HDA_SIDE] &&
1912 parse_output_path(codec, pin,
1913 spec->multiout.dac_nids[HDA_SIDE],
1914 &spec->hp_path)) {
1915 spec->hp_dac_nid = spec->hp_path.path[0];
1916 spec->hp_indep_shared = true;
Lydia Wanga2a870c2011-07-08 14:04:33 +08001917 } else if (spec->multiout.dac_nids[HDA_CLFE] &&
1918 parse_output_path(codec, pin,
1919 spec->multiout.dac_nids[HDA_CLFE],
1920 &spec->hp_path)) {
1921 spec->hp_dac_nid = spec->hp_path.path[0];
1922 spec->hp_indep_shared = true;
Takashi Iwai25250502011-06-30 17:24:47 +02001923 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001924
Takashi Iwaiece8d042011-06-19 16:24:21 +02001925 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001926 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001927 !spec->hp_dac_nid)
1928 return 0;
1929
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001930 if (spec->hp_dac_nid && !spec->hp_indep_shared) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001931 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001932 check_dac = true;
1933 } else {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001934 path = &spec->hp_dep_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001935 check_dac = false;
1936 }
1937 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001938 if (err < 0)
1939 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001940 if (spec->hp_dac_nid) {
1941 spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
1942 spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
1943 }
Harald Welte0aa62ae2008-09-09 15:58:27 +08001944
Joseph Chanc577b8a2006-11-29 15:29:40 +01001945 return 0;
1946}
1947
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001948static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1949{
1950 struct via_spec *spec = codec->spec;
1951 hda_nid_t pin, dac;
1952
1953 pin = spec->autocfg.speaker_pins[0];
1954 if (!spec->autocfg.speaker_outs || !pin)
1955 return 0;
1956
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001957 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1958 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001959 spec->multiout.extra_out_nid[0] = dac;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001960 return create_ch_ctls(codec, "Speaker", 3, true,
1961 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001962 }
1963 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001964 &spec->speaker_path))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001965 return create_ch_ctls(codec, "Speaker", 3, false,
1966 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001967
1968 return 0;
1969}
1970
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001971/* look for ADCs */
1972static int via_fill_adcs(struct hda_codec *codec)
1973{
1974 struct via_spec *spec = codec->spec;
1975 hda_nid_t nid = codec->start_nid;
1976 int i;
1977
1978 for (i = 0; i < codec->num_nodes; i++, nid++) {
1979 unsigned int wcaps = get_wcaps(codec, nid);
1980 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1981 continue;
1982 if (wcaps & AC_WCAP_DIGITAL)
1983 continue;
1984 if (!(wcaps & AC_WCAP_CONN_LIST))
1985 continue;
1986 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1987 return -ENOMEM;
1988 spec->adc_nids[spec->num_adc_nids++] = nid;
1989 }
1990 return 0;
1991}
1992
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001993/* input-src control */
1994static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
1995 struct snd_ctl_elem_info *uinfo)
1996{
1997 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1998 struct via_spec *spec = codec->spec;
1999
2000 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2001 uinfo->count = 1;
2002 uinfo->value.enumerated.items = spec->num_inputs;
2003 if (uinfo->value.enumerated.item >= spec->num_inputs)
2004 uinfo->value.enumerated.item = spec->num_inputs - 1;
2005 strcpy(uinfo->value.enumerated.name,
2006 spec->inputs[uinfo->value.enumerated.item].label);
2007 return 0;
2008}
2009
2010static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2011 struct snd_ctl_elem_value *ucontrol)
2012{
2013 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2014 struct via_spec *spec = codec->spec;
2015 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2016
2017 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2018 return 0;
2019}
2020
2021static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2022 struct snd_ctl_elem_value *ucontrol)
2023{
2024 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2025 struct via_spec *spec = codec->spec;
2026 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2027 hda_nid_t mux;
2028 int cur;
2029
2030 cur = ucontrol->value.enumerated.item[0];
2031 if (cur < 0 || cur >= spec->num_inputs)
2032 return -EINVAL;
2033 if (spec->cur_mux[idx] == cur)
2034 return 0;
2035 spec->cur_mux[idx] = cur;
2036 if (spec->dyn_adc_switch) {
2037 int adc_idx = spec->inputs[cur].adc_idx;
2038 mux = spec->mux_nids[adc_idx];
2039 via_dyn_adc_pcm_resetup(codec, cur);
2040 } else {
2041 mux = spec->mux_nids[idx];
2042 if (snd_BUG_ON(!mux))
2043 return -EINVAL;
2044 }
2045
2046 if (mux) {
2047 /* switch to D0 beofre change index */
2048 if (snd_hda_codec_read(codec, mux, 0,
2049 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2050 snd_hda_codec_write(codec, mux, 0,
2051 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2052 snd_hda_codec_write(codec, mux, 0,
2053 AC_VERB_SET_CONNECT_SEL,
2054 spec->inputs[cur].mux_idx);
2055 }
2056
2057 /* update jack power state */
2058 set_widgets_power_state(codec);
2059 return 0;
2060}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002061
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002062static const struct snd_kcontrol_new via_input_src_ctl = {
2063 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2064 /* The multiple "Capture Source" controls confuse alsamixer
2065 * So call somewhat different..
2066 */
2067 /* .name = "Capture Source", */
2068 .name = "Input Source",
2069 .info = via_mux_enum_info,
2070 .get = via_mux_enum_get,
2071 .put = via_mux_enum_put,
2072};
2073
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002074static int create_input_src_ctls(struct hda_codec *codec, int count)
2075{
2076 struct via_spec *spec = codec->spec;
2077 struct snd_kcontrol_new *knew;
2078
2079 if (spec->num_inputs <= 1 || !count)
2080 return 0; /* no need for single src */
2081
2082 knew = via_clone_control(spec, &via_input_src_ctl);
2083 if (!knew)
2084 return -ENOMEM;
2085 knew->count = count;
2086 return 0;
2087}
2088
2089/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002090static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2091{
2092 struct hda_amp_list *list;
2093
2094 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2095 return;
2096 list = spec->loopback_list + spec->num_loopbacks;
2097 list->nid = mix;
2098 list->dir = HDA_INPUT;
2099 list->idx = idx;
2100 spec->num_loopbacks++;
2101 spec->loopback.amplist = spec->loopback_list;
2102}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002103
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002104static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002105 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002106{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002107 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002108}
2109
2110/* add the input-route to the given pin */
2111static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002112{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002113 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002114 int c, idx;
2115
2116 spec->inputs[spec->num_inputs].adc_idx = -1;
2117 spec->inputs[spec->num_inputs].pin = pin;
2118 for (c = 0; c < spec->num_adc_nids; c++) {
2119 if (spec->mux_nids[c]) {
2120 idx = get_connection_index(codec, spec->mux_nids[c],
2121 pin);
2122 if (idx < 0)
2123 continue;
2124 spec->inputs[spec->num_inputs].mux_idx = idx;
2125 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002126 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002127 continue;
2128 }
2129 spec->inputs[spec->num_inputs].adc_idx = c;
2130 /* Can primary ADC satisfy all inputs? */
2131 if (!spec->dyn_adc_switch &&
2132 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2133 snd_printd(KERN_INFO
2134 "via: dynamic ADC switching enabled\n");
2135 spec->dyn_adc_switch = 1;
2136 }
2137 return true;
2138 }
2139 return false;
2140}
2141
2142static int get_mux_nids(struct hda_codec *codec);
2143
2144/* parse input-routes; fill ADCs, MUXs and input-src entries */
2145static int parse_analog_inputs(struct hda_codec *codec)
2146{
2147 struct via_spec *spec = codec->spec;
2148 const struct auto_pin_cfg *cfg = &spec->autocfg;
2149 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002150
2151 err = via_fill_adcs(codec);
2152 if (err < 0)
2153 return err;
2154 err = get_mux_nids(codec);
2155 if (err < 0)
2156 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002157
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002158 /* fill all input-routes */
2159 for (i = 0; i < cfg->num_inputs; i++) {
2160 if (add_input_route(codec, cfg->inputs[i].pin))
2161 spec->inputs[spec->num_inputs++].label =
2162 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002163 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002164
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002165 /* check for internal loopback recording */
2166 if (spec->aa_mix_nid &&
2167 add_input_route(codec, spec->aa_mix_nid))
2168 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2169
2170 return 0;
2171}
2172
2173/* create analog-loopback volume/switch controls */
2174static int create_loopback_ctls(struct hda_codec *codec)
2175{
2176 struct via_spec *spec = codec->spec;
2177 const struct auto_pin_cfg *cfg = &spec->autocfg;
2178 const char *prev_label = NULL;
2179 int type_idx = 0;
2180 int i, j, err, idx;
2181
2182 if (!spec->aa_mix_nid)
2183 return 0;
2184
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002185 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002186 hda_nid_t pin = cfg->inputs[i].pin;
2187 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2188
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002189 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002190 type_idx++;
2191 else
2192 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002193 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002194 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2195 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002196 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002197 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002198 if (err < 0)
2199 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002200 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002201 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002202
2203 /* remember the label for smart51 control */
2204 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002205 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002206 spec->smart51_idxs[j] = idx;
2207 spec->smart51_labels[j] = label;
2208 break;
2209 }
2210 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002211 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002212 return 0;
2213}
2214
2215/* create mic-boost controls (if present) */
2216static int create_mic_boost_ctls(struct hda_codec *codec)
2217{
2218 struct via_spec *spec = codec->spec;
2219 const struct auto_pin_cfg *cfg = &spec->autocfg;
2220 int i, err;
2221
2222 for (i = 0; i < cfg->num_inputs; i++) {
2223 hda_nid_t pin = cfg->inputs[i].pin;
2224 unsigned int caps;
2225 const char *label;
2226 char name[32];
2227
2228 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2229 continue;
2230 caps = query_amp_caps(codec, pin, HDA_INPUT);
2231 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2232 continue;
2233 label = hda_get_autocfg_input_label(codec, cfg, i);
2234 snprintf(name, sizeof(name), "%s Boost Volume", label);
2235 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2236 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2237 if (err < 0)
2238 return err;
2239 }
2240 return 0;
2241}
2242
2243/* create capture and input-src controls for multiple streams */
2244static int create_multi_adc_ctls(struct hda_codec *codec)
2245{
2246 struct via_spec *spec = codec->spec;
2247 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002248
2249 /* create capture mixer elements */
2250 for (i = 0; i < spec->num_adc_nids; i++) {
2251 hda_nid_t adc = spec->adc_nids[i];
2252 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2253 "Capture Volume", i,
2254 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2255 HDA_INPUT));
2256 if (err < 0)
2257 return err;
2258 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2259 "Capture Switch", i,
2260 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2261 HDA_INPUT));
2262 if (err < 0)
2263 return err;
2264 }
2265
2266 /* input-source control */
2267 for (i = 0; i < spec->num_adc_nids; i++)
2268 if (!spec->mux_nids[i])
2269 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002270 err = create_input_src_ctls(codec, i);
2271 if (err < 0)
2272 return err;
2273 return 0;
2274}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002275
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002276/* bind capture volume/switch */
2277static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2278 HDA_BIND_VOL("Capture Volume", 0);
2279static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2280 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002281
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002282static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2283 struct hda_ctl_ops *ops)
2284{
2285 struct hda_bind_ctls *ctl;
2286 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002287
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002288 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2289 if (!ctl)
2290 return -ENOMEM;
2291 ctl->ops = ops;
2292 for (i = 0; i < spec->num_adc_nids; i++)
2293 ctl->values[i] =
2294 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2295 *ctl_ret = ctl;
2296 return 0;
2297}
2298
2299/* create capture and input-src controls for dynamic ADC-switch case */
2300static int create_dyn_adc_ctls(struct hda_codec *codec)
2301{
2302 struct via_spec *spec = codec->spec;
2303 struct snd_kcontrol_new *knew;
2304 int err;
2305
2306 /* set up the bind capture ctls */
2307 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2308 if (err < 0)
2309 return err;
2310 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2311 if (err < 0)
2312 return err;
2313
2314 /* create capture mixer elements */
2315 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2316 if (!knew)
2317 return -ENOMEM;
2318 knew->private_value = (long)spec->bind_cap_vol;
2319
2320 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2321 if (!knew)
2322 return -ENOMEM;
2323 knew->private_value = (long)spec->bind_cap_sw;
2324
2325 /* input-source control */
2326 err = create_input_src_ctls(codec, 1);
2327 if (err < 0)
2328 return err;
2329 return 0;
2330}
2331
2332/* parse and create capture-related stuff */
2333static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2334{
2335 struct via_spec *spec = codec->spec;
2336 int err;
2337
2338 err = parse_analog_inputs(codec);
2339 if (err < 0)
2340 return err;
2341 if (spec->dyn_adc_switch)
2342 err = create_dyn_adc_ctls(codec);
2343 else
2344 err = create_multi_adc_ctls(codec);
2345 if (err < 0)
2346 return err;
2347 err = create_loopback_ctls(codec);
2348 if (err < 0)
2349 return err;
2350 err = create_mic_boost_ctls(codec);
2351 if (err < 0)
2352 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002353 return 0;
2354}
2355
Harald Welte76d9b0d2008-09-09 15:50:37 +08002356static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2357{
2358 unsigned int def_conf;
2359 unsigned char seqassoc;
2360
Takashi Iwai2f334f92009-02-20 14:37:42 +01002361 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002362 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2363 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002364 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2365 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2366 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2367 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002368 }
2369
2370 return;
2371}
2372
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002373static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002374 struct snd_ctl_elem_value *ucontrol)
2375{
2376 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2377 struct via_spec *spec = codec->spec;
2378
2379 if (spec->codec_type != VT1708)
2380 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002381 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002382 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002383 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002384 return 0;
2385}
2386
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002387static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002388 struct snd_ctl_elem_value *ucontrol)
2389{
2390 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2391 struct via_spec *spec = codec->spec;
2392 int change;
2393
2394 if (spec->codec_type != VT1708)
2395 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002396 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002397 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002398 == !spec->vt1708_jack_detect;
2399 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002400 mute_aa_path(codec, 1);
2401 notify_aa_path_ctls(codec);
2402 }
2403 return change;
2404}
2405
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002406static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2407 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2408 .name = "Jack Detect",
2409 .count = 1,
2410 .info = snd_ctl_boolean_mono_info,
2411 .get = vt1708_jack_detect_get,
2412 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002413};
2414
Takashi Iwai12daef62011-06-18 17:45:49 +02002415static void fill_dig_outs(struct hda_codec *codec);
2416static void fill_dig_in(struct hda_codec *codec);
2417
2418static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002419{
2420 struct via_spec *spec = codec->spec;
2421 int err;
2422
2423 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2424 if (err < 0)
2425 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002426 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002427 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002428
Takashi Iwai4a796162011-06-17 17:53:38 +02002429 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002430 if (err < 0)
2431 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002432 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002433 if (err < 0)
2434 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002435 err = via_auto_create_speaker_ctls(codec);
2436 if (err < 0)
2437 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002438 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002439 if (err < 0)
2440 return err;
2441
2442 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2443
Takashi Iwai12daef62011-06-18 17:45:49 +02002444 fill_dig_outs(codec);
2445 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002446
Takashi Iwai603c4012008-07-30 15:01:44 +02002447 if (spec->kctls.list)
2448 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002449
Joseph Chanc577b8a2006-11-29 15:29:40 +01002450
Takashi Iwai8df2a312011-06-21 11:48:29 +02002451 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002452 err = via_hp_build(codec);
2453 if (err < 0)
2454 return err;
2455 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002456
Takashi Iwaif4a78282011-06-17 18:46:48 +02002457 err = via_smart51_build(codec);
2458 if (err < 0)
2459 return err;
2460
Takashi Iwai5d417622011-06-20 11:32:27 +02002461 /* assign slave outs */
2462 if (spec->slave_dig_outs[0])
2463 codec->slave_dig_outs = spec->slave_dig_outs;
2464
Joseph Chanc577b8a2006-11-29 15:29:40 +01002465 return 1;
2466}
2467
Takashi Iwai5d417622011-06-20 11:32:27 +02002468static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002469{
Lydia Wang25eaba22009-10-10 19:08:43 +08002470 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002471 if (spec->multiout.dig_out_nid)
2472 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2473 if (spec->slave_dig_outs[0])
2474 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2475}
Lydia Wang25eaba22009-10-10 19:08:43 +08002476
Takashi Iwai5d417622011-06-20 11:32:27 +02002477static void via_auto_init_dig_in(struct hda_codec *codec)
2478{
2479 struct via_spec *spec = codec->spec;
2480 if (!spec->dig_in_nid)
2481 return;
2482 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2483 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2484}
2485
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002486/* initialize the unsolicited events */
2487static void via_auto_init_unsol_event(struct hda_codec *codec)
2488{
2489 struct via_spec *spec = codec->spec;
2490 struct auto_pin_cfg *cfg = &spec->autocfg;
2491 unsigned int ev;
2492 int i;
2493
2494 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2495 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2496 AC_VERB_SET_UNSOLICITED_ENABLE,
2497 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2498
2499 if (cfg->speaker_pins[0])
2500 ev = VIA_LINE_EVENT;
2501 else
2502 ev = 0;
2503 for (i = 0; i < cfg->line_outs; i++) {
2504 if (cfg->line_out_pins[i] &&
2505 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002506 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002507 AC_VERB_SET_UNSOLICITED_ENABLE,
2508 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2509 }
2510
2511 for (i = 0; i < cfg->num_inputs; i++) {
2512 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2513 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2514 AC_VERB_SET_UNSOLICITED_ENABLE,
2515 AC_USRSP_EN | VIA_JACK_EVENT);
2516 }
2517}
2518
Takashi Iwai5d417622011-06-20 11:32:27 +02002519static int via_init(struct hda_codec *codec)
2520{
2521 struct via_spec *spec = codec->spec;
2522 int i;
2523
2524 for (i = 0; i < spec->num_iverbs; i++)
2525 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2526
Joseph Chanc577b8a2006-11-29 15:29:40 +01002527 via_auto_init_multi_out(codec);
2528 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002529 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002530 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002531 via_auto_init_dig_outs(codec);
2532 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002533
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002534 via_auto_init_unsol_event(codec);
2535
2536 via_hp_automute(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002537
Joseph Chanc577b8a2006-11-29 15:29:40 +01002538 return 0;
2539}
2540
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002541static void vt1708_update_hp_jack_state(struct work_struct *work)
2542{
2543 struct via_spec *spec = container_of(work, struct via_spec,
2544 vt1708_hp_work.work);
2545 if (spec->codec_type != VT1708)
2546 return;
2547 /* if jack state toggled */
2548 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002549 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002550 spec->vt1708_hp_present ^= 1;
2551 via_hp_automute(spec->codec);
2552 }
2553 vt1708_start_hp_work(spec);
2554}
2555
Takashi Iwai337b9d02009-07-07 18:18:59 +02002556static int get_mux_nids(struct hda_codec *codec)
2557{
2558 struct via_spec *spec = codec->spec;
2559 hda_nid_t nid, conn[8];
2560 unsigned int type;
2561 int i, n;
2562
2563 for (i = 0; i < spec->num_adc_nids; i++) {
2564 nid = spec->adc_nids[i];
2565 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002566 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002567 if (type == AC_WID_PIN)
2568 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002569 n = snd_hda_get_connections(codec, nid, conn,
2570 ARRAY_SIZE(conn));
2571 if (n <= 0)
2572 break;
2573 if (n > 1) {
2574 spec->mux_nids[i] = nid;
2575 break;
2576 }
2577 nid = conn[0];
2578 }
2579 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002580 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002581}
2582
Joseph Chanc577b8a2006-11-29 15:29:40 +01002583static int patch_vt1708(struct hda_codec *codec)
2584{
2585 struct via_spec *spec;
2586 int err;
2587
2588 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002589 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002590 if (spec == NULL)
2591 return -ENOMEM;
2592
Takashi Iwai620e2b22011-06-17 17:19:19 +02002593 spec->aa_mix_nid = 0x17;
2594
Takashi Iwai12daef62011-06-18 17:45:49 +02002595 /* Add HP and CD pin config connect bit re-config action */
2596 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2597 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2598
Joseph Chanc577b8a2006-11-29 15:29:40 +01002599 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002600 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002601 if (err < 0) {
2602 via_free(codec);
2603 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002604 }
2605
Takashi Iwai12daef62011-06-18 17:45:49 +02002606 /* add jack detect on/off control */
2607 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2608 return -ENOMEM;
2609
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002610 /* disable 32bit format on VT1708 */
2611 if (codec->vendor_id == 0x11061708)
2612 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002613
Lydia Wange322a362011-06-29 13:52:02 +08002614 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2615
Joseph Chanc577b8a2006-11-29 15:29:40 +01002616 codec->patch_ops = via_patch_ops;
2617
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002618 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002619 return 0;
2620}
2621
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002622static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002623{
2624 struct via_spec *spec;
2625 int err;
2626
2627 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002628 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002629 if (spec == NULL)
2630 return -ENOMEM;
2631
Takashi Iwai620e2b22011-06-17 17:19:19 +02002632 spec->aa_mix_nid = 0x18;
2633
Takashi Iwai12daef62011-06-18 17:45:49 +02002634 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002635 if (err < 0) {
2636 via_free(codec);
2637 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002638 }
2639
Joseph Chanc577b8a2006-11-29 15:29:40 +01002640 codec->patch_ops = via_patch_ops;
2641
Josepch Chanf7278fd2007-12-13 16:40:40 +01002642 return 0;
2643}
2644
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002645static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2646{
2647 struct via_spec *spec = codec->spec;
2648 int imux_is_smixer;
2649 unsigned int parm;
2650 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002651 if ((spec->codec_type != VT1708B_4CH) &&
2652 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002653 is_8ch = 1;
2654
2655 /* SW0 (17h) = stereo mixer */
2656 imux_is_smixer =
2657 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2658 == ((spec->codec_type == VT1708S) ? 5 : 0));
2659 /* inputs */
2660 /* PW 1/2/5 (1ah/1bh/1eh) */
2661 parm = AC_PWRST_D3;
2662 set_pin_power_state(codec, 0x1a, &parm);
2663 set_pin_power_state(codec, 0x1b, &parm);
2664 set_pin_power_state(codec, 0x1e, &parm);
2665 if (imux_is_smixer)
2666 parm = AC_PWRST_D0;
2667 /* SW0 (17h), AIW 0/1 (13h/14h) */
2668 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2669 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2670 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2671
2672 /* outputs */
2673 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2674 parm = AC_PWRST_D3;
2675 set_pin_power_state(codec, 0x19, &parm);
2676 if (spec->smart51_enabled)
2677 set_pin_power_state(codec, 0x1b, &parm);
2678 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2679 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2680
2681 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2682 if (is_8ch) {
2683 parm = AC_PWRST_D3;
2684 set_pin_power_state(codec, 0x22, &parm);
2685 if (spec->smart51_enabled)
2686 set_pin_power_state(codec, 0x1a, &parm);
2687 snd_hda_codec_write(codec, 0x26, 0,
2688 AC_VERB_SET_POWER_STATE, parm);
2689 snd_hda_codec_write(codec, 0x24, 0,
2690 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002691 } else if (codec->vendor_id == 0x11064397) {
2692 /* PW7(23h), SW2(27h), AOW2(25h) */
2693 parm = AC_PWRST_D3;
2694 set_pin_power_state(codec, 0x23, &parm);
2695 if (spec->smart51_enabled)
2696 set_pin_power_state(codec, 0x1a, &parm);
2697 snd_hda_codec_write(codec, 0x27, 0,
2698 AC_VERB_SET_POWER_STATE, parm);
2699 snd_hda_codec_write(codec, 0x25, 0,
2700 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002701 }
2702
2703 /* PW 3/4/7 (1ch/1dh/23h) */
2704 parm = AC_PWRST_D3;
2705 /* force to D0 for internal Speaker */
2706 set_pin_power_state(codec, 0x1c, &parm);
2707 set_pin_power_state(codec, 0x1d, &parm);
2708 if (is_8ch)
2709 set_pin_power_state(codec, 0x23, &parm);
2710
2711 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2712 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2713 imux_is_smixer ? AC_PWRST_D0 : parm);
2714 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2715 if (is_8ch) {
2716 snd_hda_codec_write(codec, 0x25, 0,
2717 AC_VERB_SET_POWER_STATE, parm);
2718 snd_hda_codec_write(codec, 0x27, 0,
2719 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002720 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2721 snd_hda_codec_write(codec, 0x25, 0,
2722 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002723}
2724
Lydia Wang518bf3b2009-10-10 19:07:29 +08002725static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002726static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002727{
2728 struct via_spec *spec;
2729 int err;
2730
Lydia Wang518bf3b2009-10-10 19:07:29 +08002731 if (get_codec_type(codec) == VT1708BCE)
2732 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002733
Josepch Chanf7278fd2007-12-13 16:40:40 +01002734 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002735 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002736 if (spec == NULL)
2737 return -ENOMEM;
2738
Takashi Iwai620e2b22011-06-17 17:19:19 +02002739 spec->aa_mix_nid = 0x16;
2740
Josepch Chanf7278fd2007-12-13 16:40:40 +01002741 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002742 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002743 if (err < 0) {
2744 via_free(codec);
2745 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002746 }
2747
Josepch Chanf7278fd2007-12-13 16:40:40 +01002748 codec->patch_ops = via_patch_ops;
2749
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002750 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2751
Josepch Chanf7278fd2007-12-13 16:40:40 +01002752 return 0;
2753}
2754
Harald Welted949cac2008-09-09 15:56:01 +08002755/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002756static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002757 /* Enable Mic Boost Volume backdoor */
2758 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002759 /* don't bybass mixer */
2760 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002761 { }
2762};
2763
Takashi Iwai9da29272009-05-07 16:31:14 +02002764/* fill out digital output widgets; one for master and one for slave outputs */
2765static void fill_dig_outs(struct hda_codec *codec)
2766{
2767 struct via_spec *spec = codec->spec;
2768 int i;
2769
2770 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2771 hda_nid_t nid;
2772 int conn;
2773
2774 nid = spec->autocfg.dig_out_pins[i];
2775 if (!nid)
2776 continue;
2777 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2778 if (conn < 1)
2779 continue;
2780 if (!spec->multiout.dig_out_nid)
2781 spec->multiout.dig_out_nid = nid;
2782 else {
2783 spec->slave_dig_outs[0] = nid;
2784 break; /* at most two dig outs */
2785 }
2786 }
2787}
2788
Takashi Iwai12daef62011-06-18 17:45:49 +02002789static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002790{
2791 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002792 hda_nid_t dig_nid;
2793 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002794
Takashi Iwai12daef62011-06-18 17:45:49 +02002795 if (!spec->autocfg.dig_in_pin)
2796 return;
Harald Welted949cac2008-09-09 15:56:01 +08002797
Takashi Iwai12daef62011-06-18 17:45:49 +02002798 dig_nid = codec->start_nid;
2799 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2800 unsigned int wcaps = get_wcaps(codec, dig_nid);
2801 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2802 continue;
2803 if (!(wcaps & AC_WCAP_DIGITAL))
2804 continue;
2805 if (!(wcaps & AC_WCAP_CONN_LIST))
2806 continue;
2807 err = get_connection_index(codec, dig_nid,
2808 spec->autocfg.dig_in_pin);
2809 if (err >= 0) {
2810 spec->dig_in_nid = dig_nid;
2811 break;
2812 }
2813 }
Harald Welted949cac2008-09-09 15:56:01 +08002814}
2815
Lydia Wang6369bcf2009-10-10 19:08:31 +08002816static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2817 int offset, int num_steps, int step_size)
2818{
2819 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2820 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2821 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2822 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2823 (0 << AC_AMPCAP_MUTE_SHIFT));
2824}
2825
Harald Welted949cac2008-09-09 15:56:01 +08002826static int patch_vt1708S(struct hda_codec *codec)
2827{
2828 struct via_spec *spec;
2829 int err;
2830
2831 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002832 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002833 if (spec == NULL)
2834 return -ENOMEM;
2835
Takashi Iwai620e2b22011-06-17 17:19:19 +02002836 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002837 override_mic_boost(codec, 0x1a, 0, 3, 40);
2838 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002839
Harald Welted949cac2008-09-09 15:56:01 +08002840 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002841 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002842 if (err < 0) {
2843 via_free(codec);
2844 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002845 }
2846
Takashi Iwai096a8852011-06-20 12:09:02 +02002847 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002848
Harald Welted949cac2008-09-09 15:56:01 +08002849 codec->patch_ops = via_patch_ops;
2850
Lydia Wang518bf3b2009-10-10 19:07:29 +08002851 /* correct names for VT1708BCE */
2852 if (get_codec_type(codec) == VT1708BCE) {
2853 kfree(codec->chip_name);
2854 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2855 snprintf(codec->bus->card->mixername,
2856 sizeof(codec->bus->card->mixername),
2857 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002858 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002859 /* correct names for VT1705 */
2860 if (codec->vendor_id == 0x11064397) {
2861 kfree(codec->chip_name);
2862 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2863 snprintf(codec->bus->card->mixername,
2864 sizeof(codec->bus->card->mixername),
2865 "%s %s", codec->vendor_name, codec->chip_name);
2866 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002867 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002868 return 0;
2869}
2870
2871/* Patch for VT1702 */
2872
Takashi Iwai096a8852011-06-20 12:09:02 +02002873static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002874 /* mixer enable */
2875 {0x1, 0xF88, 0x3},
2876 /* GPIO 0~2 */
2877 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002878 { }
2879};
2880
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002881static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2882{
2883 int imux_is_smixer =
2884 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2885 unsigned int parm;
2886 /* inputs */
2887 /* PW 1/2/5 (14h/15h/18h) */
2888 parm = AC_PWRST_D3;
2889 set_pin_power_state(codec, 0x14, &parm);
2890 set_pin_power_state(codec, 0x15, &parm);
2891 set_pin_power_state(codec, 0x18, &parm);
2892 if (imux_is_smixer)
2893 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2894 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2895 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2896 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2897 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2898 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2899
2900 /* outputs */
2901 /* PW 3/4 (16h/17h) */
2902 parm = AC_PWRST_D3;
2903 set_pin_power_state(codec, 0x17, &parm);
2904 set_pin_power_state(codec, 0x16, &parm);
2905 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2906 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2907 imux_is_smixer ? AC_PWRST_D0 : parm);
2908 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2909 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2910}
2911
Harald Welted949cac2008-09-09 15:56:01 +08002912static int patch_vt1702(struct hda_codec *codec)
2913{
2914 struct via_spec *spec;
2915 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002916
2917 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002918 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002919 if (spec == NULL)
2920 return -ENOMEM;
2921
Takashi Iwai620e2b22011-06-17 17:19:19 +02002922 spec->aa_mix_nid = 0x1a;
2923
Takashi Iwai12daef62011-06-18 17:45:49 +02002924 /* limit AA path volume to 0 dB */
2925 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2926 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2927 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2928 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2929 (1 << AC_AMPCAP_MUTE_SHIFT));
2930
Harald Welted949cac2008-09-09 15:56:01 +08002931 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002932 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002933 if (err < 0) {
2934 via_free(codec);
2935 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002936 }
2937
Takashi Iwai096a8852011-06-20 12:09:02 +02002938 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002939
Harald Welted949cac2008-09-09 15:56:01 +08002940 codec->patch_ops = via_patch_ops;
2941
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002942 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002943 return 0;
2944}
2945
Lydia Wangeb7188c2009-10-10 19:08:34 +08002946/* Patch for VT1718S */
2947
Takashi Iwai096a8852011-06-20 12:09:02 +02002948static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002949 /* Enable MW0 adjust Gain 5 */
2950 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002951 /* Enable Boost Volume backdoor */
2952 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002953
Lydia Wangeb7188c2009-10-10 19:08:34 +08002954 { }
2955};
2956
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002957static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2958{
2959 struct via_spec *spec = codec->spec;
2960 int imux_is_smixer;
2961 unsigned int parm;
2962 /* MUX6 (1eh) = stereo mixer */
2963 imux_is_smixer =
2964 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2965 /* inputs */
2966 /* PW 5/6/7 (29h/2ah/2bh) */
2967 parm = AC_PWRST_D3;
2968 set_pin_power_state(codec, 0x29, &parm);
2969 set_pin_power_state(codec, 0x2a, &parm);
2970 set_pin_power_state(codec, 0x2b, &parm);
2971 if (imux_is_smixer)
2972 parm = AC_PWRST_D0;
2973 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2974 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2975 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2976 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2977 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2978
2979 /* outputs */
2980 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2981 parm = AC_PWRST_D3;
2982 set_pin_power_state(codec, 0x27, &parm);
2983 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2984 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2985
2986 /* PW2 (26h), AOW2 (ah) */
2987 parm = AC_PWRST_D3;
2988 set_pin_power_state(codec, 0x26, &parm);
2989 if (spec->smart51_enabled)
2990 set_pin_power_state(codec, 0x2b, &parm);
2991 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2992
2993 /* PW0 (24h), AOW0 (8h) */
2994 parm = AC_PWRST_D3;
2995 set_pin_power_state(codec, 0x24, &parm);
2996 if (!spec->hp_independent_mode) /* check for redirected HP */
2997 set_pin_power_state(codec, 0x28, &parm);
2998 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2999 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
3000 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
3001 imux_is_smixer ? AC_PWRST_D0 : parm);
3002
3003 /* PW1 (25h), AOW1 (9h) */
3004 parm = AC_PWRST_D3;
3005 set_pin_power_state(codec, 0x25, &parm);
3006 if (spec->smart51_enabled)
3007 set_pin_power_state(codec, 0x2a, &parm);
3008 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
3009
3010 if (spec->hp_independent_mode) {
3011 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3012 parm = AC_PWRST_D3;
3013 set_pin_power_state(codec, 0x28, &parm);
3014 snd_hda_codec_write(codec, 0x1b, 0,
3015 AC_VERB_SET_POWER_STATE, parm);
3016 snd_hda_codec_write(codec, 0x34, 0,
3017 AC_VERB_SET_POWER_STATE, parm);
3018 snd_hda_codec_write(codec, 0xc, 0,
3019 AC_VERB_SET_POWER_STATE, parm);
3020 }
3021}
3022
Lydia Wangeb7188c2009-10-10 19:08:34 +08003023static int patch_vt1718S(struct hda_codec *codec)
3024{
3025 struct via_spec *spec;
3026 int err;
3027
3028 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003029 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003030 if (spec == NULL)
3031 return -ENOMEM;
3032
Takashi Iwai620e2b22011-06-17 17:19:19 +02003033 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003034 override_mic_boost(codec, 0x2b, 0, 3, 40);
3035 override_mic_boost(codec, 0x29, 0, 3, 40);
Lydia Wangc4394f52011-07-04 16:54:15 +08003036 spec->dac_mixer_idx = 5;
Takashi Iwai620e2b22011-06-17 17:19:19 +02003037
Lydia Wangeb7188c2009-10-10 19:08:34 +08003038 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003039 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003040 if (err < 0) {
3041 via_free(codec);
3042 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003043 }
3044
Takashi Iwai096a8852011-06-20 12:09:02 +02003045 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003046
Lydia Wangeb7188c2009-10-10 19:08:34 +08003047 codec->patch_ops = via_patch_ops;
3048
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003049 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3050
Lydia Wangeb7188c2009-10-10 19:08:34 +08003051 return 0;
3052}
Lydia Wangf3db4232009-10-10 19:08:41 +08003053
3054/* Patch for VT1716S */
3055
3056static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3057 struct snd_ctl_elem_info *uinfo)
3058{
3059 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3060 uinfo->count = 1;
3061 uinfo->value.integer.min = 0;
3062 uinfo->value.integer.max = 1;
3063 return 0;
3064}
3065
3066static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3067 struct snd_ctl_elem_value *ucontrol)
3068{
3069 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3070 int index = 0;
3071
3072 index = snd_hda_codec_read(codec, 0x26, 0,
3073 AC_VERB_GET_CONNECT_SEL, 0);
3074 if (index != -1)
3075 *ucontrol->value.integer.value = index;
3076
3077 return 0;
3078}
3079
3080static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3081 struct snd_ctl_elem_value *ucontrol)
3082{
3083 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3084 struct via_spec *spec = codec->spec;
3085 int index = *ucontrol->value.integer.value;
3086
3087 snd_hda_codec_write(codec, 0x26, 0,
3088 AC_VERB_SET_CONNECT_SEL, index);
3089 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003090 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003091 return 1;
3092}
3093
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003094static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003095 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3096 {
3097 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3098 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003099 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003100 .count = 1,
3101 .info = vt1716s_dmic_info,
3102 .get = vt1716s_dmic_get,
3103 .put = vt1716s_dmic_put,
3104 },
3105 {} /* end */
3106};
3107
3108
3109/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003110static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003111 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3112 { } /* end */
3113};
3114
Takashi Iwai096a8852011-06-20 12:09:02 +02003115static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003116 /* Enable Boost Volume backdoor */
3117 {0x1, 0xf8a, 0x80},
3118 /* don't bybass mixer */
3119 {0x1, 0xf88, 0xc0},
3120 /* Enable mono output */
3121 {0x1, 0xf90, 0x08},
3122 { }
3123};
3124
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003125static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3126{
3127 struct via_spec *spec = codec->spec;
3128 int imux_is_smixer;
3129 unsigned int parm;
3130 unsigned int mono_out, present;
3131 /* SW0 (17h) = stereo mixer */
3132 imux_is_smixer =
3133 (snd_hda_codec_read(codec, 0x17, 0,
3134 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3135 /* inputs */
3136 /* PW 1/2/5 (1ah/1bh/1eh) */
3137 parm = AC_PWRST_D3;
3138 set_pin_power_state(codec, 0x1a, &parm);
3139 set_pin_power_state(codec, 0x1b, &parm);
3140 set_pin_power_state(codec, 0x1e, &parm);
3141 if (imux_is_smixer)
3142 parm = AC_PWRST_D0;
3143 /* SW0 (17h), AIW0(13h) */
3144 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3145 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3146
3147 parm = AC_PWRST_D3;
3148 set_pin_power_state(codec, 0x1e, &parm);
3149 /* PW11 (22h) */
3150 if (spec->dmic_enabled)
3151 set_pin_power_state(codec, 0x22, &parm);
3152 else
3153 snd_hda_codec_write(codec, 0x22, 0,
3154 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3155
3156 /* SW2(26h), AIW1(14h) */
3157 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3158 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3159
3160 /* outputs */
3161 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3162 parm = AC_PWRST_D3;
3163 set_pin_power_state(codec, 0x19, &parm);
3164 /* Smart 5.1 PW2(1bh) */
3165 if (spec->smart51_enabled)
3166 set_pin_power_state(codec, 0x1b, &parm);
3167 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3168 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3169
3170 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3171 parm = AC_PWRST_D3;
3172 set_pin_power_state(codec, 0x23, &parm);
3173 /* Smart 5.1 PW1(1ah) */
3174 if (spec->smart51_enabled)
3175 set_pin_power_state(codec, 0x1a, &parm);
3176 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3177
3178 /* Smart 5.1 PW5(1eh) */
3179 if (spec->smart51_enabled)
3180 set_pin_power_state(codec, 0x1e, &parm);
3181 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3182
3183 /* Mono out */
3184 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3185 present = snd_hda_jack_detect(codec, 0x1c);
3186
3187 if (present)
3188 mono_out = 0;
3189 else {
3190 present = snd_hda_jack_detect(codec, 0x1d);
3191 if (!spec->hp_independent_mode && present)
3192 mono_out = 0;
3193 else
3194 mono_out = 1;
3195 }
3196 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3197 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3198 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3199 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3200
3201 /* PW 3/4 (1ch/1dh) */
3202 parm = AC_PWRST_D3;
3203 set_pin_power_state(codec, 0x1c, &parm);
3204 set_pin_power_state(codec, 0x1d, &parm);
3205 /* HP Independent Mode, power on AOW3 */
3206 if (spec->hp_independent_mode)
3207 snd_hda_codec_write(codec, 0x25, 0,
3208 AC_VERB_SET_POWER_STATE, parm);
3209
3210 /* force to D0 for internal Speaker */
3211 /* MW0 (16h), AOW0 (10h) */
3212 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3213 imux_is_smixer ? AC_PWRST_D0 : parm);
3214 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3215 mono_out ? AC_PWRST_D0 : parm);
3216}
3217
Lydia Wangf3db4232009-10-10 19:08:41 +08003218static int patch_vt1716S(struct hda_codec *codec)
3219{
3220 struct via_spec *spec;
3221 int err;
3222
3223 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003224 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003225 if (spec == NULL)
3226 return -ENOMEM;
3227
Takashi Iwai620e2b22011-06-17 17:19:19 +02003228 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003229 override_mic_boost(codec, 0x1a, 0, 3, 40);
3230 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003231
Lydia Wangf3db4232009-10-10 19:08:41 +08003232 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003233 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003234 if (err < 0) {
3235 via_free(codec);
3236 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003237 }
3238
Takashi Iwai096a8852011-06-20 12:09:02 +02003239 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003240
Lydia Wangf3db4232009-10-10 19:08:41 +08003241 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3242 spec->num_mixers++;
3243
3244 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3245
3246 codec->patch_ops = via_patch_ops;
3247
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003248 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003249 return 0;
3250}
Lydia Wang25eaba22009-10-10 19:08:43 +08003251
3252/* for vt2002P */
3253
Takashi Iwai096a8852011-06-20 12:09:02 +02003254static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003255 /* Class-D speaker related verbs */
3256 {0x1, 0xfe0, 0x4},
3257 {0x1, 0xfe9, 0x80},
3258 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003259 /* Enable Boost Volume backdoor */
3260 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003261 /* Enable AOW0 to MW9 */
3262 {0x1, 0xfb8, 0x88},
3263 { }
3264};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003265
Takashi Iwai096a8852011-06-20 12:09:02 +02003266static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003267 /* Enable Boost Volume backdoor */
3268 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003269 /* Enable AOW0 to MW9 */
3270 {0x1, 0xfb8, 0x88},
3271 { }
3272};
Lydia Wang25eaba22009-10-10 19:08:43 +08003273
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003274static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3275{
3276 struct via_spec *spec = codec->spec;
3277 int imux_is_smixer;
3278 unsigned int parm;
3279 unsigned int present;
3280 /* MUX9 (1eh) = stereo mixer */
3281 imux_is_smixer =
3282 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3283 /* inputs */
3284 /* PW 5/6/7 (29h/2ah/2bh) */
3285 parm = AC_PWRST_D3;
3286 set_pin_power_state(codec, 0x29, &parm);
3287 set_pin_power_state(codec, 0x2a, &parm);
3288 set_pin_power_state(codec, 0x2b, &parm);
3289 parm = AC_PWRST_D0;
3290 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3291 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3292 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3293 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3294 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3295
3296 /* outputs */
3297 /* AOW0 (8h)*/
3298 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3299
Lydia Wang118909562011-03-23 17:57:34 +08003300 if (spec->codec_type == VT1802) {
3301 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3302 parm = AC_PWRST_D3;
3303 set_pin_power_state(codec, 0x28, &parm);
3304 snd_hda_codec_write(codec, 0x18, 0,
3305 AC_VERB_SET_POWER_STATE, parm);
3306 snd_hda_codec_write(codec, 0x38, 0,
3307 AC_VERB_SET_POWER_STATE, parm);
3308 } else {
3309 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3310 parm = AC_PWRST_D3;
3311 set_pin_power_state(codec, 0x26, &parm);
3312 snd_hda_codec_write(codec, 0x1c, 0,
3313 AC_VERB_SET_POWER_STATE, parm);
3314 snd_hda_codec_write(codec, 0x37, 0,
3315 AC_VERB_SET_POWER_STATE, parm);
3316 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003317
Lydia Wang118909562011-03-23 17:57:34 +08003318 if (spec->codec_type == VT1802) {
3319 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3320 parm = AC_PWRST_D3;
3321 set_pin_power_state(codec, 0x25, &parm);
3322 snd_hda_codec_write(codec, 0x15, 0,
3323 AC_VERB_SET_POWER_STATE, parm);
3324 snd_hda_codec_write(codec, 0x35, 0,
3325 AC_VERB_SET_POWER_STATE, parm);
3326 } else {
3327 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3328 parm = AC_PWRST_D3;
3329 set_pin_power_state(codec, 0x25, &parm);
3330 snd_hda_codec_write(codec, 0x19, 0,
3331 AC_VERB_SET_POWER_STATE, parm);
3332 snd_hda_codec_write(codec, 0x35, 0,
3333 AC_VERB_SET_POWER_STATE, parm);
3334 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003335
3336 if (spec->hp_independent_mode)
3337 snd_hda_codec_write(codec, 0x9, 0,
3338 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3339
3340 /* Class-D */
3341 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3342 present = snd_hda_jack_detect(codec, 0x25);
3343
3344 parm = AC_PWRST_D3;
3345 set_pin_power_state(codec, 0x24, &parm);
3346 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003347 if (spec->codec_type == VT1802)
3348 snd_hda_codec_write(codec, 0x14, 0,
3349 AC_VERB_SET_POWER_STATE, parm);
3350 else
3351 snd_hda_codec_write(codec, 0x18, 0,
3352 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003353 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3354
3355 /* Mono Out */
3356 present = snd_hda_jack_detect(codec, 0x26);
3357
3358 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003359 if (spec->codec_type == VT1802) {
3360 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3361 snd_hda_codec_write(codec, 0x33, 0,
3362 AC_VERB_SET_POWER_STATE, parm);
3363 snd_hda_codec_write(codec, 0x1c, 0,
3364 AC_VERB_SET_POWER_STATE, parm);
3365 snd_hda_codec_write(codec, 0x3c, 0,
3366 AC_VERB_SET_POWER_STATE, parm);
3367 } else {
3368 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3369 snd_hda_codec_write(codec, 0x31, 0,
3370 AC_VERB_SET_POWER_STATE, parm);
3371 snd_hda_codec_write(codec, 0x17, 0,
3372 AC_VERB_SET_POWER_STATE, parm);
3373 snd_hda_codec_write(codec, 0x3b, 0,
3374 AC_VERB_SET_POWER_STATE, parm);
3375 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003376 /* MW9 (21h) */
3377 if (imux_is_smixer || !is_aa_path_mute(codec))
3378 snd_hda_codec_write(codec, 0x21, 0,
3379 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3380 else
3381 snd_hda_codec_write(codec, 0x21, 0,
3382 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3383}
Lydia Wang25eaba22009-10-10 19:08:43 +08003384
3385/* patch for vt2002P */
3386static int patch_vt2002P(struct hda_codec *codec)
3387{
3388 struct via_spec *spec;
3389 int err;
3390
3391 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003392 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003393 if (spec == NULL)
3394 return -ENOMEM;
3395
Takashi Iwai620e2b22011-06-17 17:19:19 +02003396 spec->aa_mix_nid = 0x21;
Lydia Wang5c9a5612011-07-08 14:03:43 +08003397 spec->dac_mixer_idx = 3;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003398 override_mic_boost(codec, 0x2b, 0, 3, 40);
3399 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003400
Lydia Wang25eaba22009-10-10 19:08:43 +08003401 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003402 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003403 if (err < 0) {
3404 via_free(codec);
3405 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003406 }
3407
Lydia Wang118909562011-03-23 17:57:34 +08003408 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003409 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003410 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003411 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003412
Lydia Wang25eaba22009-10-10 19:08:43 +08003413 codec->patch_ops = via_patch_ops;
3414
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003415 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003416 return 0;
3417}
Lydia Wangab6734e2009-10-10 19:08:46 +08003418
3419/* for vt1812 */
3420
Takashi Iwai096a8852011-06-20 12:09:02 +02003421static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003422 /* Enable Boost Volume backdoor */
3423 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003424 /* Enable AOW0 to MW9 */
3425 {0x1, 0xfb8, 0xa8},
3426 { }
3427};
3428
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003429static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3430{
3431 struct via_spec *spec = codec->spec;
3432 int imux_is_smixer =
3433 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3434 unsigned int parm;
3435 unsigned int present;
3436 /* MUX10 (1eh) = stereo mixer */
3437 imux_is_smixer =
3438 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3439 /* inputs */
3440 /* PW 5/6/7 (29h/2ah/2bh) */
3441 parm = AC_PWRST_D3;
3442 set_pin_power_state(codec, 0x29, &parm);
3443 set_pin_power_state(codec, 0x2a, &parm);
3444 set_pin_power_state(codec, 0x2b, &parm);
3445 parm = AC_PWRST_D0;
3446 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3447 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3448 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3449 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3450 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3451
3452 /* outputs */
3453 /* AOW0 (8h)*/
3454 snd_hda_codec_write(codec, 0x8, 0,
3455 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3456
3457 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3458 parm = AC_PWRST_D3;
3459 set_pin_power_state(codec, 0x28, &parm);
3460 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3461 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3462
3463 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3464 parm = AC_PWRST_D3;
3465 set_pin_power_state(codec, 0x25, &parm);
3466 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3467 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3468 if (spec->hp_independent_mode)
3469 snd_hda_codec_write(codec, 0x9, 0,
3470 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3471
3472 /* Internal Speaker */
3473 /* PW0 (24h), MW0(14h), MUX0(34h) */
3474 present = snd_hda_jack_detect(codec, 0x25);
3475
3476 parm = AC_PWRST_D3;
3477 set_pin_power_state(codec, 0x24, &parm);
3478 if (present) {
3479 snd_hda_codec_write(codec, 0x14, 0,
3480 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3481 snd_hda_codec_write(codec, 0x34, 0,
3482 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3483 } else {
3484 snd_hda_codec_write(codec, 0x14, 0,
3485 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3486 snd_hda_codec_write(codec, 0x34, 0,
3487 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3488 }
3489
3490
3491 /* Mono Out */
3492 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3493 present = snd_hda_jack_detect(codec, 0x28);
3494
3495 parm = AC_PWRST_D3;
3496 set_pin_power_state(codec, 0x31, &parm);
3497 if (present) {
3498 snd_hda_codec_write(codec, 0x1c, 0,
3499 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3500 snd_hda_codec_write(codec, 0x3c, 0,
3501 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3502 snd_hda_codec_write(codec, 0x3e, 0,
3503 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3504 } else {
3505 snd_hda_codec_write(codec, 0x1c, 0,
3506 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3507 snd_hda_codec_write(codec, 0x3c, 0,
3508 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3509 snd_hda_codec_write(codec, 0x3e, 0,
3510 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3511 }
3512
3513 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3514 parm = AC_PWRST_D3;
3515 set_pin_power_state(codec, 0x33, &parm);
3516 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3517 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3518
3519}
Lydia Wangab6734e2009-10-10 19:08:46 +08003520
3521/* patch for vt1812 */
3522static int patch_vt1812(struct hda_codec *codec)
3523{
3524 struct via_spec *spec;
3525 int err;
3526
3527 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003528 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003529 if (spec == NULL)
3530 return -ENOMEM;
3531
Takashi Iwai620e2b22011-06-17 17:19:19 +02003532 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003533 override_mic_boost(codec, 0x2b, 0, 3, 40);
3534 override_mic_boost(codec, 0x29, 0, 3, 40);
Lydia Wang28dc10a2011-07-08 18:28:47 +08003535 spec->dac_mixer_idx = 5;
Takashi Iwai620e2b22011-06-17 17:19:19 +02003536
Lydia Wangab6734e2009-10-10 19:08:46 +08003537 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003538 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003539 if (err < 0) {
3540 via_free(codec);
3541 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003542 }
3543
Takashi Iwai096a8852011-06-20 12:09:02 +02003544 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003545
Lydia Wangab6734e2009-10-10 19:08:46 +08003546 codec->patch_ops = via_patch_ops;
3547
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003548 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003549 return 0;
3550}
3551
Joseph Chanc577b8a2006-11-29 15:29:40 +01003552/*
3553 * patch entries
3554 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003555static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003556 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3557 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3558 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3559 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3560 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003561 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003562 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003563 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003564 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003565 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003566 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003567 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003568 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003569 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003570 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003571 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003572 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003573 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003574 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003575 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003576 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003577 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003578 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003579 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003580 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003581 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003582 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003583 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003584 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003585 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003586 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003587 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003588 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003589 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003590 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003591 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003592 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003593 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003594 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003595 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003596 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003597 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003598 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003599 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003600 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003601 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003602 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003603 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003604 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003605 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003606 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003607 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003608 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003609 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003610 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003611 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003612 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003613 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003614 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003615 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003616 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003617 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003618 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003619 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003620 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003621 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003622 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003623 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003624 { .id = 0x11060428, .name = "VT1718S",
3625 .patch = patch_vt1718S},
3626 { .id = 0x11064428, .name = "VT1718S",
3627 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003628 { .id = 0x11060441, .name = "VT2020",
3629 .patch = patch_vt1718S},
3630 { .id = 0x11064441, .name = "VT1828S",
3631 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003632 { .id = 0x11060433, .name = "VT1716S",
3633 .patch = patch_vt1716S},
3634 { .id = 0x1106a721, .name = "VT1716S",
3635 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003636 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3637 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003638 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003639 { .id = 0x11060440, .name = "VT1818S",
3640 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003641 { .id = 0x11060446, .name = "VT1802",
3642 .patch = patch_vt2002P},
3643 { .id = 0x11068446, .name = "VT1802",
3644 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003645 {} /* terminator */
3646};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003647
3648MODULE_ALIAS("snd-hda-codec-id:1106*");
3649
3650static struct hda_codec_preset_list via_list = {
3651 .preset = snd_hda_preset_via,
3652 .owner = THIS_MODULE,
3653};
3654
3655MODULE_LICENSE("GPL");
3656MODULE_DESCRIPTION("VIA HD-audio codec");
3657
3658static int __init patch_via_init(void)
3659{
3660 return snd_hda_add_codec_preset(&via_list);
3661}
3662
3663static void __exit patch_via_exit(void)
3664{
3665 snd_hda_delete_codec_preset(&via_list);
3666}
3667
3668module_init(patch_via_init)
3669module_exit(patch_via_exit)