blob: be2e57b445071216f8acaf19770d788cf5f0a128 [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 Wangd69607b32011-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 Wangd69607b32011-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 Wangd69607b32011-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 Wangd69607b32011-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 Wangd69607b32011-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);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200618
Joseph Chanc577b8a2006-11-29 15:29:40 +0100619static void via_auto_init_analog_input(struct hda_codec *codec)
620{
621 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200622 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200623 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200624 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200625 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100626
Takashi Iwai096a8852011-06-20 12:09:02 +0200627 /* init ADCs */
628 for (i = 0; i < spec->num_adc_nids; i++) {
629 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
630 AC_VERB_SET_AMP_GAIN_MUTE,
631 AMP_IN_UNMUTE(0));
632 }
633
634 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200635 for (i = 0; i < cfg->num_inputs; i++) {
636 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200637 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200638 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100639 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200640 ctl = PIN_VREF50;
641 else
642 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100643 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200644 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100645 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200646
647 /* init input-src */
648 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200649 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
650 if (spec->mux_nids[adc_idx]) {
651 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
652 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
653 AC_VERB_SET_CONNECT_SEL,
654 mux_idx);
655 }
656 if (spec->dyn_adc_switch)
657 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200658 }
659
660 /* init aa-mixer */
661 if (!spec->aa_mix_nid)
662 return;
663 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
664 ARRAY_SIZE(conn));
665 for (i = 0; i < num_conns; i++) {
666 unsigned int caps = get_wcaps(codec, conn[i]);
667 if (get_wcaps_type(caps) == AC_WID_PIN)
668 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
669 AC_VERB_SET_AMP_GAIN_MUTE,
670 AMP_IN_MUTE(i));
671 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100672}
Lydia Wangf5271102009-10-10 19:07:35 +0800673
674static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
675 unsigned int *affected_parm)
676{
677 unsigned parm;
678 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
679 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
680 >> AC_DEFCFG_MISC_SHIFT
681 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800682 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200683 unsigned present = 0;
684
685 no_presence |= spec->no_pin_power_ctl;
686 if (!no_presence)
687 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200688 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800689 || ((no_presence || present)
690 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800691 *affected_parm = AC_PWRST_D0; /* if it's connected */
692 parm = AC_PWRST_D0;
693 } else
694 parm = AC_PWRST_D3;
695
696 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
697}
698
Takashi Iwai24088a52011-06-17 16:59:21 +0200699static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
700 struct snd_ctl_elem_info *uinfo)
701{
702 static const char * const texts[] = {
703 "Disabled", "Enabled"
704 };
705
706 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
707 uinfo->count = 1;
708 uinfo->value.enumerated.items = 2;
709 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
710 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
711 strcpy(uinfo->value.enumerated.name,
712 texts[uinfo->value.enumerated.item]);
713 return 0;
714}
715
716static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
717 struct snd_ctl_elem_value *ucontrol)
718{
719 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
720 struct via_spec *spec = codec->spec;
721 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
722 return 0;
723}
724
725static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
726 struct snd_ctl_elem_value *ucontrol)
727{
728 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
729 struct via_spec *spec = codec->spec;
730 unsigned int val = !ucontrol->value.enumerated.item[0];
731
732 if (val == spec->no_pin_power_ctl)
733 return 0;
734 spec->no_pin_power_ctl = val;
735 set_widgets_power_state(codec);
736 return 1;
737}
738
739static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
740 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
741 .name = "Dynamic Power-Control",
742 .info = via_pin_power_ctl_info,
743 .get = via_pin_power_ctl_get,
744 .put = via_pin_power_ctl_put,
745};
746
747
Harald Welte0aa62ae2008-09-09 15:58:27 +0800748static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
749 struct snd_ctl_elem_info *uinfo)
750{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200751 static const char * const texts[] = { "OFF", "ON" };
752
753 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
754 uinfo->count = 1;
755 uinfo->value.enumerated.items = 2;
756 if (uinfo->value.enumerated.item >= 2)
757 uinfo->value.enumerated.item = 1;
758 strcpy(uinfo->value.enumerated.name,
759 texts[uinfo->value.enumerated.item]);
760 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800761}
762
763static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
764 struct snd_ctl_elem_value *ucontrol)
765{
766 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800767 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800768
Takashi Iwaiece8d042011-06-19 16:24:21 +0200769 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800770 return 0;
771}
772
Harald Welte0aa62ae2008-09-09 15:58:27 +0800773static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
774 struct snd_ctl_elem_value *ucontrol)
775{
776 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
777 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +0200778 int cur;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200779
Takashi Iwai25250502011-06-30 17:24:47 +0200780 /* no independent-hp status change during PCM playback is running */
781 if (spec->num_active_streams)
782 return -EBUSY;
783
784 cur = !!ucontrol->value.enumerated.item[0];
785 if (spec->hp_independent_mode == cur)
786 return 0;
787 spec->hp_independent_mode = cur;
788 if (cur) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200789 activate_output_path(codec, &spec->hp_dep_path, false, false);
790 activate_output_path(codec, &spec->hp_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200791 if (spec->hp_indep_shared)
792 activate_output_path(codec, &spec->out_path[HDA_SIDE],
793 false, false);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200794 } else {
795 activate_output_path(codec, &spec->hp_path, false, false);
796 activate_output_path(codec, &spec->hp_dep_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200797 if (spec->hp_indep_shared)
798 activate_output_path(codec, &spec->out_path[HDA_SIDE],
799 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200800 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800801
Lydia Wangce0e5a92011-03-22 16:22:37 +0800802 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800803 set_widgets_power_state(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200804 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800805}
806
Takashi Iwaiece8d042011-06-19 16:24:21 +0200807static const struct snd_kcontrol_new via_hp_mixer = {
808 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
809 .name = "Independent HP",
810 .info = via_independent_hp_info,
811 .get = via_independent_hp_get,
812 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800813};
814
Takashi Iwai3d83e572010-04-14 14:36:23 +0200815static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100816{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200817 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100818 struct snd_kcontrol_new *knew;
819 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100820
Takashi Iwaiece8d042011-06-19 16:24:21 +0200821 nid = spec->autocfg.hp_pins[0];
822 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200823 if (knew == NULL)
824 return -ENOMEM;
825
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100826 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100827
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100828 return 0;
829}
830
Lydia Wang1564b282009-10-10 19:07:52 +0800831static void notify_aa_path_ctls(struct hda_codec *codec)
832{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200833 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800834 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800835
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200836 for (i = 0; i < spec->smart51_nums; i++) {
837 struct snd_kcontrol *ctl;
838 struct snd_ctl_elem_id id;
839 memset(&id, 0, sizeof(id));
840 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
841 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800842 ctl = snd_hda_find_mixer_ctl(codec, id.name);
843 if (ctl)
844 snd_ctl_notify(codec->bus->card,
845 SNDRV_CTL_EVENT_MASK_VALUE,
846 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800847 }
848}
849
850static void mute_aa_path(struct hda_codec *codec, int mute)
851{
852 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200853 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800854 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200855
Lydia Wang1564b282009-10-10 19:07:52 +0800856 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200857 for (i = 0; i < spec->smart51_nums; i++) {
858 if (spec->smart51_idxs[i] < 0)
859 continue;
860 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
861 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800862 HDA_AMP_MUTE, val);
863 }
864}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200865
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200866static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
867{
868 struct via_spec *spec = codec->spec;
869 int i;
870
871 for (i = 0; i < spec->smart51_nums; i++)
872 if (spec->smart51_pins[i] == pin)
873 return true;
874 return false;
875}
876
Lydia Wang1564b282009-10-10 19:07:52 +0800877static int via_smart51_get(struct snd_kcontrol *kcontrol,
878 struct snd_ctl_elem_value *ucontrol)
879{
880 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
881 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800882
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200883 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800884 return 0;
885}
886
887static int via_smart51_put(struct snd_kcontrol *kcontrol,
888 struct snd_ctl_elem_value *ucontrol)
889{
890 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
891 struct via_spec *spec = codec->spec;
892 int out_in = *ucontrol->value.integer.value
893 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800894 int i;
895
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200896 for (i = 0; i < spec->smart51_nums; i++) {
897 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200898 unsigned int parm;
899
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200900 parm = snd_hda_codec_read(codec, nid, 0,
901 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
902 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
903 parm |= out_in;
904 snd_hda_codec_write(codec, nid, 0,
905 AC_VERB_SET_PIN_WIDGET_CONTROL,
906 parm);
907 if (out_in == AC_PINCTL_OUT_EN) {
908 mute_aa_path(codec, 1);
909 notify_aa_path_ctls(codec);
910 }
Lydia Wang1564b282009-10-10 19:07:52 +0800911 }
912 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800913 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800914 return 1;
915}
916
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200917static const struct snd_kcontrol_new via_smart51_mixer = {
918 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
919 .name = "Smart 5.1",
920 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200921 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200922 .get = via_smart51_get,
923 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800924};
925
Takashi Iwaif4a78282011-06-17 18:46:48 +0200926static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100927{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200928 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100929
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200930 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800931 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200932 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100933 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100934 return 0;
935}
936
Takashi Iwaiada509e2011-06-20 15:40:19 +0200937/* check AA path's mute status */
938static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800939{
Lydia Wangf5271102009-10-10 19:07:35 +0800940 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200941 const struct hda_amp_list *p;
942 int i, ch, v;
943
944 for (i = 0; i < spec->num_loopbacks; i++) {
945 p = &spec->loopback_list[i];
946 for (ch = 0; ch < 2; ch++) {
947 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
948 p->idx);
949 if (!(v & HDA_AMP_MUTE) && v > 0)
950 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800951 }
952 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200953 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800954}
955
956/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200957static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800958{
959 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200960 bool enable;
961 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800962
Takashi Iwaiada509e2011-06-20 15:40:19 +0200963 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800964
965 /* decide low current mode's verb & parameter */
966 switch (spec->codec_type) {
967 case VT1708B_8CH:
968 case VT1708B_4CH:
969 verb = 0xf70;
970 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
971 break;
972 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800973 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800974 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800975 verb = 0xf73;
976 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
977 break;
978 case VT1702:
979 verb = 0xf73;
980 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
981 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800982 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800983 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800984 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800985 verb = 0xf93;
986 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
987 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800988 default:
989 return; /* other codecs are not supported */
990 }
991 /* send verb */
992 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
993}
994
Joseph Chanc577b8a2006-11-29 15:29:40 +0100995/*
996 * generic initialization of ADC, input mixers and output mixers
997 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200998static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800999 /* power down jack detect function */
1000 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001001 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001002};
1003
Takashi Iwaiada509e2011-06-20 15:40:19 +02001004static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001005{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001006 struct via_spec *spec = codec->spec;
1007
1008 if (active)
1009 spec->num_active_streams++;
1010 else
1011 spec->num_active_streams--;
1012 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001013}
1014
Takashi Iwaiece8d042011-06-19 16:24:21 +02001015static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001016 struct hda_codec *codec,
1017 struct snd_pcm_substream *substream)
1018{
1019 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001020 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001021 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001022
Takashi Iwai25250502011-06-30 17:24:47 +02001023 spec->multiout.hp_nid = 0;
1024 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
1025 if (!spec->hp_independent_mode) {
1026 if (!spec->hp_indep_shared)
1027 spec->multiout.hp_nid = spec->hp_dac_nid;
1028 } else {
1029 if (spec->hp_indep_shared)
1030 spec->multiout.num_dacs = cfg->line_outs - 1;
1031 }
1032 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001033 set_stream_active(codec, true);
1034 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1035 hinfo);
1036 if (err < 0) {
1037 spec->multiout.hp_nid = 0;
1038 set_stream_active(codec, false);
1039 return err;
1040 }
1041 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001042}
1043
Takashi Iwaiece8d042011-06-19 16:24:21 +02001044static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001045 struct hda_codec *codec,
1046 struct snd_pcm_substream *substream)
1047{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001048 struct via_spec *spec = codec->spec;
1049
1050 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001051 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001052 return 0;
1053}
1054
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001055static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1056 struct hda_codec *codec,
1057 struct snd_pcm_substream *substream)
1058{
1059 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001060
Takashi Iwaiece8d042011-06-19 16:24:21 +02001061 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001062 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001063 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1064 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001065 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001066 return 0;
1067}
1068
1069static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1070 struct hda_codec *codec,
1071 struct snd_pcm_substream *substream)
1072{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001073 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001074 return 0;
1075}
1076
1077static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1078 struct hda_codec *codec,
1079 unsigned int stream_tag,
1080 unsigned int format,
1081 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001082{
1083 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001084
Takashi Iwaiece8d042011-06-19 16:24:21 +02001085 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1086 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001087 vt1708_start_hp_work(spec);
1088 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001089}
1090
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001091static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1092 struct hda_codec *codec,
1093 unsigned int stream_tag,
1094 unsigned int format,
1095 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001096{
1097 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001098
Takashi Iwaiece8d042011-06-19 16:24:21 +02001099 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1100 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001101 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001102 return 0;
1103}
1104
1105static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1106 struct hda_codec *codec,
1107 struct snd_pcm_substream *substream)
1108{
1109 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001110
Takashi Iwaiece8d042011-06-19 16:24:21 +02001111 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001112 vt1708_stop_hp_work(spec);
1113 return 0;
1114}
1115
1116static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1117 struct hda_codec *codec,
1118 struct snd_pcm_substream *substream)
1119{
1120 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001121
Takashi Iwaiece8d042011-06-19 16:24:21 +02001122 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001123 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001124 return 0;
1125}
1126
Joseph Chanc577b8a2006-11-29 15:29:40 +01001127/*
1128 * Digital out
1129 */
1130static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1131 struct hda_codec *codec,
1132 struct snd_pcm_substream *substream)
1133{
1134 struct via_spec *spec = codec->spec;
1135 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1136}
1137
1138static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1139 struct hda_codec *codec,
1140 struct snd_pcm_substream *substream)
1141{
1142 struct via_spec *spec = codec->spec;
1143 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1144}
1145
Harald Welte5691ec72008-09-15 22:42:26 +08001146static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001147 struct hda_codec *codec,
1148 unsigned int stream_tag,
1149 unsigned int format,
1150 struct snd_pcm_substream *substream)
1151{
1152 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001153 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1154 stream_tag, format, substream);
1155}
Harald Welte5691ec72008-09-15 22:42:26 +08001156
Takashi Iwai9da29272009-05-07 16:31:14 +02001157static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1158 struct hda_codec *codec,
1159 struct snd_pcm_substream *substream)
1160{
1161 struct via_spec *spec = codec->spec;
1162 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001163 return 0;
1164}
1165
Joseph Chanc577b8a2006-11-29 15:29:40 +01001166/*
1167 * Analog capture
1168 */
1169static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1170 struct hda_codec *codec,
1171 unsigned int stream_tag,
1172 unsigned int format,
1173 struct snd_pcm_substream *substream)
1174{
1175 struct via_spec *spec = codec->spec;
1176
1177 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1178 stream_tag, 0, format);
1179 return 0;
1180}
1181
1182static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1183 struct hda_codec *codec,
1184 struct snd_pcm_substream *substream)
1185{
1186 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001187 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001188 return 0;
1189}
1190
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001191/* analog capture with dynamic ADC switching */
1192static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1193 struct hda_codec *codec,
1194 unsigned int stream_tag,
1195 unsigned int format,
1196 struct snd_pcm_substream *substream)
1197{
1198 struct via_spec *spec = codec->spec;
1199 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1200
1201 spec->cur_adc = spec->adc_nids[adc_idx];
1202 spec->cur_adc_stream_tag = stream_tag;
1203 spec->cur_adc_format = format;
1204 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
1205 return 0;
1206}
1207
1208static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1209 struct hda_codec *codec,
1210 struct snd_pcm_substream *substream)
1211{
1212 struct via_spec *spec = codec->spec;
1213
1214 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1215 spec->cur_adc = 0;
1216 return 0;
1217}
1218
1219/* re-setup the stream if running; called from input-src put */
1220static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1221{
1222 struct via_spec *spec = codec->spec;
1223 int adc_idx = spec->inputs[cur].adc_idx;
1224 hda_nid_t adc = spec->adc_nids[adc_idx];
1225
1226 if (spec->cur_adc && spec->cur_adc != adc) {
1227 /* stream is running, let's swap the current ADC */
1228 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1229 spec->cur_adc = adc;
1230 snd_hda_codec_setup_stream(codec, adc,
1231 spec->cur_adc_stream_tag, 0,
1232 spec->cur_adc_format);
1233 return true;
1234 }
1235 return false;
1236}
1237
Takashi Iwai9af74212011-06-18 16:17:45 +02001238static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001239 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001240 .channels_min = 2,
1241 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001242 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001243 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001244 .open = via_playback_multi_pcm_open,
1245 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001246 .prepare = via_playback_multi_pcm_prepare,
1247 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001248 },
1249};
1250
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001251static const struct hda_pcm_stream via_pcm_hp_playback = {
1252 .substreams = 1,
1253 .channels_min = 2,
1254 .channels_max = 2,
1255 /* NID is set in via_build_pcms */
1256 .ops = {
1257 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001258 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001259 .prepare = via_playback_hp_pcm_prepare,
1260 .cleanup = via_playback_hp_pcm_cleanup
1261 },
1262};
1263
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001264static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001265 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001266 .channels_min = 2,
1267 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001268 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001269 /* We got noisy outputs on the right channel on VT1708 when
1270 * 24bit samples are used. Until any workaround is found,
1271 * disable the 24bit format, so far.
1272 */
1273 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1274 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001275 .open = via_playback_multi_pcm_open,
1276 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001277 .prepare = via_playback_multi_pcm_prepare,
1278 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001279 },
1280};
1281
Takashi Iwai9af74212011-06-18 16:17:45 +02001282static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001283 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001284 .channels_min = 2,
1285 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001286 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001287 .ops = {
1288 .prepare = via_capture_pcm_prepare,
1289 .cleanup = via_capture_pcm_cleanup
1290 },
1291};
1292
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001293static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1294 .substreams = 1,
1295 .channels_min = 2,
1296 .channels_max = 2,
1297 /* NID is set in via_build_pcms */
1298 .ops = {
1299 .prepare = via_dyn_adc_capture_pcm_prepare,
1300 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1301 },
1302};
1303
Takashi Iwai9af74212011-06-18 16:17:45 +02001304static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001305 .substreams = 1,
1306 .channels_min = 2,
1307 .channels_max = 2,
1308 /* NID is set in via_build_pcms */
1309 .ops = {
1310 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001311 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001312 .prepare = via_dig_playback_pcm_prepare,
1313 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001314 },
1315};
1316
Takashi Iwai9af74212011-06-18 16:17:45 +02001317static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001318 .substreams = 1,
1319 .channels_min = 2,
1320 .channels_max = 2,
1321};
1322
Takashi Iwai370bafb2011-06-20 12:47:45 +02001323/*
1324 * slave controls for virtual master
1325 */
1326static const char * const via_slave_vols[] = {
1327 "Front Playback Volume",
1328 "Surround Playback Volume",
1329 "Center Playback Volume",
1330 "LFE Playback Volume",
1331 "Side Playback Volume",
1332 "Headphone Playback Volume",
1333 "Speaker Playback Volume",
1334 NULL,
1335};
1336
1337static const char * const via_slave_sws[] = {
1338 "Front Playback Switch",
1339 "Surround Playback Switch",
1340 "Center Playback Switch",
1341 "LFE Playback Switch",
1342 "Side Playback Switch",
1343 "Headphone Playback Switch",
1344 "Speaker Playback Switch",
1345 NULL,
1346};
1347
Joseph Chanc577b8a2006-11-29 15:29:40 +01001348static int via_build_controls(struct hda_codec *codec)
1349{
1350 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001351 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001352 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001353
Takashi Iwai24088a52011-06-17 16:59:21 +02001354 if (spec->set_widgets_power_state)
1355 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1356 return -ENOMEM;
1357
Joseph Chanc577b8a2006-11-29 15:29:40 +01001358 for (i = 0; i < spec->num_mixers; i++) {
1359 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1360 if (err < 0)
1361 return err;
1362 }
1363
1364 if (spec->multiout.dig_out_nid) {
1365 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001366 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001367 spec->multiout.dig_out_nid);
1368 if (err < 0)
1369 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001370 err = snd_hda_create_spdif_share_sw(codec,
1371 &spec->multiout);
1372 if (err < 0)
1373 return err;
1374 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001375 }
1376 if (spec->dig_in_nid) {
1377 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1378 if (err < 0)
1379 return err;
1380 }
Lydia Wang17314372009-10-10 19:07:37 +08001381
Takashi Iwai370bafb2011-06-20 12:47:45 +02001382 /* if we have no master control, let's create it */
1383 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1384 unsigned int vmaster_tlv[4];
1385 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1386 HDA_OUTPUT, vmaster_tlv);
1387 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1388 vmaster_tlv, via_slave_vols);
1389 if (err < 0)
1390 return err;
1391 }
1392 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1393 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1394 NULL, via_slave_sws);
1395 if (err < 0)
1396 return err;
1397 }
1398
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001399 /* assign Capture Source enums to NID */
1400 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1401 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001402 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001403 if (err < 0)
1404 return err;
1405 }
1406
Lydia Wang17314372009-10-10 19:07:37 +08001407 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001408 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001409 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001410
Takashi Iwai603c4012008-07-30 15:01:44 +02001411 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001412 return 0;
1413}
1414
1415static int via_build_pcms(struct hda_codec *codec)
1416{
1417 struct via_spec *spec = codec->spec;
1418 struct hda_pcm *info = spec->pcm_rec;
1419
1420 codec->num_pcms = 1;
1421 codec->pcm_info = info;
1422
Takashi Iwai82673bc2011-06-17 16:24:21 +02001423 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1424 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001425 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001426
1427 if (!spec->stream_analog_playback)
1428 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001429 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001430 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001431 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1432 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001433 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1434 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001435
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001436 if (!spec->stream_analog_capture) {
1437 if (spec->dyn_adc_switch)
1438 spec->stream_analog_capture =
1439 &via_pcm_dyn_adc_analog_capture;
1440 else
1441 spec->stream_analog_capture = &via_pcm_analog_capture;
1442 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001443 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1444 *spec->stream_analog_capture;
1445 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001446 if (!spec->dyn_adc_switch)
1447 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1448 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001449
1450 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1451 codec->num_pcms++;
1452 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001453 snprintf(spec->stream_name_digital,
1454 sizeof(spec->stream_name_digital),
1455 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001456 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001457 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001458 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001459 if (!spec->stream_digital_playback)
1460 spec->stream_digital_playback =
1461 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001462 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001463 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001464 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1465 spec->multiout.dig_out_nid;
1466 }
1467 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001468 if (!spec->stream_digital_capture)
1469 spec->stream_digital_capture =
1470 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001471 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001472 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001473 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1474 spec->dig_in_nid;
1475 }
1476 }
1477
Takashi Iwaiece8d042011-06-19 16:24:21 +02001478 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001479 codec->num_pcms++;
1480 info++;
1481 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1482 "%s HP", codec->chip_name);
1483 info->name = spec->stream_name_hp;
1484 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1485 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001486 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001487 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001488 return 0;
1489}
1490
1491static void via_free(struct hda_codec *codec)
1492{
1493 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001494
1495 if (!spec)
1496 return;
1497
Takashi Iwai603c4012008-07-30 15:01:44 +02001498 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001499 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001500 kfree(spec->bind_cap_vol);
1501 kfree(spec->bind_cap_sw);
1502 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001503}
1504
Takashi Iwai64be2852011-06-17 16:51:39 +02001505/* mute/unmute outputs */
1506static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1507 hda_nid_t *pins, bool mute)
1508{
1509 int i;
1510 for (i = 0; i < num_pins; i++)
1511 snd_hda_codec_write(codec, pins[i], 0,
1512 AC_VERB_SET_PIN_WIDGET_CONTROL,
1513 mute ? 0 : PIN_OUT);
1514}
1515
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001516/* mute internal speaker if line-out is plugged */
1517static void via_line_automute(struct hda_codec *codec, int present)
1518{
1519 struct via_spec *spec = codec->spec;
1520
1521 if (!spec->autocfg.speaker_outs)
1522 return;
1523 if (!present)
1524 present = snd_hda_jack_detect(codec,
1525 spec->autocfg.line_out_pins[0]);
1526 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1527 spec->autocfg.speaker_pins,
1528 present);
1529}
1530
Harald Welte69e52a82008-09-09 15:57:32 +08001531/* mute internal speaker if HP is plugged */
1532static void via_hp_automute(struct hda_codec *codec)
1533{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001534 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001535 struct via_spec *spec = codec->spec;
1536
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001537 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001538 int nums;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001539 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001540 if (spec->smart51_enabled)
1541 nums = spec->autocfg.line_outs + spec->smart51_nums;
1542 else
1543 nums = spec->autocfg.line_outs;
1544 toggle_output_mutes(codec, nums,
Takashi Iwai64be2852011-06-17 16:51:39 +02001545 spec->autocfg.line_out_pins,
1546 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001547 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001548 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001549}
1550
Harald Welte69e52a82008-09-09 15:57:32 +08001551static void via_gpio_control(struct hda_codec *codec)
1552{
1553 unsigned int gpio_data;
1554 unsigned int vol_counter;
1555 unsigned int vol;
1556 unsigned int master_vol;
1557
1558 struct via_spec *spec = codec->spec;
1559
1560 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1561 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1562
1563 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1564 0xF84, 0) & 0x3F0000) >> 16;
1565
1566 vol = vol_counter & 0x1F;
1567 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1568 AC_VERB_GET_AMP_GAIN_MUTE,
1569 AC_AMP_GET_INPUT);
1570
1571 if (gpio_data == 0x02) {
1572 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001573 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1574 AC_VERB_SET_PIN_WIDGET_CONTROL,
1575 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001576 if (vol_counter & 0x20) {
1577 /* decrease volume */
1578 if (vol > master_vol)
1579 vol = master_vol;
1580 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1581 0, HDA_AMP_VOLMASK,
1582 master_vol-vol);
1583 } else {
1584 /* increase volume */
1585 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1586 HDA_AMP_VOLMASK,
1587 ((master_vol+vol) > 0x2A) ? 0x2A :
1588 (master_vol+vol));
1589 }
1590 } else if (!(gpio_data & 0x02)) {
1591 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001592 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1593 AC_VERB_SET_PIN_WIDGET_CONTROL,
1594 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001595 }
1596}
1597
1598/* unsolicited event for jack sensing */
1599static void via_unsol_event(struct hda_codec *codec,
1600 unsigned int res)
1601{
1602 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001603
Lydia Wanga34df192009-10-10 19:08:01 +08001604 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001605 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001606
1607 res &= ~VIA_JACK_EVENT;
1608
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001609 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001610 via_hp_automute(codec);
1611 else if (res == VIA_GPIO_EVENT)
1612 via_gpio_control(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001613}
1614
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001615#ifdef SND_HDA_NEEDS_RESUME
1616static int via_suspend(struct hda_codec *codec, pm_message_t state)
1617{
1618 struct via_spec *spec = codec->spec;
1619 vt1708_stop_hp_work(spec);
1620 return 0;
1621}
1622#endif
1623
Takashi Iwaicb53c622007-08-10 17:21:45 +02001624#ifdef CONFIG_SND_HDA_POWER_SAVE
1625static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1626{
1627 struct via_spec *spec = codec->spec;
1628 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1629}
1630#endif
1631
Joseph Chanc577b8a2006-11-29 15:29:40 +01001632/*
1633 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001634
1635static int via_init(struct hda_codec *codec);
1636
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001637static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001638 .build_controls = via_build_controls,
1639 .build_pcms = via_build_pcms,
1640 .init = via_init,
1641 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001642 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001643#ifdef SND_HDA_NEEDS_RESUME
1644 .suspend = via_suspend,
1645#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001646#ifdef CONFIG_SND_HDA_POWER_SAVE
1647 .check_power_status = via_check_power_status,
1648#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001649};
1650
Takashi Iwai4a796162011-06-17 17:53:38 +02001651static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001652{
Takashi Iwai4a796162011-06-17 17:53:38 +02001653 struct via_spec *spec = codec->spec;
1654 int i;
1655
1656 for (i = 0; i < spec->multiout.num_dacs; i++) {
1657 if (spec->multiout.dac_nids[i] == dac)
1658 return false;
1659 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001660 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001661 return false;
1662 return true;
1663}
1664
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001665static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001666 hda_nid_t target_dac, struct nid_path *path,
1667 int depth, int wid_type)
1668{
1669 hda_nid_t conn[8];
1670 int i, nums;
1671
1672 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1673 for (i = 0; i < nums; i++) {
1674 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1675 continue;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001676 if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
1677 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001678 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001679 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001680 return false;
1681 for (i = 0; i < nums; i++) {
1682 unsigned int type;
1683 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1684 if (type == AC_WID_AUD_OUT ||
1685 (wid_type != -1 && type != wid_type))
1686 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001687 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001688 path, depth + 1, AC_WID_AUD_SEL))
1689 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001690 }
1691 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001692
1693 found:
1694 path->path[path->depth] = conn[i];
1695 path->idx[path->depth] = i;
1696 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1697 path->multi[path->depth] = 1;
1698 path->depth++;
1699 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001700}
1701
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001702static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1703 hda_nid_t target_dac, struct nid_path *path)
1704{
1705 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1706 path->path[path->depth] = nid;
1707 path->depth++;
1708 return true;
1709 }
1710 return false;
1711}
1712
Takashi Iwai4a796162011-06-17 17:53:38 +02001713static int via_auto_fill_dac_nids(struct hda_codec *codec)
1714{
1715 struct via_spec *spec = codec->spec;
1716 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001717 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001718 hda_nid_t nid;
1719
Joseph Chanc577b8a2006-11-29 15:29:40 +01001720 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001721 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001722 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001723 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001724 if (!nid)
1725 continue;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001726 if (parse_output_path(codec, nid, 0, &spec->out_path[i])) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001727 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Lydia Wang5c9a5612011-07-08 14:03:43 +08001728 dac_num++;
1729 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001730 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001731 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001732 return 0;
1733}
1734
Takashi Iwai4a796162011-06-17 17:53:38 +02001735static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001736 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001737{
Takashi Iwai4a796162011-06-17 17:53:38 +02001738 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001739 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001740 hda_nid_t dac, pin, sel, nid;
1741 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001742
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001743 dac = check_dac ? path->path[0] : 0;
1744 pin = path->path[path->depth - 1];
1745 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001746
Takashi Iwai8df2a312011-06-21 11:48:29 +02001747 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001748 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001749 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001750 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001751 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1752 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001753 else
1754 nid = 0;
1755 if (nid) {
1756 sprintf(name, "%s Playback Volume", pfx);
1757 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001758 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001759 if (err < 0)
1760 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001761 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001762 }
1763
Takashi Iwai8df2a312011-06-21 11:48:29 +02001764 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001765 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001766 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001767 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001768 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1769 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001770 else
1771 nid = 0;
1772 if (nid) {
1773 sprintf(name, "%s Playback Switch", pfx);
1774 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1775 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1776 if (err < 0)
1777 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001778 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001779 }
1780 return 0;
1781}
1782
Takashi Iwaif4a78282011-06-17 18:46:48 +02001783static void mangle_smart51(struct hda_codec *codec)
1784{
1785 struct via_spec *spec = codec->spec;
1786 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001787 struct auto_pin_cfg_item *ins = cfg->inputs;
1788 int i, j, nums, attr;
1789 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001790
Takashi Iwai0f98c242011-06-21 12:51:33 +02001791 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1792 nums = 0;
1793 for (i = 0; i < cfg->num_inputs; i++) {
1794 unsigned int def;
1795 if (ins[i].type > AUTO_PIN_LINE_IN)
1796 continue;
1797 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1798 if (snd_hda_get_input_pin_attr(def) != attr)
1799 continue;
1800 for (j = 0; j < nums; j++)
1801 if (ins[pins[j]].type < ins[i].type) {
1802 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001803 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001804 break;
1805 }
1806 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001807 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001808 }
1809 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001810 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001811 for (i = 0; i < nums; i++) {
1812 hda_nid_t pin = ins[pins[i]].pin;
1813 spec->smart51_pins[spec->smart51_nums++] = pin;
1814 cfg->line_out_pins[cfg->line_outs++] = pin;
1815 if (cfg->line_outs == 3)
1816 break;
1817 }
1818 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001819 }
1820}
1821
Takashi Iwai4a796162011-06-17 17:53:38 +02001822/* add playback controls from the parsed DAC table */
1823static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1824{
1825 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001826 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001827 static const char * const chname[4] = {
1828 "Front", "Surround", "C/LFE", "Side"
1829 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001830 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001831 int old_line_outs;
1832
1833 /* check smart51 */
1834 old_line_outs = cfg->line_outs;
1835 if (cfg->line_outs == 1)
1836 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001837
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001838 err = via_auto_fill_dac_nids(codec);
1839 if (err < 0)
1840 return err;
1841
Lydia Wang5c9a5612011-07-08 14:03:43 +08001842 if (spec->multiout.num_dacs < 3) {
1843 spec->smart51_nums = 0;
1844 cfg->line_outs = old_line_outs;
1845 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001846 for (i = 0; i < cfg->line_outs; i++) {
1847 hda_nid_t pin, dac;
1848 pin = cfg->line_out_pins[i];
1849 dac = spec->multiout.dac_nids[i];
1850 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001851 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001852 if (i == HDA_CLFE) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001853 err = create_ch_ctls(codec, "Center", 1, true,
1854 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001855 if (err < 0)
1856 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001857 err = create_ch_ctls(codec, "LFE", 2, true,
1858 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001859 if (err < 0)
1860 return err;
1861 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001862 const char *pfx = chname[i];
1863 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1864 cfg->line_outs == 1)
1865 pfx = "Speaker";
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001866 err = create_ch_ctls(codec, pfx, 3, true,
1867 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001868 if (err < 0)
1869 return err;
1870 }
1871 }
1872
Takashi Iwai4a796162011-06-17 17:53:38 +02001873 idx = get_connection_index(codec, spec->aa_mix_nid,
1874 spec->multiout.dac_nids[0]);
Lydia Wangc4394f52011-07-04 16:54:15 +08001875 if (idx < 0 && spec->dac_mixer_idx)
1876 idx = spec->dac_mixer_idx;
Takashi Iwai4a796162011-06-17 17:53:38 +02001877 if (idx >= 0) {
1878 /* add control to mixer */
1879 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1880 "PCM Playback Volume",
1881 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1882 idx, HDA_INPUT));
1883 if (err < 0)
1884 return err;
1885 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1886 "PCM Playback Switch",
1887 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1888 idx, HDA_INPUT));
1889 if (err < 0)
1890 return err;
1891 }
1892
Takashi Iwaif4a78282011-06-17 18:46:48 +02001893 cfg->line_outs = old_line_outs;
1894
Joseph Chanc577b8a2006-11-29 15:29:40 +01001895 return 0;
1896}
1897
Takashi Iwai4a796162011-06-17 17:53:38 +02001898static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001899{
Takashi Iwai4a796162011-06-17 17:53:38 +02001900 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001901 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001902 bool check_dac;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001903 int err;
1904
1905 if (!pin)
1906 return 0;
1907
Takashi Iwai8df2a312011-06-21 11:48:29 +02001908 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001909 spec->hp_dac_nid = spec->hp_path.path[0];
Takashi Iwai25250502011-06-30 17:24:47 +02001910 else if (spec->multiout.dac_nids[HDA_SIDE] &&
1911 parse_output_path(codec, pin,
1912 spec->multiout.dac_nids[HDA_SIDE],
1913 &spec->hp_path)) {
1914 spec->hp_dac_nid = spec->hp_path.path[0];
1915 spec->hp_indep_shared = true;
Lydia Wanga2a870c2011-07-08 14:04:33 +08001916 } else if (spec->multiout.dac_nids[HDA_CLFE] &&
1917 parse_output_path(codec, pin,
1918 spec->multiout.dac_nids[HDA_CLFE],
1919 &spec->hp_path)) {
1920 spec->hp_dac_nid = spec->hp_path.path[0];
1921 spec->hp_indep_shared = true;
Takashi Iwai25250502011-06-30 17:24:47 +02001922 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001923
Takashi Iwaiece8d042011-06-19 16:24:21 +02001924 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001925 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001926 !spec->hp_dac_nid)
1927 return 0;
1928
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001929 if (spec->hp_dac_nid && !spec->hp_indep_shared) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001930 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001931 check_dac = true;
1932 } else {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001933 path = &spec->hp_dep_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001934 check_dac = false;
1935 }
1936 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001937 if (err < 0)
1938 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001939 if (spec->hp_dac_nid) {
1940 spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
1941 spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
1942 }
Harald Welte0aa62ae2008-09-09 15:58:27 +08001943
Joseph Chanc577b8a2006-11-29 15:29:40 +01001944 return 0;
1945}
1946
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001947static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1948{
1949 struct via_spec *spec = codec->spec;
1950 hda_nid_t pin, dac;
1951
1952 pin = spec->autocfg.speaker_pins[0];
1953 if (!spec->autocfg.speaker_outs || !pin)
1954 return 0;
1955
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001956 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1957 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001958 spec->multiout.extra_out_nid[0] = dac;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001959 return create_ch_ctls(codec, "Speaker", 3, true,
1960 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001961 }
1962 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001963 &spec->speaker_path))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001964 return create_ch_ctls(codec, "Speaker", 3, false,
1965 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001966
1967 return 0;
1968}
1969
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001970/* look for ADCs */
1971static int via_fill_adcs(struct hda_codec *codec)
1972{
1973 struct via_spec *spec = codec->spec;
1974 hda_nid_t nid = codec->start_nid;
1975 int i;
1976
1977 for (i = 0; i < codec->num_nodes; i++, nid++) {
1978 unsigned int wcaps = get_wcaps(codec, nid);
1979 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1980 continue;
1981 if (wcaps & AC_WCAP_DIGITAL)
1982 continue;
1983 if (!(wcaps & AC_WCAP_CONN_LIST))
1984 continue;
1985 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1986 return -ENOMEM;
1987 spec->adc_nids[spec->num_adc_nids++] = nid;
1988 }
1989 return 0;
1990}
1991
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001992/* input-src control */
1993static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
1994 struct snd_ctl_elem_info *uinfo)
1995{
1996 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1997 struct via_spec *spec = codec->spec;
1998
1999 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2000 uinfo->count = 1;
2001 uinfo->value.enumerated.items = spec->num_inputs;
2002 if (uinfo->value.enumerated.item >= spec->num_inputs)
2003 uinfo->value.enumerated.item = spec->num_inputs - 1;
2004 strcpy(uinfo->value.enumerated.name,
2005 spec->inputs[uinfo->value.enumerated.item].label);
2006 return 0;
2007}
2008
2009static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2010 struct snd_ctl_elem_value *ucontrol)
2011{
2012 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2013 struct via_spec *spec = codec->spec;
2014 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2015
2016 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2017 return 0;
2018}
2019
2020static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2021 struct snd_ctl_elem_value *ucontrol)
2022{
2023 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2024 struct via_spec *spec = codec->spec;
2025 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2026 hda_nid_t mux;
2027 int cur;
2028
2029 cur = ucontrol->value.enumerated.item[0];
2030 if (cur < 0 || cur >= spec->num_inputs)
2031 return -EINVAL;
2032 if (spec->cur_mux[idx] == cur)
2033 return 0;
2034 spec->cur_mux[idx] = cur;
2035 if (spec->dyn_adc_switch) {
2036 int adc_idx = spec->inputs[cur].adc_idx;
2037 mux = spec->mux_nids[adc_idx];
2038 via_dyn_adc_pcm_resetup(codec, cur);
2039 } else {
2040 mux = spec->mux_nids[idx];
2041 if (snd_BUG_ON(!mux))
2042 return -EINVAL;
2043 }
2044
2045 if (mux) {
2046 /* switch to D0 beofre change index */
2047 if (snd_hda_codec_read(codec, mux, 0,
2048 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2049 snd_hda_codec_write(codec, mux, 0,
2050 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2051 snd_hda_codec_write(codec, mux, 0,
2052 AC_VERB_SET_CONNECT_SEL,
2053 spec->inputs[cur].mux_idx);
2054 }
2055
2056 /* update jack power state */
2057 set_widgets_power_state(codec);
2058 return 0;
2059}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002060
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002061static const struct snd_kcontrol_new via_input_src_ctl = {
2062 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2063 /* The multiple "Capture Source" controls confuse alsamixer
2064 * So call somewhat different..
2065 */
2066 /* .name = "Capture Source", */
2067 .name = "Input Source",
2068 .info = via_mux_enum_info,
2069 .get = via_mux_enum_get,
2070 .put = via_mux_enum_put,
2071};
2072
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002073static int create_input_src_ctls(struct hda_codec *codec, int count)
2074{
2075 struct via_spec *spec = codec->spec;
2076 struct snd_kcontrol_new *knew;
2077
2078 if (spec->num_inputs <= 1 || !count)
2079 return 0; /* no need for single src */
2080
2081 knew = via_clone_control(spec, &via_input_src_ctl);
2082 if (!knew)
2083 return -ENOMEM;
2084 knew->count = count;
2085 return 0;
2086}
2087
2088/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002089static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2090{
2091 struct hda_amp_list *list;
2092
2093 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2094 return;
2095 list = spec->loopback_list + spec->num_loopbacks;
2096 list->nid = mix;
2097 list->dir = HDA_INPUT;
2098 list->idx = idx;
2099 spec->num_loopbacks++;
2100 spec->loopback.amplist = spec->loopback_list;
2101}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002102
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002103static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002104 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002105{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002106 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002107}
2108
2109/* add the input-route to the given pin */
2110static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002111{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002112 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002113 int c, idx;
2114
2115 spec->inputs[spec->num_inputs].adc_idx = -1;
2116 spec->inputs[spec->num_inputs].pin = pin;
2117 for (c = 0; c < spec->num_adc_nids; c++) {
2118 if (spec->mux_nids[c]) {
2119 idx = get_connection_index(codec, spec->mux_nids[c],
2120 pin);
2121 if (idx < 0)
2122 continue;
2123 spec->inputs[spec->num_inputs].mux_idx = idx;
2124 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002125 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002126 continue;
2127 }
2128 spec->inputs[spec->num_inputs].adc_idx = c;
2129 /* Can primary ADC satisfy all inputs? */
2130 if (!spec->dyn_adc_switch &&
2131 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2132 snd_printd(KERN_INFO
2133 "via: dynamic ADC switching enabled\n");
2134 spec->dyn_adc_switch = 1;
2135 }
2136 return true;
2137 }
2138 return false;
2139}
2140
2141static int get_mux_nids(struct hda_codec *codec);
2142
2143/* parse input-routes; fill ADCs, MUXs and input-src entries */
2144static int parse_analog_inputs(struct hda_codec *codec)
2145{
2146 struct via_spec *spec = codec->spec;
2147 const struct auto_pin_cfg *cfg = &spec->autocfg;
2148 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002149
2150 err = via_fill_adcs(codec);
2151 if (err < 0)
2152 return err;
2153 err = get_mux_nids(codec);
2154 if (err < 0)
2155 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002156
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002157 /* fill all input-routes */
2158 for (i = 0; i < cfg->num_inputs; i++) {
2159 if (add_input_route(codec, cfg->inputs[i].pin))
2160 spec->inputs[spec->num_inputs++].label =
2161 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002162 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002163
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002164 /* check for internal loopback recording */
2165 if (spec->aa_mix_nid &&
2166 add_input_route(codec, spec->aa_mix_nid))
2167 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2168
2169 return 0;
2170}
2171
2172/* create analog-loopback volume/switch controls */
2173static int create_loopback_ctls(struct hda_codec *codec)
2174{
2175 struct via_spec *spec = codec->spec;
2176 const struct auto_pin_cfg *cfg = &spec->autocfg;
2177 const char *prev_label = NULL;
2178 int type_idx = 0;
2179 int i, j, err, idx;
2180
2181 if (!spec->aa_mix_nid)
2182 return 0;
2183
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002184 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002185 hda_nid_t pin = cfg->inputs[i].pin;
2186 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2187
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002188 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002189 type_idx++;
2190 else
2191 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002192 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002193 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2194 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002195 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002196 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002197 if (err < 0)
2198 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002199 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002200 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002201
2202 /* remember the label for smart51 control */
2203 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002204 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002205 spec->smart51_idxs[j] = idx;
2206 spec->smart51_labels[j] = label;
2207 break;
2208 }
2209 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002210 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002211 return 0;
2212}
2213
2214/* create mic-boost controls (if present) */
2215static int create_mic_boost_ctls(struct hda_codec *codec)
2216{
2217 struct via_spec *spec = codec->spec;
2218 const struct auto_pin_cfg *cfg = &spec->autocfg;
2219 int i, err;
2220
2221 for (i = 0; i < cfg->num_inputs; i++) {
2222 hda_nid_t pin = cfg->inputs[i].pin;
2223 unsigned int caps;
2224 const char *label;
2225 char name[32];
2226
2227 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2228 continue;
2229 caps = query_amp_caps(codec, pin, HDA_INPUT);
2230 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2231 continue;
2232 label = hda_get_autocfg_input_label(codec, cfg, i);
2233 snprintf(name, sizeof(name), "%s Boost Volume", label);
2234 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2235 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2236 if (err < 0)
2237 return err;
2238 }
2239 return 0;
2240}
2241
2242/* create capture and input-src controls for multiple streams */
2243static int create_multi_adc_ctls(struct hda_codec *codec)
2244{
2245 struct via_spec *spec = codec->spec;
2246 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002247
2248 /* create capture mixer elements */
2249 for (i = 0; i < spec->num_adc_nids; i++) {
2250 hda_nid_t adc = spec->adc_nids[i];
2251 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2252 "Capture Volume", i,
2253 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2254 HDA_INPUT));
2255 if (err < 0)
2256 return err;
2257 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2258 "Capture Switch", i,
2259 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2260 HDA_INPUT));
2261 if (err < 0)
2262 return err;
2263 }
2264
2265 /* input-source control */
2266 for (i = 0; i < spec->num_adc_nids; i++)
2267 if (!spec->mux_nids[i])
2268 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002269 err = create_input_src_ctls(codec, i);
2270 if (err < 0)
2271 return err;
2272 return 0;
2273}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002274
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002275/* bind capture volume/switch */
2276static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2277 HDA_BIND_VOL("Capture Volume", 0);
2278static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2279 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002280
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002281static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2282 struct hda_ctl_ops *ops)
2283{
2284 struct hda_bind_ctls *ctl;
2285 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002286
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002287 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2288 if (!ctl)
2289 return -ENOMEM;
2290 ctl->ops = ops;
2291 for (i = 0; i < spec->num_adc_nids; i++)
2292 ctl->values[i] =
2293 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2294 *ctl_ret = ctl;
2295 return 0;
2296}
2297
2298/* create capture and input-src controls for dynamic ADC-switch case */
2299static int create_dyn_adc_ctls(struct hda_codec *codec)
2300{
2301 struct via_spec *spec = codec->spec;
2302 struct snd_kcontrol_new *knew;
2303 int err;
2304
2305 /* set up the bind capture ctls */
2306 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2307 if (err < 0)
2308 return err;
2309 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2310 if (err < 0)
2311 return err;
2312
2313 /* create capture mixer elements */
2314 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2315 if (!knew)
2316 return -ENOMEM;
2317 knew->private_value = (long)spec->bind_cap_vol;
2318
2319 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2320 if (!knew)
2321 return -ENOMEM;
2322 knew->private_value = (long)spec->bind_cap_sw;
2323
2324 /* input-source control */
2325 err = create_input_src_ctls(codec, 1);
2326 if (err < 0)
2327 return err;
2328 return 0;
2329}
2330
2331/* parse and create capture-related stuff */
2332static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2333{
2334 struct via_spec *spec = codec->spec;
2335 int err;
2336
2337 err = parse_analog_inputs(codec);
2338 if (err < 0)
2339 return err;
2340 if (spec->dyn_adc_switch)
2341 err = create_dyn_adc_ctls(codec);
2342 else
2343 err = create_multi_adc_ctls(codec);
2344 if (err < 0)
2345 return err;
2346 err = create_loopback_ctls(codec);
2347 if (err < 0)
2348 return err;
2349 err = create_mic_boost_ctls(codec);
2350 if (err < 0)
2351 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002352 return 0;
2353}
2354
Harald Welte76d9b0d2008-09-09 15:50:37 +08002355static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2356{
2357 unsigned int def_conf;
2358 unsigned char seqassoc;
2359
Takashi Iwai2f334f92009-02-20 14:37:42 +01002360 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002361 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2362 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002363 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2364 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2365 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2366 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002367 }
2368
2369 return;
2370}
2371
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002372static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002373 struct snd_ctl_elem_value *ucontrol)
2374{
2375 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2376 struct via_spec *spec = codec->spec;
2377
2378 if (spec->codec_type != VT1708)
2379 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002380 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002381 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002382 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002383 return 0;
2384}
2385
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002386static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002387 struct snd_ctl_elem_value *ucontrol)
2388{
2389 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2390 struct via_spec *spec = codec->spec;
2391 int change;
2392
2393 if (spec->codec_type != VT1708)
2394 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002395 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002396 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002397 == !spec->vt1708_jack_detect;
2398 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002399 mute_aa_path(codec, 1);
2400 notify_aa_path_ctls(codec);
2401 }
2402 return change;
2403}
2404
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002405static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2406 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2407 .name = "Jack Detect",
2408 .count = 1,
2409 .info = snd_ctl_boolean_mono_info,
2410 .get = vt1708_jack_detect_get,
2411 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002412};
2413
Takashi Iwai12daef62011-06-18 17:45:49 +02002414static void fill_dig_outs(struct hda_codec *codec);
2415static void fill_dig_in(struct hda_codec *codec);
2416
2417static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002418{
2419 struct via_spec *spec = codec->spec;
2420 int err;
2421
2422 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2423 if (err < 0)
2424 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002425 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002426 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002427
Takashi Iwai4a796162011-06-17 17:53:38 +02002428 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002429 if (err < 0)
2430 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002431 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002432 if (err < 0)
2433 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002434 err = via_auto_create_speaker_ctls(codec);
2435 if (err < 0)
2436 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002437 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002438 if (err < 0)
2439 return err;
2440
2441 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2442
Takashi Iwai12daef62011-06-18 17:45:49 +02002443 fill_dig_outs(codec);
2444 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002445
Takashi Iwai603c4012008-07-30 15:01:44 +02002446 if (spec->kctls.list)
2447 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002448
Joseph Chanc577b8a2006-11-29 15:29:40 +01002449
Takashi Iwai8df2a312011-06-21 11:48:29 +02002450 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002451 err = via_hp_build(codec);
2452 if (err < 0)
2453 return err;
2454 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002455
Takashi Iwaif4a78282011-06-17 18:46:48 +02002456 err = via_smart51_build(codec);
2457 if (err < 0)
2458 return err;
2459
Takashi Iwai5d417622011-06-20 11:32:27 +02002460 /* assign slave outs */
2461 if (spec->slave_dig_outs[0])
2462 codec->slave_dig_outs = spec->slave_dig_outs;
2463
Joseph Chanc577b8a2006-11-29 15:29:40 +01002464 return 1;
2465}
2466
Takashi Iwai5d417622011-06-20 11:32:27 +02002467static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002468{
Lydia Wang25eaba22009-10-10 19:08:43 +08002469 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002470 if (spec->multiout.dig_out_nid)
2471 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2472 if (spec->slave_dig_outs[0])
2473 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2474}
Lydia Wang25eaba22009-10-10 19:08:43 +08002475
Takashi Iwai5d417622011-06-20 11:32:27 +02002476static void via_auto_init_dig_in(struct hda_codec *codec)
2477{
2478 struct via_spec *spec = codec->spec;
2479 if (!spec->dig_in_nid)
2480 return;
2481 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2482 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2483}
2484
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002485/* initialize the unsolicited events */
2486static void via_auto_init_unsol_event(struct hda_codec *codec)
2487{
2488 struct via_spec *spec = codec->spec;
2489 struct auto_pin_cfg *cfg = &spec->autocfg;
2490 unsigned int ev;
2491 int i;
2492
2493 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2494 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2495 AC_VERB_SET_UNSOLICITED_ENABLE,
2496 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2497
2498 if (cfg->speaker_pins[0])
2499 ev = VIA_LINE_EVENT;
2500 else
2501 ev = 0;
2502 for (i = 0; i < cfg->line_outs; i++) {
2503 if (cfg->line_out_pins[i] &&
2504 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002505 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002506 AC_VERB_SET_UNSOLICITED_ENABLE,
2507 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2508 }
2509
2510 for (i = 0; i < cfg->num_inputs; i++) {
2511 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2512 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2513 AC_VERB_SET_UNSOLICITED_ENABLE,
2514 AC_USRSP_EN | VIA_JACK_EVENT);
2515 }
2516}
2517
Takashi Iwai5d417622011-06-20 11:32:27 +02002518static int via_init(struct hda_codec *codec)
2519{
2520 struct via_spec *spec = codec->spec;
2521 int i;
2522
2523 for (i = 0; i < spec->num_iverbs; i++)
2524 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2525
Joseph Chanc577b8a2006-11-29 15:29:40 +01002526 via_auto_init_multi_out(codec);
2527 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002528 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002529 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002530 via_auto_init_dig_outs(codec);
2531 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002532
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002533 via_auto_init_unsol_event(codec);
2534
2535 via_hp_automute(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002536
Joseph Chanc577b8a2006-11-29 15:29:40 +01002537 return 0;
2538}
2539
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002540static void vt1708_update_hp_jack_state(struct work_struct *work)
2541{
2542 struct via_spec *spec = container_of(work, struct via_spec,
2543 vt1708_hp_work.work);
2544 if (spec->codec_type != VT1708)
2545 return;
2546 /* if jack state toggled */
2547 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002548 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002549 spec->vt1708_hp_present ^= 1;
2550 via_hp_automute(spec->codec);
2551 }
2552 vt1708_start_hp_work(spec);
2553}
2554
Takashi Iwai337b9d02009-07-07 18:18:59 +02002555static int get_mux_nids(struct hda_codec *codec)
2556{
2557 struct via_spec *spec = codec->spec;
2558 hda_nid_t nid, conn[8];
2559 unsigned int type;
2560 int i, n;
2561
2562 for (i = 0; i < spec->num_adc_nids; i++) {
2563 nid = spec->adc_nids[i];
2564 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002565 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002566 if (type == AC_WID_PIN)
2567 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002568 n = snd_hda_get_connections(codec, nid, conn,
2569 ARRAY_SIZE(conn));
2570 if (n <= 0)
2571 break;
2572 if (n > 1) {
2573 spec->mux_nids[i] = nid;
2574 break;
2575 }
2576 nid = conn[0];
2577 }
2578 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002579 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002580}
2581
Joseph Chanc577b8a2006-11-29 15:29:40 +01002582static int patch_vt1708(struct hda_codec *codec)
2583{
2584 struct via_spec *spec;
2585 int err;
2586
2587 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002588 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002589 if (spec == NULL)
2590 return -ENOMEM;
2591
Takashi Iwai620e2b22011-06-17 17:19:19 +02002592 spec->aa_mix_nid = 0x17;
2593
Takashi Iwai12daef62011-06-18 17:45:49 +02002594 /* Add HP and CD pin config connect bit re-config action */
2595 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2596 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2597
Joseph Chanc577b8a2006-11-29 15:29:40 +01002598 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002599 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002600 if (err < 0) {
2601 via_free(codec);
2602 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002603 }
2604
Takashi Iwai12daef62011-06-18 17:45:49 +02002605 /* add jack detect on/off control */
2606 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2607 return -ENOMEM;
2608
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002609 /* disable 32bit format on VT1708 */
2610 if (codec->vendor_id == 0x11061708)
2611 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002612
Lydia Wange322a362011-06-29 13:52:02 +08002613 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2614
Joseph Chanc577b8a2006-11-29 15:29:40 +01002615 codec->patch_ops = via_patch_ops;
2616
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002617 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002618 return 0;
2619}
2620
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002621static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002622{
2623 struct via_spec *spec;
2624 int err;
2625
2626 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002627 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002628 if (spec == NULL)
2629 return -ENOMEM;
2630
Takashi Iwai620e2b22011-06-17 17:19:19 +02002631 spec->aa_mix_nid = 0x18;
2632
Takashi Iwai12daef62011-06-18 17:45:49 +02002633 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002634 if (err < 0) {
2635 via_free(codec);
2636 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002637 }
2638
Joseph Chanc577b8a2006-11-29 15:29:40 +01002639 codec->patch_ops = via_patch_ops;
2640
Josepch Chanf7278fd2007-12-13 16:40:40 +01002641 return 0;
2642}
2643
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002644static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2645{
2646 struct via_spec *spec = codec->spec;
2647 int imux_is_smixer;
2648 unsigned int parm;
2649 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002650 if ((spec->codec_type != VT1708B_4CH) &&
2651 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002652 is_8ch = 1;
2653
2654 /* SW0 (17h) = stereo mixer */
2655 imux_is_smixer =
2656 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2657 == ((spec->codec_type == VT1708S) ? 5 : 0));
2658 /* inputs */
2659 /* PW 1/2/5 (1ah/1bh/1eh) */
2660 parm = AC_PWRST_D3;
2661 set_pin_power_state(codec, 0x1a, &parm);
2662 set_pin_power_state(codec, 0x1b, &parm);
2663 set_pin_power_state(codec, 0x1e, &parm);
2664 if (imux_is_smixer)
2665 parm = AC_PWRST_D0;
2666 /* SW0 (17h), AIW 0/1 (13h/14h) */
2667 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2668 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2669 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2670
2671 /* outputs */
2672 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2673 parm = AC_PWRST_D3;
2674 set_pin_power_state(codec, 0x19, &parm);
2675 if (spec->smart51_enabled)
2676 set_pin_power_state(codec, 0x1b, &parm);
2677 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2678 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2679
2680 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2681 if (is_8ch) {
2682 parm = AC_PWRST_D3;
2683 set_pin_power_state(codec, 0x22, &parm);
2684 if (spec->smart51_enabled)
2685 set_pin_power_state(codec, 0x1a, &parm);
2686 snd_hda_codec_write(codec, 0x26, 0,
2687 AC_VERB_SET_POWER_STATE, parm);
2688 snd_hda_codec_write(codec, 0x24, 0,
2689 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002690 } else if (codec->vendor_id == 0x11064397) {
2691 /* PW7(23h), SW2(27h), AOW2(25h) */
2692 parm = AC_PWRST_D3;
2693 set_pin_power_state(codec, 0x23, &parm);
2694 if (spec->smart51_enabled)
2695 set_pin_power_state(codec, 0x1a, &parm);
2696 snd_hda_codec_write(codec, 0x27, 0,
2697 AC_VERB_SET_POWER_STATE, parm);
2698 snd_hda_codec_write(codec, 0x25, 0,
2699 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002700 }
2701
2702 /* PW 3/4/7 (1ch/1dh/23h) */
2703 parm = AC_PWRST_D3;
2704 /* force to D0 for internal Speaker */
2705 set_pin_power_state(codec, 0x1c, &parm);
2706 set_pin_power_state(codec, 0x1d, &parm);
2707 if (is_8ch)
2708 set_pin_power_state(codec, 0x23, &parm);
2709
2710 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2711 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2712 imux_is_smixer ? AC_PWRST_D0 : parm);
2713 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2714 if (is_8ch) {
2715 snd_hda_codec_write(codec, 0x25, 0,
2716 AC_VERB_SET_POWER_STATE, parm);
2717 snd_hda_codec_write(codec, 0x27, 0,
2718 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002719 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2720 snd_hda_codec_write(codec, 0x25, 0,
2721 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002722}
2723
Lydia Wang518bf3b2009-10-10 19:07:29 +08002724static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002725static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002726{
2727 struct via_spec *spec;
2728 int err;
2729
Lydia Wang518bf3b2009-10-10 19:07:29 +08002730 if (get_codec_type(codec) == VT1708BCE)
2731 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002732
Josepch Chanf7278fd2007-12-13 16:40:40 +01002733 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002734 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002735 if (spec == NULL)
2736 return -ENOMEM;
2737
Takashi Iwai620e2b22011-06-17 17:19:19 +02002738 spec->aa_mix_nid = 0x16;
2739
Josepch Chanf7278fd2007-12-13 16:40:40 +01002740 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002741 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002742 if (err < 0) {
2743 via_free(codec);
2744 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002745 }
2746
Josepch Chanf7278fd2007-12-13 16:40:40 +01002747 codec->patch_ops = via_patch_ops;
2748
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002749 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2750
Josepch Chanf7278fd2007-12-13 16:40:40 +01002751 return 0;
2752}
2753
Harald Welted949cac2008-09-09 15:56:01 +08002754/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002755static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002756 /* Enable Mic Boost Volume backdoor */
2757 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002758 /* don't bybass mixer */
2759 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002760 { }
2761};
2762
Takashi Iwai9da29272009-05-07 16:31:14 +02002763/* fill out digital output widgets; one for master and one for slave outputs */
2764static void fill_dig_outs(struct hda_codec *codec)
2765{
2766 struct via_spec *spec = codec->spec;
2767 int i;
2768
2769 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2770 hda_nid_t nid;
2771 int conn;
2772
2773 nid = spec->autocfg.dig_out_pins[i];
2774 if (!nid)
2775 continue;
2776 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2777 if (conn < 1)
2778 continue;
2779 if (!spec->multiout.dig_out_nid)
2780 spec->multiout.dig_out_nid = nid;
2781 else {
2782 spec->slave_dig_outs[0] = nid;
2783 break; /* at most two dig outs */
2784 }
2785 }
2786}
2787
Takashi Iwai12daef62011-06-18 17:45:49 +02002788static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002789{
2790 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002791 hda_nid_t dig_nid;
2792 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002793
Takashi Iwai12daef62011-06-18 17:45:49 +02002794 if (!spec->autocfg.dig_in_pin)
2795 return;
Harald Welted949cac2008-09-09 15:56:01 +08002796
Takashi Iwai12daef62011-06-18 17:45:49 +02002797 dig_nid = codec->start_nid;
2798 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2799 unsigned int wcaps = get_wcaps(codec, dig_nid);
2800 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2801 continue;
2802 if (!(wcaps & AC_WCAP_DIGITAL))
2803 continue;
2804 if (!(wcaps & AC_WCAP_CONN_LIST))
2805 continue;
2806 err = get_connection_index(codec, dig_nid,
2807 spec->autocfg.dig_in_pin);
2808 if (err >= 0) {
2809 spec->dig_in_nid = dig_nid;
2810 break;
2811 }
2812 }
Harald Welted949cac2008-09-09 15:56:01 +08002813}
2814
Lydia Wang6369bcf2009-10-10 19:08:31 +08002815static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2816 int offset, int num_steps, int step_size)
2817{
2818 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2819 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2820 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2821 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2822 (0 << AC_AMPCAP_MUTE_SHIFT));
2823}
2824
Harald Welted949cac2008-09-09 15:56:01 +08002825static int patch_vt1708S(struct hda_codec *codec)
2826{
2827 struct via_spec *spec;
2828 int err;
2829
2830 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002831 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002832 if (spec == NULL)
2833 return -ENOMEM;
2834
Takashi Iwai620e2b22011-06-17 17:19:19 +02002835 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002836 override_mic_boost(codec, 0x1a, 0, 3, 40);
2837 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002838
Harald Welted949cac2008-09-09 15:56:01 +08002839 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002840 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002841 if (err < 0) {
2842 via_free(codec);
2843 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002844 }
2845
Takashi Iwai096a8852011-06-20 12:09:02 +02002846 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002847
Harald Welted949cac2008-09-09 15:56:01 +08002848 codec->patch_ops = via_patch_ops;
2849
Lydia Wang518bf3b2009-10-10 19:07:29 +08002850 /* correct names for VT1708BCE */
2851 if (get_codec_type(codec) == VT1708BCE) {
2852 kfree(codec->chip_name);
2853 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2854 snprintf(codec->bus->card->mixername,
2855 sizeof(codec->bus->card->mixername),
2856 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002857 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002858 /* correct names for VT1705 */
2859 if (codec->vendor_id == 0x11064397) {
2860 kfree(codec->chip_name);
2861 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2862 snprintf(codec->bus->card->mixername,
2863 sizeof(codec->bus->card->mixername),
2864 "%s %s", codec->vendor_name, codec->chip_name);
2865 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002866 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002867 return 0;
2868}
2869
2870/* Patch for VT1702 */
2871
Takashi Iwai096a8852011-06-20 12:09:02 +02002872static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002873 /* mixer enable */
2874 {0x1, 0xF88, 0x3},
2875 /* GPIO 0~2 */
2876 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002877 { }
2878};
2879
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002880static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2881{
2882 int imux_is_smixer =
2883 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2884 unsigned int parm;
2885 /* inputs */
2886 /* PW 1/2/5 (14h/15h/18h) */
2887 parm = AC_PWRST_D3;
2888 set_pin_power_state(codec, 0x14, &parm);
2889 set_pin_power_state(codec, 0x15, &parm);
2890 set_pin_power_state(codec, 0x18, &parm);
2891 if (imux_is_smixer)
2892 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2893 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2894 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2895 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2896 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2897 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2898
2899 /* outputs */
2900 /* PW 3/4 (16h/17h) */
2901 parm = AC_PWRST_D3;
2902 set_pin_power_state(codec, 0x17, &parm);
2903 set_pin_power_state(codec, 0x16, &parm);
2904 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2905 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2906 imux_is_smixer ? AC_PWRST_D0 : parm);
2907 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2908 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2909}
2910
Harald Welted949cac2008-09-09 15:56:01 +08002911static int patch_vt1702(struct hda_codec *codec)
2912{
2913 struct via_spec *spec;
2914 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002915
2916 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002917 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002918 if (spec == NULL)
2919 return -ENOMEM;
2920
Takashi Iwai620e2b22011-06-17 17:19:19 +02002921 spec->aa_mix_nid = 0x1a;
2922
Takashi Iwai12daef62011-06-18 17:45:49 +02002923 /* limit AA path volume to 0 dB */
2924 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2925 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2926 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2927 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2928 (1 << AC_AMPCAP_MUTE_SHIFT));
2929
Harald Welted949cac2008-09-09 15:56:01 +08002930 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002931 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002932 if (err < 0) {
2933 via_free(codec);
2934 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002935 }
2936
Takashi Iwai096a8852011-06-20 12:09:02 +02002937 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002938
Harald Welted949cac2008-09-09 15:56:01 +08002939 codec->patch_ops = via_patch_ops;
2940
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002941 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002942 return 0;
2943}
2944
Lydia Wangeb7188c2009-10-10 19:08:34 +08002945/* Patch for VT1718S */
2946
Takashi Iwai096a8852011-06-20 12:09:02 +02002947static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002948 /* Enable MW0 adjust Gain 5 */
2949 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002950 /* Enable Boost Volume backdoor */
2951 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002952
Lydia Wangeb7188c2009-10-10 19:08:34 +08002953 { }
2954};
2955
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002956static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2957{
2958 struct via_spec *spec = codec->spec;
2959 int imux_is_smixer;
2960 unsigned int parm;
2961 /* MUX6 (1eh) = stereo mixer */
2962 imux_is_smixer =
2963 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2964 /* inputs */
2965 /* PW 5/6/7 (29h/2ah/2bh) */
2966 parm = AC_PWRST_D3;
2967 set_pin_power_state(codec, 0x29, &parm);
2968 set_pin_power_state(codec, 0x2a, &parm);
2969 set_pin_power_state(codec, 0x2b, &parm);
2970 if (imux_is_smixer)
2971 parm = AC_PWRST_D0;
2972 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2973 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2974 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2975 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2976 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2977
2978 /* outputs */
2979 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2980 parm = AC_PWRST_D3;
2981 set_pin_power_state(codec, 0x27, &parm);
2982 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2983 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2984
2985 /* PW2 (26h), AOW2 (ah) */
2986 parm = AC_PWRST_D3;
2987 set_pin_power_state(codec, 0x26, &parm);
2988 if (spec->smart51_enabled)
2989 set_pin_power_state(codec, 0x2b, &parm);
2990 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2991
2992 /* PW0 (24h), AOW0 (8h) */
2993 parm = AC_PWRST_D3;
2994 set_pin_power_state(codec, 0x24, &parm);
2995 if (!spec->hp_independent_mode) /* check for redirected HP */
2996 set_pin_power_state(codec, 0x28, &parm);
2997 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2998 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2999 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
3000 imux_is_smixer ? AC_PWRST_D0 : parm);
3001
3002 /* PW1 (25h), AOW1 (9h) */
3003 parm = AC_PWRST_D3;
3004 set_pin_power_state(codec, 0x25, &parm);
3005 if (spec->smart51_enabled)
3006 set_pin_power_state(codec, 0x2a, &parm);
3007 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
3008
3009 if (spec->hp_independent_mode) {
3010 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3011 parm = AC_PWRST_D3;
3012 set_pin_power_state(codec, 0x28, &parm);
3013 snd_hda_codec_write(codec, 0x1b, 0,
3014 AC_VERB_SET_POWER_STATE, parm);
3015 snd_hda_codec_write(codec, 0x34, 0,
3016 AC_VERB_SET_POWER_STATE, parm);
3017 snd_hda_codec_write(codec, 0xc, 0,
3018 AC_VERB_SET_POWER_STATE, parm);
3019 }
3020}
3021
Lydia Wangeb7188c2009-10-10 19:08:34 +08003022static int patch_vt1718S(struct hda_codec *codec)
3023{
3024 struct via_spec *spec;
3025 int err;
3026
3027 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003028 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003029 if (spec == NULL)
3030 return -ENOMEM;
3031
Takashi Iwai620e2b22011-06-17 17:19:19 +02003032 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003033 override_mic_boost(codec, 0x2b, 0, 3, 40);
3034 override_mic_boost(codec, 0x29, 0, 3, 40);
Lydia Wangc4394f52011-07-04 16:54:15 +08003035 spec->dac_mixer_idx = 5;
Takashi Iwai620e2b22011-06-17 17:19:19 +02003036
Lydia Wangeb7188c2009-10-10 19:08:34 +08003037 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003038 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003039 if (err < 0) {
3040 via_free(codec);
3041 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003042 }
3043
Takashi Iwai096a8852011-06-20 12:09:02 +02003044 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003045
Lydia Wangeb7188c2009-10-10 19:08:34 +08003046 codec->patch_ops = via_patch_ops;
3047
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003048 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3049
Lydia Wangeb7188c2009-10-10 19:08:34 +08003050 return 0;
3051}
Lydia Wangf3db4232009-10-10 19:08:41 +08003052
3053/* Patch for VT1716S */
3054
3055static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3056 struct snd_ctl_elem_info *uinfo)
3057{
3058 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3059 uinfo->count = 1;
3060 uinfo->value.integer.min = 0;
3061 uinfo->value.integer.max = 1;
3062 return 0;
3063}
3064
3065static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3066 struct snd_ctl_elem_value *ucontrol)
3067{
3068 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3069 int index = 0;
3070
3071 index = snd_hda_codec_read(codec, 0x26, 0,
3072 AC_VERB_GET_CONNECT_SEL, 0);
3073 if (index != -1)
3074 *ucontrol->value.integer.value = index;
3075
3076 return 0;
3077}
3078
3079static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3080 struct snd_ctl_elem_value *ucontrol)
3081{
3082 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3083 struct via_spec *spec = codec->spec;
3084 int index = *ucontrol->value.integer.value;
3085
3086 snd_hda_codec_write(codec, 0x26, 0,
3087 AC_VERB_SET_CONNECT_SEL, index);
3088 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003089 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003090 return 1;
3091}
3092
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003093static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003094 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3095 {
3096 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3097 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003098 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003099 .count = 1,
3100 .info = vt1716s_dmic_info,
3101 .get = vt1716s_dmic_get,
3102 .put = vt1716s_dmic_put,
3103 },
3104 {} /* end */
3105};
3106
3107
3108/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003109static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003110 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3111 { } /* end */
3112};
3113
Takashi Iwai096a8852011-06-20 12:09:02 +02003114static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003115 /* Enable Boost Volume backdoor */
3116 {0x1, 0xf8a, 0x80},
3117 /* don't bybass mixer */
3118 {0x1, 0xf88, 0xc0},
3119 /* Enable mono output */
3120 {0x1, 0xf90, 0x08},
3121 { }
3122};
3123
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003124static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3125{
3126 struct via_spec *spec = codec->spec;
3127 int imux_is_smixer;
3128 unsigned int parm;
3129 unsigned int mono_out, present;
3130 /* SW0 (17h) = stereo mixer */
3131 imux_is_smixer =
3132 (snd_hda_codec_read(codec, 0x17, 0,
3133 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3134 /* inputs */
3135 /* PW 1/2/5 (1ah/1bh/1eh) */
3136 parm = AC_PWRST_D3;
3137 set_pin_power_state(codec, 0x1a, &parm);
3138 set_pin_power_state(codec, 0x1b, &parm);
3139 set_pin_power_state(codec, 0x1e, &parm);
3140 if (imux_is_smixer)
3141 parm = AC_PWRST_D0;
3142 /* SW0 (17h), AIW0(13h) */
3143 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3144 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3145
3146 parm = AC_PWRST_D3;
3147 set_pin_power_state(codec, 0x1e, &parm);
3148 /* PW11 (22h) */
3149 if (spec->dmic_enabled)
3150 set_pin_power_state(codec, 0x22, &parm);
3151 else
3152 snd_hda_codec_write(codec, 0x22, 0,
3153 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3154
3155 /* SW2(26h), AIW1(14h) */
3156 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3157 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3158
3159 /* outputs */
3160 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3161 parm = AC_PWRST_D3;
3162 set_pin_power_state(codec, 0x19, &parm);
3163 /* Smart 5.1 PW2(1bh) */
3164 if (spec->smart51_enabled)
3165 set_pin_power_state(codec, 0x1b, &parm);
3166 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3167 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3168
3169 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3170 parm = AC_PWRST_D3;
3171 set_pin_power_state(codec, 0x23, &parm);
3172 /* Smart 5.1 PW1(1ah) */
3173 if (spec->smart51_enabled)
3174 set_pin_power_state(codec, 0x1a, &parm);
3175 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3176
3177 /* Smart 5.1 PW5(1eh) */
3178 if (spec->smart51_enabled)
3179 set_pin_power_state(codec, 0x1e, &parm);
3180 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3181
3182 /* Mono out */
3183 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3184 present = snd_hda_jack_detect(codec, 0x1c);
3185
3186 if (present)
3187 mono_out = 0;
3188 else {
3189 present = snd_hda_jack_detect(codec, 0x1d);
3190 if (!spec->hp_independent_mode && present)
3191 mono_out = 0;
3192 else
3193 mono_out = 1;
3194 }
3195 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3196 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3197 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3198 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3199
3200 /* PW 3/4 (1ch/1dh) */
3201 parm = AC_PWRST_D3;
3202 set_pin_power_state(codec, 0x1c, &parm);
3203 set_pin_power_state(codec, 0x1d, &parm);
3204 /* HP Independent Mode, power on AOW3 */
3205 if (spec->hp_independent_mode)
3206 snd_hda_codec_write(codec, 0x25, 0,
3207 AC_VERB_SET_POWER_STATE, parm);
3208
3209 /* force to D0 for internal Speaker */
3210 /* MW0 (16h), AOW0 (10h) */
3211 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3212 imux_is_smixer ? AC_PWRST_D0 : parm);
3213 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3214 mono_out ? AC_PWRST_D0 : parm);
3215}
3216
Lydia Wangf3db4232009-10-10 19:08:41 +08003217static int patch_vt1716S(struct hda_codec *codec)
3218{
3219 struct via_spec *spec;
3220 int err;
3221
3222 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003223 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003224 if (spec == NULL)
3225 return -ENOMEM;
3226
Takashi Iwai620e2b22011-06-17 17:19:19 +02003227 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003228 override_mic_boost(codec, 0x1a, 0, 3, 40);
3229 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003230
Lydia Wangf3db4232009-10-10 19:08:41 +08003231 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003232 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003233 if (err < 0) {
3234 via_free(codec);
3235 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003236 }
3237
Takashi Iwai096a8852011-06-20 12:09:02 +02003238 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003239
Lydia Wangf3db4232009-10-10 19:08:41 +08003240 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3241 spec->num_mixers++;
3242
3243 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3244
3245 codec->patch_ops = via_patch_ops;
3246
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003247 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003248 return 0;
3249}
Lydia Wang25eaba22009-10-10 19:08:43 +08003250
3251/* for vt2002P */
3252
Takashi Iwai096a8852011-06-20 12:09:02 +02003253static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003254 /* Class-D speaker related verbs */
3255 {0x1, 0xfe0, 0x4},
3256 {0x1, 0xfe9, 0x80},
3257 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003258 /* Enable Boost Volume backdoor */
3259 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003260 /* Enable AOW0 to MW9 */
3261 {0x1, 0xfb8, 0x88},
3262 { }
3263};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003264
Takashi Iwai096a8852011-06-20 12:09:02 +02003265static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003266 /* Enable Boost Volume backdoor */
3267 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003268 /* Enable AOW0 to MW9 */
3269 {0x1, 0xfb8, 0x88},
3270 { }
3271};
Lydia Wang25eaba22009-10-10 19:08:43 +08003272
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003273static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3274{
3275 struct via_spec *spec = codec->spec;
3276 int imux_is_smixer;
3277 unsigned int parm;
3278 unsigned int present;
3279 /* MUX9 (1eh) = stereo mixer */
3280 imux_is_smixer =
3281 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3282 /* inputs */
3283 /* PW 5/6/7 (29h/2ah/2bh) */
3284 parm = AC_PWRST_D3;
3285 set_pin_power_state(codec, 0x29, &parm);
3286 set_pin_power_state(codec, 0x2a, &parm);
3287 set_pin_power_state(codec, 0x2b, &parm);
3288 parm = AC_PWRST_D0;
3289 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3290 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3291 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3292 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3293 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3294
3295 /* outputs */
3296 /* AOW0 (8h)*/
3297 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3298
Lydia Wang118909562011-03-23 17:57:34 +08003299 if (spec->codec_type == VT1802) {
3300 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3301 parm = AC_PWRST_D3;
3302 set_pin_power_state(codec, 0x28, &parm);
3303 snd_hda_codec_write(codec, 0x18, 0,
3304 AC_VERB_SET_POWER_STATE, parm);
3305 snd_hda_codec_write(codec, 0x38, 0,
3306 AC_VERB_SET_POWER_STATE, parm);
3307 } else {
3308 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3309 parm = AC_PWRST_D3;
3310 set_pin_power_state(codec, 0x26, &parm);
3311 snd_hda_codec_write(codec, 0x1c, 0,
3312 AC_VERB_SET_POWER_STATE, parm);
3313 snd_hda_codec_write(codec, 0x37, 0,
3314 AC_VERB_SET_POWER_STATE, parm);
3315 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003316
Lydia Wang118909562011-03-23 17:57:34 +08003317 if (spec->codec_type == VT1802) {
3318 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3319 parm = AC_PWRST_D3;
3320 set_pin_power_state(codec, 0x25, &parm);
3321 snd_hda_codec_write(codec, 0x15, 0,
3322 AC_VERB_SET_POWER_STATE, parm);
3323 snd_hda_codec_write(codec, 0x35, 0,
3324 AC_VERB_SET_POWER_STATE, parm);
3325 } else {
3326 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3327 parm = AC_PWRST_D3;
3328 set_pin_power_state(codec, 0x25, &parm);
3329 snd_hda_codec_write(codec, 0x19, 0,
3330 AC_VERB_SET_POWER_STATE, parm);
3331 snd_hda_codec_write(codec, 0x35, 0,
3332 AC_VERB_SET_POWER_STATE, parm);
3333 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003334
3335 if (spec->hp_independent_mode)
3336 snd_hda_codec_write(codec, 0x9, 0,
3337 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3338
3339 /* Class-D */
3340 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3341 present = snd_hda_jack_detect(codec, 0x25);
3342
3343 parm = AC_PWRST_D3;
3344 set_pin_power_state(codec, 0x24, &parm);
3345 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003346 if (spec->codec_type == VT1802)
3347 snd_hda_codec_write(codec, 0x14, 0,
3348 AC_VERB_SET_POWER_STATE, parm);
3349 else
3350 snd_hda_codec_write(codec, 0x18, 0,
3351 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003352 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3353
3354 /* Mono Out */
3355 present = snd_hda_jack_detect(codec, 0x26);
3356
3357 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003358 if (spec->codec_type == VT1802) {
3359 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3360 snd_hda_codec_write(codec, 0x33, 0,
3361 AC_VERB_SET_POWER_STATE, parm);
3362 snd_hda_codec_write(codec, 0x1c, 0,
3363 AC_VERB_SET_POWER_STATE, parm);
3364 snd_hda_codec_write(codec, 0x3c, 0,
3365 AC_VERB_SET_POWER_STATE, parm);
3366 } else {
3367 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3368 snd_hda_codec_write(codec, 0x31, 0,
3369 AC_VERB_SET_POWER_STATE, parm);
3370 snd_hda_codec_write(codec, 0x17, 0,
3371 AC_VERB_SET_POWER_STATE, parm);
3372 snd_hda_codec_write(codec, 0x3b, 0,
3373 AC_VERB_SET_POWER_STATE, parm);
3374 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003375 /* MW9 (21h) */
3376 if (imux_is_smixer || !is_aa_path_mute(codec))
3377 snd_hda_codec_write(codec, 0x21, 0,
3378 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3379 else
3380 snd_hda_codec_write(codec, 0x21, 0,
3381 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3382}
Lydia Wang25eaba22009-10-10 19:08:43 +08003383
3384/* patch for vt2002P */
3385static int patch_vt2002P(struct hda_codec *codec)
3386{
3387 struct via_spec *spec;
3388 int err;
3389
3390 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003391 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003392 if (spec == NULL)
3393 return -ENOMEM;
3394
Takashi Iwai620e2b22011-06-17 17:19:19 +02003395 spec->aa_mix_nid = 0x21;
Lydia Wang5c9a5612011-07-08 14:03:43 +08003396 spec->dac_mixer_idx = 3;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003397 override_mic_boost(codec, 0x2b, 0, 3, 40);
3398 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003399
Lydia Wang25eaba22009-10-10 19:08:43 +08003400 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003401 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003402 if (err < 0) {
3403 via_free(codec);
3404 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003405 }
3406
Lydia Wang118909562011-03-23 17:57:34 +08003407 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003408 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003409 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003410 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003411
Lydia Wang25eaba22009-10-10 19:08:43 +08003412 codec->patch_ops = via_patch_ops;
3413
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003414 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003415 return 0;
3416}
Lydia Wangab6734e2009-10-10 19:08:46 +08003417
3418/* for vt1812 */
3419
Takashi Iwai096a8852011-06-20 12:09:02 +02003420static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003421 /* Enable Boost Volume backdoor */
3422 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003423 /* Enable AOW0 to MW9 */
3424 {0x1, 0xfb8, 0xa8},
3425 { }
3426};
3427
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003428static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3429{
3430 struct via_spec *spec = codec->spec;
3431 int imux_is_smixer =
3432 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3433 unsigned int parm;
3434 unsigned int present;
3435 /* MUX10 (1eh) = stereo mixer */
3436 imux_is_smixer =
3437 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3438 /* inputs */
3439 /* PW 5/6/7 (29h/2ah/2bh) */
3440 parm = AC_PWRST_D3;
3441 set_pin_power_state(codec, 0x29, &parm);
3442 set_pin_power_state(codec, 0x2a, &parm);
3443 set_pin_power_state(codec, 0x2b, &parm);
3444 parm = AC_PWRST_D0;
3445 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3446 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3447 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3448 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3449 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3450
3451 /* outputs */
3452 /* AOW0 (8h)*/
3453 snd_hda_codec_write(codec, 0x8, 0,
3454 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3455
3456 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3457 parm = AC_PWRST_D3;
3458 set_pin_power_state(codec, 0x28, &parm);
3459 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3460 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3461
3462 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3463 parm = AC_PWRST_D3;
3464 set_pin_power_state(codec, 0x25, &parm);
3465 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3466 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3467 if (spec->hp_independent_mode)
3468 snd_hda_codec_write(codec, 0x9, 0,
3469 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3470
3471 /* Internal Speaker */
3472 /* PW0 (24h), MW0(14h), MUX0(34h) */
3473 present = snd_hda_jack_detect(codec, 0x25);
3474
3475 parm = AC_PWRST_D3;
3476 set_pin_power_state(codec, 0x24, &parm);
3477 if (present) {
3478 snd_hda_codec_write(codec, 0x14, 0,
3479 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3480 snd_hda_codec_write(codec, 0x34, 0,
3481 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3482 } else {
3483 snd_hda_codec_write(codec, 0x14, 0,
3484 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3485 snd_hda_codec_write(codec, 0x34, 0,
3486 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3487 }
3488
3489
3490 /* Mono Out */
3491 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3492 present = snd_hda_jack_detect(codec, 0x28);
3493
3494 parm = AC_PWRST_D3;
3495 set_pin_power_state(codec, 0x31, &parm);
3496 if (present) {
3497 snd_hda_codec_write(codec, 0x1c, 0,
3498 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3499 snd_hda_codec_write(codec, 0x3c, 0,
3500 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3501 snd_hda_codec_write(codec, 0x3e, 0,
3502 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3503 } else {
3504 snd_hda_codec_write(codec, 0x1c, 0,
3505 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3506 snd_hda_codec_write(codec, 0x3c, 0,
3507 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3508 snd_hda_codec_write(codec, 0x3e, 0,
3509 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3510 }
3511
3512 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3513 parm = AC_PWRST_D3;
3514 set_pin_power_state(codec, 0x33, &parm);
3515 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3516 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3517
3518}
Lydia Wangab6734e2009-10-10 19:08:46 +08003519
3520/* patch for vt1812 */
3521static int patch_vt1812(struct hda_codec *codec)
3522{
3523 struct via_spec *spec;
3524 int err;
3525
3526 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003527 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003528 if (spec == NULL)
3529 return -ENOMEM;
3530
Takashi Iwai620e2b22011-06-17 17:19:19 +02003531 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003532 override_mic_boost(codec, 0x2b, 0, 3, 40);
3533 override_mic_boost(codec, 0x29, 0, 3, 40);
Lydia Wang28dc10a2011-07-08 18:28:47 +08003534 spec->dac_mixer_idx = 5;
Takashi Iwai620e2b22011-06-17 17:19:19 +02003535
Lydia Wangab6734e2009-10-10 19:08:46 +08003536 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003537 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003538 if (err < 0) {
3539 via_free(codec);
3540 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003541 }
3542
Takashi Iwai096a8852011-06-20 12:09:02 +02003543 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003544
Lydia Wangab6734e2009-10-10 19:08:46 +08003545 codec->patch_ops = via_patch_ops;
3546
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003547 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003548 return 0;
3549}
3550
Joseph Chanc577b8a2006-11-29 15:29:40 +01003551/*
3552 * patch entries
3553 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003554static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003555 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3556 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3557 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3558 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3559 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003560 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003561 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003562 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003563 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003564 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003565 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003566 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003567 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003568 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003569 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003570 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003571 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003572 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003573 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003574 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003575 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003576 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003577 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003578 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003579 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003580 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003581 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003582 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003583 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003584 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003585 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003586 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003587 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003588 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003589 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003590 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003591 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003592 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003593 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003594 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003595 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003596 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003597 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003598 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003599 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003600 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003601 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003602 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003603 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003604 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003605 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003606 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003607 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003608 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003609 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003610 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003611 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003612 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003613 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003614 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003615 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003616 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003617 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003618 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003619 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003620 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003621 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003622 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003623 { .id = 0x11060428, .name = "VT1718S",
3624 .patch = patch_vt1718S},
3625 { .id = 0x11064428, .name = "VT1718S",
3626 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003627 { .id = 0x11060441, .name = "VT2020",
3628 .patch = patch_vt1718S},
3629 { .id = 0x11064441, .name = "VT1828S",
3630 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003631 { .id = 0x11060433, .name = "VT1716S",
3632 .patch = patch_vt1716S},
3633 { .id = 0x1106a721, .name = "VT1716S",
3634 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003635 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3636 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003637 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003638 { .id = 0x11060440, .name = "VT1818S",
3639 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003640 { .id = 0x11060446, .name = "VT1802",
3641 .patch = patch_vt2002P},
3642 { .id = 0x11068446, .name = "VT1802",
3643 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003644 {} /* terminator */
3645};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003646
3647MODULE_ALIAS("snd-hda-codec-id:1106*");
3648
3649static struct hda_codec_preset_list via_list = {
3650 .preset = snd_hda_preset_via,
3651 .owner = THIS_MODULE,
3652};
3653
3654MODULE_LICENSE("GPL");
3655MODULE_DESCRIPTION("VIA HD-audio codec");
3656
3657static int __init patch_via_init(void)
3658{
3659 return snd_hda_add_codec_preset(&via_list);
3660}
3661
3662static void __exit patch_via_exit(void)
3663{
3664 snd_hda_delete_codec_preset(&via_list);
3665}
3666
3667module_init(patch_via_init)
3668module_exit(patch_via_exit)