blob: b289abf0db5507e0bb65b41ba252e6c27c806cc0 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010052#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080053#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010054#include "hda_codec.h"
55#include "hda_local.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010056
Joseph Chanc577b8a2006-11-29 15:29:40 +010057/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080058#define VT1708_HP_PIN_NID 0x20
59#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010060
Harald Welted7426322008-09-15 22:43:23 +080061enum VIA_HDA_CODEC {
62 UNKNOWN = -1,
63 VT1708,
64 VT1709_10CH,
65 VT1709_6CH,
66 VT1708B_8CH,
67 VT1708B_4CH,
68 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080069 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080070 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080071 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080072 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080073 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080074 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080075 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080076 CODEC_TYPES,
77};
78
Lydia Wang118909562011-03-23 17:57:34 +080079#define VT2002P_COMPATIBLE(spec) \
80 ((spec)->codec_type == VT2002P ||\
81 (spec)->codec_type == VT1812 ||\
82 (spec)->codec_type == VT1802)
83
Takashi Iwai8e3679d2011-06-21 09:01:36 +020084#define MAX_NID_PATH_DEPTH 5
85
Takashi Iwai09a9ad62011-06-21 15:57:44 +020086/* output-path: DAC -> ... -> pin
87 * idx[] contains the source index number of the next widget;
88 * e.g. idx[0] is the index of the DAC selected by path[1] widget
89 * multi[] indicates whether it's a selector widget with multi-connectors
90 * (i.e. the connection selection is mandatory)
91 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
92 */
Takashi Iwai4a796162011-06-17 17:53:38 +020093struct nid_path {
94 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020095 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad62011-06-21 15:57:44 +020096 unsigned char idx[MAX_NID_PATH_DEPTH];
97 unsigned char multi[MAX_NID_PATH_DEPTH];
98 unsigned int vol_ctl;
99 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200100};
101
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200102/* input-path */
103struct via_input {
104 hda_nid_t pin; /* input-pin or aa-mix */
105 int adc_idx; /* ADC index to be used */
106 int mux_idx; /* MUX index (if any) */
107 const char *label; /* input-source label */
108};
109
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 Wangbb3c6bf2009-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 Iwai09a9ad62011-06-21 15:57:44 +0200438#define have_mute(codec, nid, dir) \
439 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200440
Lydia Wangd69607b2011-07-08 14:02:52 +0800441static bool is_node_in_path(struct nid_path *path, hda_nid_t nid)
442{
443 int i;
444 if (!nid)
445 return false;
446 for (i = 0; i < path->depth; i++) {
447 if (path->path[i] == nid)
448 return true;
449 }
450 return false;
451}
452
453/* enable/disable the output-route mixers */
454static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
455 hda_nid_t mix_nid, int aa_mix_idx, bool enable)
456{
457 int i, num, val;
458 bool hp_path, front_path;
459 struct via_spec *spec = codec->spec;
460
461 if (!path)
462 return;
463 num = snd_hda_get_conn_list(codec, mix_nid, NULL);
464 hp_path = is_node_in_path(path, spec->hp_dac_nid);
465 front_path = is_node_in_path(path, spec->multiout.dac_nids[0]);
466
467 for (i = 0; i < num; i++) {
468 if (i == aa_mix_idx) {
469 if (hp_path)
470 val = enable ? AMP_IN_MUTE(i) :
471 AMP_IN_UNMUTE(i);
472 else if (front_path)
473 val = AMP_IN_UNMUTE(i);
474 else
475 val = AMP_IN_MUTE(i);
476 } else {
477 if (hp_path)
478 val = enable ? AMP_IN_UNMUTE(i) :
479 AMP_IN_MUTE(i);
480 else if (front_path)
481 val = AMP_IN_MUTE(i);
482 else
483 val = AMP_IN_UNMUTE(i);
484 }
485 snd_hda_codec_write(codec, mix_nid, 0,
486 AC_VERB_SET_AMP_GAIN_MUTE, val);
487 }
488}
489
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200490/* enable/disable the output-route */
491static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
492 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200493{
Lydia Wangd69607b2011-07-08 14:02:52 +0800494 int i, val;
495 struct via_spec *spec = codec->spec;
496 hda_nid_t aa_mix_nid = spec->aa_mix_nid;
Takashi Iwai09a9ad62011-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 Iwai09a9ad62011-06-21 15:57:44 +0200512 if (have_mute(codec, dst, HDA_INPUT)) {
Lydia Wangd69607b2011-07-08 14:02:52 +0800513 if (dst == aa_mix_nid) {
514 val = enable ? AMP_IN_UNMUTE(idx) :
515 AMP_IN_MUTE(idx);
516 snd_hda_codec_write(codec, dst, 0,
517 AC_VERB_SET_AMP_GAIN_MUTE, val);
518 } else {
519 idx = get_connection_index(codec, dst,
520 aa_mix_nid);
521 if (idx >= 0) {
522 activate_output_mix(codec, path,
523 dst, idx, enable);
524 }
525 }
Takashi Iwai09a9ad62011-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 Iwai09a9ad62011-06-21 15:57:44 +0200550static void via_auto_init_output(struct hda_codec *codec,
551 struct nid_path *path, int pin_type,
Takashi Iwaibac4b922011-07-04 17:35:51 +0200552 bool with_aa_mix, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200553{
554 struct via_spec *spec = codec->spec;
555 unsigned int caps;
Lydia Wangd69607b2011-07-08 14:02:52 +0800556 hda_nid_t pin;
Takashi Iwai5d417622011-06-20 11:32:27 +0200557
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200558 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200559 return;
Takashi Iwai09a9ad62011-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 Iwai09a9ad62011-06-21 15:57:44 +0200571 /* initialize the AA-path */
572 if (!spec->aa_mix_nid)
573 return;
Lydia Wangd69607b2011-07-08 14:02:52 +0800574 activate_output_path(codec, path, true, force);
Takashi Iwai5d417622011-06-20 11:32:27 +0200575}
576
Joseph Chanc577b8a2006-11-29 15:29:40 +0100577static void via_auto_init_multi_out(struct hda_codec *codec)
578{
579 struct via_spec *spec = codec->spec;
580 int i;
581
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200582 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwaibac4b922011-07-04 17:35:51 +0200583 /* enable aa-mute only for the front channel */
584 via_auto_init_output(codec, &spec->out_path[i], PIN_OUT,
585 i == 0, true);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100586}
587
588static void via_auto_init_hp_out(struct hda_codec *codec)
589{
590 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100591
Takashi Iwai09a9ad62011-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 Iwai09a9ad62011-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 Iwai09a9ad62011-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 Iwai09a9ad62011-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 Iwai09a9ad62011-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 Iwai09a9ad62011-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
1609 if (res == VIA_HP_EVENT)
1610 via_hp_automute(codec);
1611 else if (res == VIA_GPIO_EVENT)
1612 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001613 else if (res == VIA_LINE_EVENT)
1614 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001615}
1616
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001617#ifdef SND_HDA_NEEDS_RESUME
1618static int via_suspend(struct hda_codec *codec, pm_message_t state)
1619{
1620 struct via_spec *spec = codec->spec;
1621 vt1708_stop_hp_work(spec);
1622 return 0;
1623}
1624#endif
1625
Takashi Iwaicb53c622007-08-10 17:21:45 +02001626#ifdef CONFIG_SND_HDA_POWER_SAVE
1627static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1628{
1629 struct via_spec *spec = codec->spec;
1630 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1631}
1632#endif
1633
Joseph Chanc577b8a2006-11-29 15:29:40 +01001634/*
1635 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001636
1637static int via_init(struct hda_codec *codec);
1638
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001639static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001640 .build_controls = via_build_controls,
1641 .build_pcms = via_build_pcms,
1642 .init = via_init,
1643 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001644 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001645#ifdef SND_HDA_NEEDS_RESUME
1646 .suspend = via_suspend,
1647#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001648#ifdef CONFIG_SND_HDA_POWER_SAVE
1649 .check_power_status = via_check_power_status,
1650#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001651};
1652
Takashi Iwai4a796162011-06-17 17:53:38 +02001653static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001654{
Takashi Iwai4a796162011-06-17 17:53:38 +02001655 struct via_spec *spec = codec->spec;
1656 int i;
1657
1658 for (i = 0; i < spec->multiout.num_dacs; i++) {
1659 if (spec->multiout.dac_nids[i] == dac)
1660 return false;
1661 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001662 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001663 return false;
1664 return true;
1665}
1666
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001667static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001668 hda_nid_t target_dac, struct nid_path *path,
1669 int depth, int wid_type)
1670{
1671 hda_nid_t conn[8];
1672 int i, nums;
1673
1674 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1675 for (i = 0; i < nums; i++) {
1676 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1677 continue;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001678 if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
1679 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001680 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001681 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001682 return false;
1683 for (i = 0; i < nums; i++) {
1684 unsigned int type;
1685 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1686 if (type == AC_WID_AUD_OUT ||
1687 (wid_type != -1 && type != wid_type))
1688 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001689 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001690 path, depth + 1, AC_WID_AUD_SEL))
1691 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001692 }
1693 return false;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001694
1695 found:
1696 path->path[path->depth] = conn[i];
1697 path->idx[path->depth] = i;
1698 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1699 path->multi[path->depth] = 1;
1700 path->depth++;
1701 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001702}
1703
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001704static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1705 hda_nid_t target_dac, struct nid_path *path)
1706{
1707 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1708 path->path[path->depth] = nid;
1709 path->depth++;
1710 return true;
1711 }
1712 return false;
1713}
1714
Takashi Iwai4a796162011-06-17 17:53:38 +02001715static int via_auto_fill_dac_nids(struct hda_codec *codec)
1716{
1717 struct via_spec *spec = codec->spec;
1718 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001719 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001720 hda_nid_t nid;
1721
Joseph Chanc577b8a2006-11-29 15:29:40 +01001722 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001723 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001724 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001725 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001726 if (!nid)
1727 continue;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001728 if (parse_output_path(codec, nid, 0, &spec->out_path[i])) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001729 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Lydia Wang5c9a5612011-07-08 14:03:43 +08001730 dac_num++;
1731 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001732 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001733 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001734 return 0;
1735}
1736
Takashi Iwai4a796162011-06-17 17:53:38 +02001737static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001738 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001739{
Takashi Iwai4a796162011-06-17 17:53:38 +02001740 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001741 char name[32];
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001742 hda_nid_t dac, pin, sel, nid;
1743 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001744
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001745 dac = check_dac ? path->path[0] : 0;
1746 pin = path->path[path->depth - 1];
1747 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001748
Takashi Iwai8df2a312011-06-21 11:48:29 +02001749 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001750 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001751 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001752 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001753 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1754 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001755 else
1756 nid = 0;
1757 if (nid) {
1758 sprintf(name, "%s Playback Volume", pfx);
1759 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001760 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001761 if (err < 0)
1762 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001763 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001764 }
1765
Takashi Iwai8df2a312011-06-21 11:48:29 +02001766 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001767 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001768 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001769 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001770 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1771 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001772 else
1773 nid = 0;
1774 if (nid) {
1775 sprintf(name, "%s Playback Switch", pfx);
1776 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1777 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1778 if (err < 0)
1779 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001780 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001781 }
1782 return 0;
1783}
1784
Takashi Iwaif4a78282011-06-17 18:46:48 +02001785static void mangle_smart51(struct hda_codec *codec)
1786{
1787 struct via_spec *spec = codec->spec;
1788 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001789 struct auto_pin_cfg_item *ins = cfg->inputs;
1790 int i, j, nums, attr;
1791 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001792
Takashi Iwai0f98c242011-06-21 12:51:33 +02001793 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1794 nums = 0;
1795 for (i = 0; i < cfg->num_inputs; i++) {
1796 unsigned int def;
1797 if (ins[i].type > AUTO_PIN_LINE_IN)
1798 continue;
1799 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1800 if (snd_hda_get_input_pin_attr(def) != attr)
1801 continue;
1802 for (j = 0; j < nums; j++)
1803 if (ins[pins[j]].type < ins[i].type) {
1804 memmove(pins + j + 1, pins + j,
1805 (nums - j - 1) * sizeof(int));
1806 break;
1807 }
1808 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001809 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001810 }
1811 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001812 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001813 for (i = 0; i < nums; i++) {
1814 hda_nid_t pin = ins[pins[i]].pin;
1815 spec->smart51_pins[spec->smart51_nums++] = pin;
1816 cfg->line_out_pins[cfg->line_outs++] = pin;
1817 if (cfg->line_outs == 3)
1818 break;
1819 }
1820 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001821 }
1822}
1823
Takashi Iwai4a796162011-06-17 17:53:38 +02001824/* add playback controls from the parsed DAC table */
1825static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1826{
1827 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001828 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001829 static const char * const chname[4] = {
1830 "Front", "Surround", "C/LFE", "Side"
1831 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001832 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001833 int old_line_outs;
1834
1835 /* check smart51 */
1836 old_line_outs = cfg->line_outs;
1837 if (cfg->line_outs == 1)
1838 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001839
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001840 err = via_auto_fill_dac_nids(codec);
1841 if (err < 0)
1842 return err;
1843
Lydia Wang5c9a5612011-07-08 14:03:43 +08001844 if (spec->multiout.num_dacs < 3) {
1845 spec->smart51_nums = 0;
1846 cfg->line_outs = old_line_outs;
1847 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001848 for (i = 0; i < cfg->line_outs; i++) {
1849 hda_nid_t pin, dac;
1850 pin = cfg->line_out_pins[i];
1851 dac = spec->multiout.dac_nids[i];
1852 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001853 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001854 if (i == HDA_CLFE) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001855 err = create_ch_ctls(codec, "Center", 1, true,
1856 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001857 if (err < 0)
1858 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001859 err = create_ch_ctls(codec, "LFE", 2, true,
1860 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001861 if (err < 0)
1862 return err;
1863 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001864 const char *pfx = chname[i];
1865 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1866 cfg->line_outs == 1)
1867 pfx = "Speaker";
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001868 err = create_ch_ctls(codec, pfx, 3, true,
1869 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001870 if (err < 0)
1871 return err;
1872 }
1873 }
1874
Takashi Iwai4a796162011-06-17 17:53:38 +02001875 idx = get_connection_index(codec, spec->aa_mix_nid,
1876 spec->multiout.dac_nids[0]);
Lydia Wangc4394f52011-07-04 16:54:15 +08001877 if (idx < 0 && spec->dac_mixer_idx)
1878 idx = spec->dac_mixer_idx;
Takashi Iwai4a796162011-06-17 17:53:38 +02001879 if (idx >= 0) {
1880 /* add control to mixer */
1881 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1882 "PCM Playback Volume",
1883 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1884 idx, HDA_INPUT));
1885 if (err < 0)
1886 return err;
1887 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1888 "PCM Playback Switch",
1889 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1890 idx, HDA_INPUT));
1891 if (err < 0)
1892 return err;
1893 }
1894
Takashi Iwaif4a78282011-06-17 18:46:48 +02001895 cfg->line_outs = old_line_outs;
1896
Joseph Chanc577b8a2006-11-29 15:29:40 +01001897 return 0;
1898}
1899
Takashi Iwai4a796162011-06-17 17:53:38 +02001900static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001901{
Takashi Iwai4a796162011-06-17 17:53:38 +02001902 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001903 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001904 bool check_dac;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001905 int err;
1906
1907 if (!pin)
1908 return 0;
1909
Takashi Iwai8df2a312011-06-21 11:48:29 +02001910 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001911 spec->hp_dac_nid = spec->hp_path.path[0];
Takashi Iwai25250502011-06-30 17:24:47 +02001912 else if (spec->multiout.dac_nids[HDA_SIDE] &&
1913 parse_output_path(codec, pin,
1914 spec->multiout.dac_nids[HDA_SIDE],
1915 &spec->hp_path)) {
1916 spec->hp_dac_nid = spec->hp_path.path[0];
1917 spec->hp_indep_shared = true;
1918 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001919
Takashi Iwaiece8d042011-06-19 16:24:21 +02001920 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001921 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001922 !spec->hp_dac_nid)
1923 return 0;
1924
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001925 if (spec->hp_dac_nid && !spec->hp_indep_shared) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001926 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001927 check_dac = true;
1928 } else {
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001929 path = &spec->hp_dep_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001930 check_dac = false;
1931 }
1932 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001933 if (err < 0)
1934 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001935 if (spec->hp_dac_nid) {
1936 spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
1937 spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
1938 }
Harald Welte0aa62ae2008-09-09 15:58:27 +08001939
Joseph Chanc577b8a2006-11-29 15:29:40 +01001940 return 0;
1941}
1942
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001943static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1944{
1945 struct via_spec *spec = codec->spec;
1946 hda_nid_t pin, dac;
1947
1948 pin = spec->autocfg.speaker_pins[0];
1949 if (!spec->autocfg.speaker_outs || !pin)
1950 return 0;
1951
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001952 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1953 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001954 spec->multiout.extra_out_nid[0] = dac;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001955 return create_ch_ctls(codec, "Speaker", 3, true,
1956 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001957 }
1958 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001959 &spec->speaker_path))
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001960 return create_ch_ctls(codec, "Speaker", 3, false,
1961 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001962
1963 return 0;
1964}
1965
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001966/* look for ADCs */
1967static int via_fill_adcs(struct hda_codec *codec)
1968{
1969 struct via_spec *spec = codec->spec;
1970 hda_nid_t nid = codec->start_nid;
1971 int i;
1972
1973 for (i = 0; i < codec->num_nodes; i++, nid++) {
1974 unsigned int wcaps = get_wcaps(codec, nid);
1975 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1976 continue;
1977 if (wcaps & AC_WCAP_DIGITAL)
1978 continue;
1979 if (!(wcaps & AC_WCAP_CONN_LIST))
1980 continue;
1981 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1982 return -ENOMEM;
1983 spec->adc_nids[spec->num_adc_nids++] = nid;
1984 }
1985 return 0;
1986}
1987
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001988/* input-src control */
1989static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
1990 struct snd_ctl_elem_info *uinfo)
1991{
1992 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1993 struct via_spec *spec = codec->spec;
1994
1995 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1996 uinfo->count = 1;
1997 uinfo->value.enumerated.items = spec->num_inputs;
1998 if (uinfo->value.enumerated.item >= spec->num_inputs)
1999 uinfo->value.enumerated.item = spec->num_inputs - 1;
2000 strcpy(uinfo->value.enumerated.name,
2001 spec->inputs[uinfo->value.enumerated.item].label);
2002 return 0;
2003}
2004
2005static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2006 struct snd_ctl_elem_value *ucontrol)
2007{
2008 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2009 struct via_spec *spec = codec->spec;
2010 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2011
2012 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2013 return 0;
2014}
2015
2016static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2017 struct snd_ctl_elem_value *ucontrol)
2018{
2019 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2020 struct via_spec *spec = codec->spec;
2021 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2022 hda_nid_t mux;
2023 int cur;
2024
2025 cur = ucontrol->value.enumerated.item[0];
2026 if (cur < 0 || cur >= spec->num_inputs)
2027 return -EINVAL;
2028 if (spec->cur_mux[idx] == cur)
2029 return 0;
2030 spec->cur_mux[idx] = cur;
2031 if (spec->dyn_adc_switch) {
2032 int adc_idx = spec->inputs[cur].adc_idx;
2033 mux = spec->mux_nids[adc_idx];
2034 via_dyn_adc_pcm_resetup(codec, cur);
2035 } else {
2036 mux = spec->mux_nids[idx];
2037 if (snd_BUG_ON(!mux))
2038 return -EINVAL;
2039 }
2040
2041 if (mux) {
2042 /* switch to D0 beofre change index */
2043 if (snd_hda_codec_read(codec, mux, 0,
2044 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2045 snd_hda_codec_write(codec, mux, 0,
2046 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2047 snd_hda_codec_write(codec, mux, 0,
2048 AC_VERB_SET_CONNECT_SEL,
2049 spec->inputs[cur].mux_idx);
2050 }
2051
2052 /* update jack power state */
2053 set_widgets_power_state(codec);
2054 return 0;
2055}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002056
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002057static const struct snd_kcontrol_new via_input_src_ctl = {
2058 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2059 /* The multiple "Capture Source" controls confuse alsamixer
2060 * So call somewhat different..
2061 */
2062 /* .name = "Capture Source", */
2063 .name = "Input Source",
2064 .info = via_mux_enum_info,
2065 .get = via_mux_enum_get,
2066 .put = via_mux_enum_put,
2067};
2068
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002069static int create_input_src_ctls(struct hda_codec *codec, int count)
2070{
2071 struct via_spec *spec = codec->spec;
2072 struct snd_kcontrol_new *knew;
2073
2074 if (spec->num_inputs <= 1 || !count)
2075 return 0; /* no need for single src */
2076
2077 knew = via_clone_control(spec, &via_input_src_ctl);
2078 if (!knew)
2079 return -ENOMEM;
2080 knew->count = count;
2081 return 0;
2082}
2083
2084/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002085static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2086{
2087 struct hda_amp_list *list;
2088
2089 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2090 return;
2091 list = spec->loopback_list + spec->num_loopbacks;
2092 list->nid = mix;
2093 list->dir = HDA_INPUT;
2094 list->idx = idx;
2095 spec->num_loopbacks++;
2096 spec->loopback.amplist = spec->loopback_list;
2097}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002098
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002099static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002100 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002101{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002102 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002103}
2104
2105/* add the input-route to the given pin */
2106static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002107{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002108 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002109 int c, idx;
2110
2111 spec->inputs[spec->num_inputs].adc_idx = -1;
2112 spec->inputs[spec->num_inputs].pin = pin;
2113 for (c = 0; c < spec->num_adc_nids; c++) {
2114 if (spec->mux_nids[c]) {
2115 idx = get_connection_index(codec, spec->mux_nids[c],
2116 pin);
2117 if (idx < 0)
2118 continue;
2119 spec->inputs[spec->num_inputs].mux_idx = idx;
2120 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002121 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002122 continue;
2123 }
2124 spec->inputs[spec->num_inputs].adc_idx = c;
2125 /* Can primary ADC satisfy all inputs? */
2126 if (!spec->dyn_adc_switch &&
2127 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2128 snd_printd(KERN_INFO
2129 "via: dynamic ADC switching enabled\n");
2130 spec->dyn_adc_switch = 1;
2131 }
2132 return true;
2133 }
2134 return false;
2135}
2136
2137static int get_mux_nids(struct hda_codec *codec);
2138
2139/* parse input-routes; fill ADCs, MUXs and input-src entries */
2140static int parse_analog_inputs(struct hda_codec *codec)
2141{
2142 struct via_spec *spec = codec->spec;
2143 const struct auto_pin_cfg *cfg = &spec->autocfg;
2144 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002145
2146 err = via_fill_adcs(codec);
2147 if (err < 0)
2148 return err;
2149 err = get_mux_nids(codec);
2150 if (err < 0)
2151 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002152
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002153 /* fill all input-routes */
2154 for (i = 0; i < cfg->num_inputs; i++) {
2155 if (add_input_route(codec, cfg->inputs[i].pin))
2156 spec->inputs[spec->num_inputs++].label =
2157 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002158 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002159
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002160 /* check for internal loopback recording */
2161 if (spec->aa_mix_nid &&
2162 add_input_route(codec, spec->aa_mix_nid))
2163 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2164
2165 return 0;
2166}
2167
2168/* create analog-loopback volume/switch controls */
2169static int create_loopback_ctls(struct hda_codec *codec)
2170{
2171 struct via_spec *spec = codec->spec;
2172 const struct auto_pin_cfg *cfg = &spec->autocfg;
2173 const char *prev_label = NULL;
2174 int type_idx = 0;
2175 int i, j, err, idx;
2176
2177 if (!spec->aa_mix_nid)
2178 return 0;
2179
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002180 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002181 hda_nid_t pin = cfg->inputs[i].pin;
2182 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2183
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002184 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002185 type_idx++;
2186 else
2187 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002188 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002189 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2190 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002191 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002192 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002193 if (err < 0)
2194 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002195 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002196 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002197
2198 /* remember the label for smart51 control */
2199 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002200 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002201 spec->smart51_idxs[j] = idx;
2202 spec->smart51_labels[j] = label;
2203 break;
2204 }
2205 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002206 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002207 return 0;
2208}
2209
2210/* create mic-boost controls (if present) */
2211static int create_mic_boost_ctls(struct hda_codec *codec)
2212{
2213 struct via_spec *spec = codec->spec;
2214 const struct auto_pin_cfg *cfg = &spec->autocfg;
2215 int i, err;
2216
2217 for (i = 0; i < cfg->num_inputs; i++) {
2218 hda_nid_t pin = cfg->inputs[i].pin;
2219 unsigned int caps;
2220 const char *label;
2221 char name[32];
2222
2223 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2224 continue;
2225 caps = query_amp_caps(codec, pin, HDA_INPUT);
2226 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2227 continue;
2228 label = hda_get_autocfg_input_label(codec, cfg, i);
2229 snprintf(name, sizeof(name), "%s Boost Volume", label);
2230 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2231 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2232 if (err < 0)
2233 return err;
2234 }
2235 return 0;
2236}
2237
2238/* create capture and input-src controls for multiple streams */
2239static int create_multi_adc_ctls(struct hda_codec *codec)
2240{
2241 struct via_spec *spec = codec->spec;
2242 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002243
2244 /* create capture mixer elements */
2245 for (i = 0; i < spec->num_adc_nids; i++) {
2246 hda_nid_t adc = spec->adc_nids[i];
2247 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2248 "Capture Volume", i,
2249 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2250 HDA_INPUT));
2251 if (err < 0)
2252 return err;
2253 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2254 "Capture Switch", i,
2255 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2256 HDA_INPUT));
2257 if (err < 0)
2258 return err;
2259 }
2260
2261 /* input-source control */
2262 for (i = 0; i < spec->num_adc_nids; i++)
2263 if (!spec->mux_nids[i])
2264 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002265 err = create_input_src_ctls(codec, i);
2266 if (err < 0)
2267 return err;
2268 return 0;
2269}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002270
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002271/* bind capture volume/switch */
2272static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2273 HDA_BIND_VOL("Capture Volume", 0);
2274static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2275 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002276
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002277static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2278 struct hda_ctl_ops *ops)
2279{
2280 struct hda_bind_ctls *ctl;
2281 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002282
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002283 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2284 if (!ctl)
2285 return -ENOMEM;
2286 ctl->ops = ops;
2287 for (i = 0; i < spec->num_adc_nids; i++)
2288 ctl->values[i] =
2289 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2290 *ctl_ret = ctl;
2291 return 0;
2292}
2293
2294/* create capture and input-src controls for dynamic ADC-switch case */
2295static int create_dyn_adc_ctls(struct hda_codec *codec)
2296{
2297 struct via_spec *spec = codec->spec;
2298 struct snd_kcontrol_new *knew;
2299 int err;
2300
2301 /* set up the bind capture ctls */
2302 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2303 if (err < 0)
2304 return err;
2305 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2306 if (err < 0)
2307 return err;
2308
2309 /* create capture mixer elements */
2310 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2311 if (!knew)
2312 return -ENOMEM;
2313 knew->private_value = (long)spec->bind_cap_vol;
2314
2315 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2316 if (!knew)
2317 return -ENOMEM;
2318 knew->private_value = (long)spec->bind_cap_sw;
2319
2320 /* input-source control */
2321 err = create_input_src_ctls(codec, 1);
2322 if (err < 0)
2323 return err;
2324 return 0;
2325}
2326
2327/* parse and create capture-related stuff */
2328static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2329{
2330 struct via_spec *spec = codec->spec;
2331 int err;
2332
2333 err = parse_analog_inputs(codec);
2334 if (err < 0)
2335 return err;
2336 if (spec->dyn_adc_switch)
2337 err = create_dyn_adc_ctls(codec);
2338 else
2339 err = create_multi_adc_ctls(codec);
2340 if (err < 0)
2341 return err;
2342 err = create_loopback_ctls(codec);
2343 if (err < 0)
2344 return err;
2345 err = create_mic_boost_ctls(codec);
2346 if (err < 0)
2347 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002348 return 0;
2349}
2350
Harald Welte76d9b0d2008-09-09 15:50:37 +08002351static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2352{
2353 unsigned int def_conf;
2354 unsigned char seqassoc;
2355
Takashi Iwai2f334f92009-02-20 14:37:42 +01002356 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002357 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2358 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002359 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2360 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2361 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2362 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002363 }
2364
2365 return;
2366}
2367
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002368static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002369 struct snd_ctl_elem_value *ucontrol)
2370{
2371 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2372 struct via_spec *spec = codec->spec;
2373
2374 if (spec->codec_type != VT1708)
2375 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002376 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002377 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002378 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002379 return 0;
2380}
2381
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002382static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002383 struct snd_ctl_elem_value *ucontrol)
2384{
2385 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2386 struct via_spec *spec = codec->spec;
2387 int change;
2388
2389 if (spec->codec_type != VT1708)
2390 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002391 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002392 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002393 == !spec->vt1708_jack_detect;
2394 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002395 mute_aa_path(codec, 1);
2396 notify_aa_path_ctls(codec);
2397 }
2398 return change;
2399}
2400
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002401static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2402 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2403 .name = "Jack Detect",
2404 .count = 1,
2405 .info = snd_ctl_boolean_mono_info,
2406 .get = vt1708_jack_detect_get,
2407 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002408};
2409
Takashi Iwai12daef62011-06-18 17:45:49 +02002410static void fill_dig_outs(struct hda_codec *codec);
2411static void fill_dig_in(struct hda_codec *codec);
2412
2413static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002414{
2415 struct via_spec *spec = codec->spec;
2416 int err;
2417
2418 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2419 if (err < 0)
2420 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002421 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002422 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002423
Takashi Iwai4a796162011-06-17 17:53:38 +02002424 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002425 if (err < 0)
2426 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002427 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002428 if (err < 0)
2429 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002430 err = via_auto_create_speaker_ctls(codec);
2431 if (err < 0)
2432 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002433 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002434 if (err < 0)
2435 return err;
2436
2437 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2438
Takashi Iwai12daef62011-06-18 17:45:49 +02002439 fill_dig_outs(codec);
2440 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002441
Takashi Iwai603c4012008-07-30 15:01:44 +02002442 if (spec->kctls.list)
2443 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002444
Joseph Chanc577b8a2006-11-29 15:29:40 +01002445
Takashi Iwai8df2a312011-06-21 11:48:29 +02002446 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002447 err = via_hp_build(codec);
2448 if (err < 0)
2449 return err;
2450 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002451
Takashi Iwaif4a78282011-06-17 18:46:48 +02002452 err = via_smart51_build(codec);
2453 if (err < 0)
2454 return err;
2455
Takashi Iwai5d417622011-06-20 11:32:27 +02002456 /* assign slave outs */
2457 if (spec->slave_dig_outs[0])
2458 codec->slave_dig_outs = spec->slave_dig_outs;
2459
Joseph Chanc577b8a2006-11-29 15:29:40 +01002460 return 1;
2461}
2462
Takashi Iwai5d417622011-06-20 11:32:27 +02002463static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002464{
Lydia Wang25eaba22009-10-10 19:08:43 +08002465 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002466 if (spec->multiout.dig_out_nid)
2467 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2468 if (spec->slave_dig_outs[0])
2469 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2470}
Lydia Wang25eaba22009-10-10 19:08:43 +08002471
Takashi Iwai5d417622011-06-20 11:32:27 +02002472static void via_auto_init_dig_in(struct hda_codec *codec)
2473{
2474 struct via_spec *spec = codec->spec;
2475 if (!spec->dig_in_nid)
2476 return;
2477 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2478 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2479}
2480
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002481/* initialize the unsolicited events */
2482static void via_auto_init_unsol_event(struct hda_codec *codec)
2483{
2484 struct via_spec *spec = codec->spec;
2485 struct auto_pin_cfg *cfg = &spec->autocfg;
2486 unsigned int ev;
2487 int i;
2488
2489 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2490 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2491 AC_VERB_SET_UNSOLICITED_ENABLE,
2492 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2493
2494 if (cfg->speaker_pins[0])
2495 ev = VIA_LINE_EVENT;
2496 else
2497 ev = 0;
2498 for (i = 0; i < cfg->line_outs; i++) {
2499 if (cfg->line_out_pins[i] &&
2500 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002501 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002502 AC_VERB_SET_UNSOLICITED_ENABLE,
2503 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2504 }
2505
2506 for (i = 0; i < cfg->num_inputs; i++) {
2507 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2508 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2509 AC_VERB_SET_UNSOLICITED_ENABLE,
2510 AC_USRSP_EN | VIA_JACK_EVENT);
2511 }
2512}
2513
Takashi Iwai5d417622011-06-20 11:32:27 +02002514static int via_init(struct hda_codec *codec)
2515{
2516 struct via_spec *spec = codec->spec;
2517 int i;
2518
2519 for (i = 0; i < spec->num_iverbs; i++)
2520 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2521
Joseph Chanc577b8a2006-11-29 15:29:40 +01002522 via_auto_init_multi_out(codec);
2523 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002524 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002525 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002526 via_auto_init_dig_outs(codec);
2527 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002528
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002529 via_auto_init_unsol_event(codec);
2530
2531 via_hp_automute(codec);
2532 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002533
Joseph Chanc577b8a2006-11-29 15:29:40 +01002534 return 0;
2535}
2536
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002537static void vt1708_update_hp_jack_state(struct work_struct *work)
2538{
2539 struct via_spec *spec = container_of(work, struct via_spec,
2540 vt1708_hp_work.work);
2541 if (spec->codec_type != VT1708)
2542 return;
2543 /* if jack state toggled */
2544 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002545 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002546 spec->vt1708_hp_present ^= 1;
2547 via_hp_automute(spec->codec);
2548 }
2549 vt1708_start_hp_work(spec);
2550}
2551
Takashi Iwai337b9d02009-07-07 18:18:59 +02002552static int get_mux_nids(struct hda_codec *codec)
2553{
2554 struct via_spec *spec = codec->spec;
2555 hda_nid_t nid, conn[8];
2556 unsigned int type;
2557 int i, n;
2558
2559 for (i = 0; i < spec->num_adc_nids; i++) {
2560 nid = spec->adc_nids[i];
2561 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002562 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002563 if (type == AC_WID_PIN)
2564 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002565 n = snd_hda_get_connections(codec, nid, conn,
2566 ARRAY_SIZE(conn));
2567 if (n <= 0)
2568 break;
2569 if (n > 1) {
2570 spec->mux_nids[i] = nid;
2571 break;
2572 }
2573 nid = conn[0];
2574 }
2575 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002576 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002577}
2578
Joseph Chanc577b8a2006-11-29 15:29:40 +01002579static int patch_vt1708(struct hda_codec *codec)
2580{
2581 struct via_spec *spec;
2582 int err;
2583
2584 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002585 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002586 if (spec == NULL)
2587 return -ENOMEM;
2588
Takashi Iwai620e2b22011-06-17 17:19:19 +02002589 spec->aa_mix_nid = 0x17;
2590
Takashi Iwai12daef62011-06-18 17:45:49 +02002591 /* Add HP and CD pin config connect bit re-config action */
2592 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2593 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2594
Joseph Chanc577b8a2006-11-29 15:29:40 +01002595 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002596 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002597 if (err < 0) {
2598 via_free(codec);
2599 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002600 }
2601
Takashi Iwai12daef62011-06-18 17:45:49 +02002602 /* add jack detect on/off control */
2603 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2604 return -ENOMEM;
2605
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002606 /* disable 32bit format on VT1708 */
2607 if (codec->vendor_id == 0x11061708)
2608 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002609
Lydia Wange322a362011-06-29 13:52:02 +08002610 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2611
Joseph Chanc577b8a2006-11-29 15:29:40 +01002612 codec->patch_ops = via_patch_ops;
2613
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002614 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002615 return 0;
2616}
2617
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002618static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002619{
2620 struct via_spec *spec;
2621 int err;
2622
2623 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002624 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002625 if (spec == NULL)
2626 return -ENOMEM;
2627
Takashi Iwai620e2b22011-06-17 17:19:19 +02002628 spec->aa_mix_nid = 0x18;
2629
Takashi Iwai12daef62011-06-18 17:45:49 +02002630 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002631 if (err < 0) {
2632 via_free(codec);
2633 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002634 }
2635
Joseph Chanc577b8a2006-11-29 15:29:40 +01002636 codec->patch_ops = via_patch_ops;
2637
Josepch Chanf7278fd2007-12-13 16:40:40 +01002638 return 0;
2639}
2640
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002641static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2642{
2643 struct via_spec *spec = codec->spec;
2644 int imux_is_smixer;
2645 unsigned int parm;
2646 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002647 if ((spec->codec_type != VT1708B_4CH) &&
2648 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002649 is_8ch = 1;
2650
2651 /* SW0 (17h) = stereo mixer */
2652 imux_is_smixer =
2653 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2654 == ((spec->codec_type == VT1708S) ? 5 : 0));
2655 /* inputs */
2656 /* PW 1/2/5 (1ah/1bh/1eh) */
2657 parm = AC_PWRST_D3;
2658 set_pin_power_state(codec, 0x1a, &parm);
2659 set_pin_power_state(codec, 0x1b, &parm);
2660 set_pin_power_state(codec, 0x1e, &parm);
2661 if (imux_is_smixer)
2662 parm = AC_PWRST_D0;
2663 /* SW0 (17h), AIW 0/1 (13h/14h) */
2664 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2665 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2666 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2667
2668 /* outputs */
2669 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2670 parm = AC_PWRST_D3;
2671 set_pin_power_state(codec, 0x19, &parm);
2672 if (spec->smart51_enabled)
2673 set_pin_power_state(codec, 0x1b, &parm);
2674 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2675 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2676
2677 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2678 if (is_8ch) {
2679 parm = AC_PWRST_D3;
2680 set_pin_power_state(codec, 0x22, &parm);
2681 if (spec->smart51_enabled)
2682 set_pin_power_state(codec, 0x1a, &parm);
2683 snd_hda_codec_write(codec, 0x26, 0,
2684 AC_VERB_SET_POWER_STATE, parm);
2685 snd_hda_codec_write(codec, 0x24, 0,
2686 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002687 } else if (codec->vendor_id == 0x11064397) {
2688 /* PW7(23h), SW2(27h), AOW2(25h) */
2689 parm = AC_PWRST_D3;
2690 set_pin_power_state(codec, 0x23, &parm);
2691 if (spec->smart51_enabled)
2692 set_pin_power_state(codec, 0x1a, &parm);
2693 snd_hda_codec_write(codec, 0x27, 0,
2694 AC_VERB_SET_POWER_STATE, parm);
2695 snd_hda_codec_write(codec, 0x25, 0,
2696 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002697 }
2698
2699 /* PW 3/4/7 (1ch/1dh/23h) */
2700 parm = AC_PWRST_D3;
2701 /* force to D0 for internal Speaker */
2702 set_pin_power_state(codec, 0x1c, &parm);
2703 set_pin_power_state(codec, 0x1d, &parm);
2704 if (is_8ch)
2705 set_pin_power_state(codec, 0x23, &parm);
2706
2707 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2708 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2709 imux_is_smixer ? AC_PWRST_D0 : parm);
2710 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2711 if (is_8ch) {
2712 snd_hda_codec_write(codec, 0x25, 0,
2713 AC_VERB_SET_POWER_STATE, parm);
2714 snd_hda_codec_write(codec, 0x27, 0,
2715 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002716 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2717 snd_hda_codec_write(codec, 0x25, 0,
2718 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002719}
2720
Lydia Wang518bf3b2009-10-10 19:07:29 +08002721static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002722static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002723{
2724 struct via_spec *spec;
2725 int err;
2726
Lydia Wang518bf3b2009-10-10 19:07:29 +08002727 if (get_codec_type(codec) == VT1708BCE)
2728 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002729
Josepch Chanf7278fd2007-12-13 16:40:40 +01002730 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002731 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002732 if (spec == NULL)
2733 return -ENOMEM;
2734
Takashi Iwai620e2b22011-06-17 17:19:19 +02002735 spec->aa_mix_nid = 0x16;
2736
Josepch Chanf7278fd2007-12-13 16:40:40 +01002737 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002738 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002739 if (err < 0) {
2740 via_free(codec);
2741 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002742 }
2743
Josepch Chanf7278fd2007-12-13 16:40:40 +01002744 codec->patch_ops = via_patch_ops;
2745
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002746 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2747
Josepch Chanf7278fd2007-12-13 16:40:40 +01002748 return 0;
2749}
2750
Harald Welted949cac2008-09-09 15:56:01 +08002751/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002752static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002753 /* Enable Mic Boost Volume backdoor */
2754 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002755 /* don't bybass mixer */
2756 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002757 { }
2758};
2759
Takashi Iwai9da29272009-05-07 16:31:14 +02002760/* fill out digital output widgets; one for master and one for slave outputs */
2761static void fill_dig_outs(struct hda_codec *codec)
2762{
2763 struct via_spec *spec = codec->spec;
2764 int i;
2765
2766 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2767 hda_nid_t nid;
2768 int conn;
2769
2770 nid = spec->autocfg.dig_out_pins[i];
2771 if (!nid)
2772 continue;
2773 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2774 if (conn < 1)
2775 continue;
2776 if (!spec->multiout.dig_out_nid)
2777 spec->multiout.dig_out_nid = nid;
2778 else {
2779 spec->slave_dig_outs[0] = nid;
2780 break; /* at most two dig outs */
2781 }
2782 }
2783}
2784
Takashi Iwai12daef62011-06-18 17:45:49 +02002785static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002786{
2787 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002788 hda_nid_t dig_nid;
2789 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002790
Takashi Iwai12daef62011-06-18 17:45:49 +02002791 if (!spec->autocfg.dig_in_pin)
2792 return;
Harald Welted949cac2008-09-09 15:56:01 +08002793
Takashi Iwai12daef62011-06-18 17:45:49 +02002794 dig_nid = codec->start_nid;
2795 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2796 unsigned int wcaps = get_wcaps(codec, dig_nid);
2797 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2798 continue;
2799 if (!(wcaps & AC_WCAP_DIGITAL))
2800 continue;
2801 if (!(wcaps & AC_WCAP_CONN_LIST))
2802 continue;
2803 err = get_connection_index(codec, dig_nid,
2804 spec->autocfg.dig_in_pin);
2805 if (err >= 0) {
2806 spec->dig_in_nid = dig_nid;
2807 break;
2808 }
2809 }
Harald Welted949cac2008-09-09 15:56:01 +08002810}
2811
Lydia Wang6369bcf2009-10-10 19:08:31 +08002812static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2813 int offset, int num_steps, int step_size)
2814{
2815 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2816 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2817 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2818 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2819 (0 << AC_AMPCAP_MUTE_SHIFT));
2820}
2821
Harald Welted949cac2008-09-09 15:56:01 +08002822static int patch_vt1708S(struct hda_codec *codec)
2823{
2824 struct via_spec *spec;
2825 int err;
2826
2827 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002828 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002829 if (spec == NULL)
2830 return -ENOMEM;
2831
Takashi Iwai620e2b22011-06-17 17:19:19 +02002832 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002833 override_mic_boost(codec, 0x1a, 0, 3, 40);
2834 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002835
Harald Welted949cac2008-09-09 15:56:01 +08002836 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002837 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002838 if (err < 0) {
2839 via_free(codec);
2840 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002841 }
2842
Takashi Iwai096a8852011-06-20 12:09:02 +02002843 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002844
Harald Welted949cac2008-09-09 15:56:01 +08002845 codec->patch_ops = via_patch_ops;
2846
Lydia Wang518bf3b2009-10-10 19:07:29 +08002847 /* correct names for VT1708BCE */
2848 if (get_codec_type(codec) == VT1708BCE) {
2849 kfree(codec->chip_name);
2850 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2851 snprintf(codec->bus->card->mixername,
2852 sizeof(codec->bus->card->mixername),
2853 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002854 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002855 /* correct names for VT1705 */
2856 if (codec->vendor_id == 0x11064397) {
2857 kfree(codec->chip_name);
2858 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2859 snprintf(codec->bus->card->mixername,
2860 sizeof(codec->bus->card->mixername),
2861 "%s %s", codec->vendor_name, codec->chip_name);
2862 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002863 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002864 return 0;
2865}
2866
2867/* Patch for VT1702 */
2868
Takashi Iwai096a8852011-06-20 12:09:02 +02002869static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002870 /* mixer enable */
2871 {0x1, 0xF88, 0x3},
2872 /* GPIO 0~2 */
2873 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002874 { }
2875};
2876
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002877static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2878{
2879 int imux_is_smixer =
2880 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2881 unsigned int parm;
2882 /* inputs */
2883 /* PW 1/2/5 (14h/15h/18h) */
2884 parm = AC_PWRST_D3;
2885 set_pin_power_state(codec, 0x14, &parm);
2886 set_pin_power_state(codec, 0x15, &parm);
2887 set_pin_power_state(codec, 0x18, &parm);
2888 if (imux_is_smixer)
2889 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2890 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2891 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2892 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2893 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2894 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2895
2896 /* outputs */
2897 /* PW 3/4 (16h/17h) */
2898 parm = AC_PWRST_D3;
2899 set_pin_power_state(codec, 0x17, &parm);
2900 set_pin_power_state(codec, 0x16, &parm);
2901 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2902 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2903 imux_is_smixer ? AC_PWRST_D0 : parm);
2904 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2905 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2906}
2907
Harald Welted949cac2008-09-09 15:56:01 +08002908static int patch_vt1702(struct hda_codec *codec)
2909{
2910 struct via_spec *spec;
2911 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002912
2913 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002914 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002915 if (spec == NULL)
2916 return -ENOMEM;
2917
Takashi Iwai620e2b22011-06-17 17:19:19 +02002918 spec->aa_mix_nid = 0x1a;
2919
Takashi Iwai12daef62011-06-18 17:45:49 +02002920 /* limit AA path volume to 0 dB */
2921 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2922 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2923 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2924 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2925 (1 << AC_AMPCAP_MUTE_SHIFT));
2926
Harald Welted949cac2008-09-09 15:56:01 +08002927 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002928 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002929 if (err < 0) {
2930 via_free(codec);
2931 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002932 }
2933
Takashi Iwai096a8852011-06-20 12:09:02 +02002934 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002935
Harald Welted949cac2008-09-09 15:56:01 +08002936 codec->patch_ops = via_patch_ops;
2937
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002938 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002939 return 0;
2940}
2941
Lydia Wangeb7188c2009-10-10 19:08:34 +08002942/* Patch for VT1718S */
2943
Takashi Iwai096a8852011-06-20 12:09:02 +02002944static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002945 /* Enable MW0 adjust Gain 5 */
2946 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002947 /* Enable Boost Volume backdoor */
2948 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002949
Lydia Wangeb7188c2009-10-10 19:08:34 +08002950 { }
2951};
2952
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002953static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2954{
2955 struct via_spec *spec = codec->spec;
2956 int imux_is_smixer;
2957 unsigned int parm;
2958 /* MUX6 (1eh) = stereo mixer */
2959 imux_is_smixer =
2960 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2961 /* inputs */
2962 /* PW 5/6/7 (29h/2ah/2bh) */
2963 parm = AC_PWRST_D3;
2964 set_pin_power_state(codec, 0x29, &parm);
2965 set_pin_power_state(codec, 0x2a, &parm);
2966 set_pin_power_state(codec, 0x2b, &parm);
2967 if (imux_is_smixer)
2968 parm = AC_PWRST_D0;
2969 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2970 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2971 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2972 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2973 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2974
2975 /* outputs */
2976 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2977 parm = AC_PWRST_D3;
2978 set_pin_power_state(codec, 0x27, &parm);
2979 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2980 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2981
2982 /* PW2 (26h), AOW2 (ah) */
2983 parm = AC_PWRST_D3;
2984 set_pin_power_state(codec, 0x26, &parm);
2985 if (spec->smart51_enabled)
2986 set_pin_power_state(codec, 0x2b, &parm);
2987 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2988
2989 /* PW0 (24h), AOW0 (8h) */
2990 parm = AC_PWRST_D3;
2991 set_pin_power_state(codec, 0x24, &parm);
2992 if (!spec->hp_independent_mode) /* check for redirected HP */
2993 set_pin_power_state(codec, 0x28, &parm);
2994 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2995 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2996 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2997 imux_is_smixer ? AC_PWRST_D0 : parm);
2998
2999 /* PW1 (25h), AOW1 (9h) */
3000 parm = AC_PWRST_D3;
3001 set_pin_power_state(codec, 0x25, &parm);
3002 if (spec->smart51_enabled)
3003 set_pin_power_state(codec, 0x2a, &parm);
3004 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
3005
3006 if (spec->hp_independent_mode) {
3007 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3008 parm = AC_PWRST_D3;
3009 set_pin_power_state(codec, 0x28, &parm);
3010 snd_hda_codec_write(codec, 0x1b, 0,
3011 AC_VERB_SET_POWER_STATE, parm);
3012 snd_hda_codec_write(codec, 0x34, 0,
3013 AC_VERB_SET_POWER_STATE, parm);
3014 snd_hda_codec_write(codec, 0xc, 0,
3015 AC_VERB_SET_POWER_STATE, parm);
3016 }
3017}
3018
Lydia Wangeb7188c2009-10-10 19:08:34 +08003019static int patch_vt1718S(struct hda_codec *codec)
3020{
3021 struct via_spec *spec;
3022 int err;
3023
3024 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003025 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003026 if (spec == NULL)
3027 return -ENOMEM;
3028
Takashi Iwai620e2b22011-06-17 17:19:19 +02003029 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003030 override_mic_boost(codec, 0x2b, 0, 3, 40);
3031 override_mic_boost(codec, 0x29, 0, 3, 40);
Lydia Wangc4394f52011-07-04 16:54:15 +08003032 spec->dac_mixer_idx = 5;
Takashi Iwai620e2b22011-06-17 17:19:19 +02003033
Lydia Wangeb7188c2009-10-10 19:08:34 +08003034 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003035 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003036 if (err < 0) {
3037 via_free(codec);
3038 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003039 }
3040
Takashi Iwai096a8852011-06-20 12:09:02 +02003041 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003042
Lydia Wangeb7188c2009-10-10 19:08:34 +08003043 codec->patch_ops = via_patch_ops;
3044
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003045 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3046
Lydia Wangeb7188c2009-10-10 19:08:34 +08003047 return 0;
3048}
Lydia Wangf3db4232009-10-10 19:08:41 +08003049
3050/* Patch for VT1716S */
3051
3052static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3053 struct snd_ctl_elem_info *uinfo)
3054{
3055 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3056 uinfo->count = 1;
3057 uinfo->value.integer.min = 0;
3058 uinfo->value.integer.max = 1;
3059 return 0;
3060}
3061
3062static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3063 struct snd_ctl_elem_value *ucontrol)
3064{
3065 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3066 int index = 0;
3067
3068 index = snd_hda_codec_read(codec, 0x26, 0,
3069 AC_VERB_GET_CONNECT_SEL, 0);
3070 if (index != -1)
3071 *ucontrol->value.integer.value = index;
3072
3073 return 0;
3074}
3075
3076static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3077 struct snd_ctl_elem_value *ucontrol)
3078{
3079 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3080 struct via_spec *spec = codec->spec;
3081 int index = *ucontrol->value.integer.value;
3082
3083 snd_hda_codec_write(codec, 0x26, 0,
3084 AC_VERB_SET_CONNECT_SEL, index);
3085 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003086 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003087 return 1;
3088}
3089
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003090static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003091 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3092 {
3093 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3094 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003095 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003096 .count = 1,
3097 .info = vt1716s_dmic_info,
3098 .get = vt1716s_dmic_get,
3099 .put = vt1716s_dmic_put,
3100 },
3101 {} /* end */
3102};
3103
3104
3105/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003106static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003107 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3108 { } /* end */
3109};
3110
Takashi Iwai096a8852011-06-20 12:09:02 +02003111static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003112 /* Enable Boost Volume backdoor */
3113 {0x1, 0xf8a, 0x80},
3114 /* don't bybass mixer */
3115 {0x1, 0xf88, 0xc0},
3116 /* Enable mono output */
3117 {0x1, 0xf90, 0x08},
3118 { }
3119};
3120
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003121static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3122{
3123 struct via_spec *spec = codec->spec;
3124 int imux_is_smixer;
3125 unsigned int parm;
3126 unsigned int mono_out, present;
3127 /* SW0 (17h) = stereo mixer */
3128 imux_is_smixer =
3129 (snd_hda_codec_read(codec, 0x17, 0,
3130 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3131 /* inputs */
3132 /* PW 1/2/5 (1ah/1bh/1eh) */
3133 parm = AC_PWRST_D3;
3134 set_pin_power_state(codec, 0x1a, &parm);
3135 set_pin_power_state(codec, 0x1b, &parm);
3136 set_pin_power_state(codec, 0x1e, &parm);
3137 if (imux_is_smixer)
3138 parm = AC_PWRST_D0;
3139 /* SW0 (17h), AIW0(13h) */
3140 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3141 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3142
3143 parm = AC_PWRST_D3;
3144 set_pin_power_state(codec, 0x1e, &parm);
3145 /* PW11 (22h) */
3146 if (spec->dmic_enabled)
3147 set_pin_power_state(codec, 0x22, &parm);
3148 else
3149 snd_hda_codec_write(codec, 0x22, 0,
3150 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3151
3152 /* SW2(26h), AIW1(14h) */
3153 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3154 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3155
3156 /* outputs */
3157 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3158 parm = AC_PWRST_D3;
3159 set_pin_power_state(codec, 0x19, &parm);
3160 /* Smart 5.1 PW2(1bh) */
3161 if (spec->smart51_enabled)
3162 set_pin_power_state(codec, 0x1b, &parm);
3163 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3164 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3165
3166 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3167 parm = AC_PWRST_D3;
3168 set_pin_power_state(codec, 0x23, &parm);
3169 /* Smart 5.1 PW1(1ah) */
3170 if (spec->smart51_enabled)
3171 set_pin_power_state(codec, 0x1a, &parm);
3172 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3173
3174 /* Smart 5.1 PW5(1eh) */
3175 if (spec->smart51_enabled)
3176 set_pin_power_state(codec, 0x1e, &parm);
3177 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3178
3179 /* Mono out */
3180 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3181 present = snd_hda_jack_detect(codec, 0x1c);
3182
3183 if (present)
3184 mono_out = 0;
3185 else {
3186 present = snd_hda_jack_detect(codec, 0x1d);
3187 if (!spec->hp_independent_mode && present)
3188 mono_out = 0;
3189 else
3190 mono_out = 1;
3191 }
3192 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3193 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3194 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3195 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3196
3197 /* PW 3/4 (1ch/1dh) */
3198 parm = AC_PWRST_D3;
3199 set_pin_power_state(codec, 0x1c, &parm);
3200 set_pin_power_state(codec, 0x1d, &parm);
3201 /* HP Independent Mode, power on AOW3 */
3202 if (spec->hp_independent_mode)
3203 snd_hda_codec_write(codec, 0x25, 0,
3204 AC_VERB_SET_POWER_STATE, parm);
3205
3206 /* force to D0 for internal Speaker */
3207 /* MW0 (16h), AOW0 (10h) */
3208 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3209 imux_is_smixer ? AC_PWRST_D0 : parm);
3210 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3211 mono_out ? AC_PWRST_D0 : parm);
3212}
3213
Lydia Wangf3db4232009-10-10 19:08:41 +08003214static int patch_vt1716S(struct hda_codec *codec)
3215{
3216 struct via_spec *spec;
3217 int err;
3218
3219 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003220 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003221 if (spec == NULL)
3222 return -ENOMEM;
3223
Takashi Iwai620e2b22011-06-17 17:19:19 +02003224 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003225 override_mic_boost(codec, 0x1a, 0, 3, 40);
3226 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003227
Lydia Wangf3db4232009-10-10 19:08:41 +08003228 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003229 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003230 if (err < 0) {
3231 via_free(codec);
3232 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003233 }
3234
Takashi Iwai096a8852011-06-20 12:09:02 +02003235 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003236
Lydia Wangf3db4232009-10-10 19:08:41 +08003237 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3238 spec->num_mixers++;
3239
3240 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3241
3242 codec->patch_ops = via_patch_ops;
3243
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003244 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003245 return 0;
3246}
Lydia Wang25eaba22009-10-10 19:08:43 +08003247
3248/* for vt2002P */
3249
Takashi Iwai096a8852011-06-20 12:09:02 +02003250static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003251 /* Class-D speaker related verbs */
3252 {0x1, 0xfe0, 0x4},
3253 {0x1, 0xfe9, 0x80},
3254 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003255 /* Enable Boost Volume backdoor */
3256 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003257 /* Enable AOW0 to MW9 */
3258 {0x1, 0xfb8, 0x88},
3259 { }
3260};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003261
Takashi Iwai096a8852011-06-20 12:09:02 +02003262static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003263 /* Enable Boost Volume backdoor */
3264 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003265 /* Enable AOW0 to MW9 */
3266 {0x1, 0xfb8, 0x88},
3267 { }
3268};
Lydia Wang25eaba22009-10-10 19:08:43 +08003269
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003270static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3271{
3272 struct via_spec *spec = codec->spec;
3273 int imux_is_smixer;
3274 unsigned int parm;
3275 unsigned int present;
3276 /* MUX9 (1eh) = stereo mixer */
3277 imux_is_smixer =
3278 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3279 /* inputs */
3280 /* PW 5/6/7 (29h/2ah/2bh) */
3281 parm = AC_PWRST_D3;
3282 set_pin_power_state(codec, 0x29, &parm);
3283 set_pin_power_state(codec, 0x2a, &parm);
3284 set_pin_power_state(codec, 0x2b, &parm);
3285 parm = AC_PWRST_D0;
3286 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3287 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3288 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3289 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3290 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3291
3292 /* outputs */
3293 /* AOW0 (8h)*/
3294 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3295
Lydia Wang118909562011-03-23 17:57:34 +08003296 if (spec->codec_type == VT1802) {
3297 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3298 parm = AC_PWRST_D3;
3299 set_pin_power_state(codec, 0x28, &parm);
3300 snd_hda_codec_write(codec, 0x18, 0,
3301 AC_VERB_SET_POWER_STATE, parm);
3302 snd_hda_codec_write(codec, 0x38, 0,
3303 AC_VERB_SET_POWER_STATE, parm);
3304 } else {
3305 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3306 parm = AC_PWRST_D3;
3307 set_pin_power_state(codec, 0x26, &parm);
3308 snd_hda_codec_write(codec, 0x1c, 0,
3309 AC_VERB_SET_POWER_STATE, parm);
3310 snd_hda_codec_write(codec, 0x37, 0,
3311 AC_VERB_SET_POWER_STATE, parm);
3312 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003313
Lydia Wang118909562011-03-23 17:57:34 +08003314 if (spec->codec_type == VT1802) {
3315 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3316 parm = AC_PWRST_D3;
3317 set_pin_power_state(codec, 0x25, &parm);
3318 snd_hda_codec_write(codec, 0x15, 0,
3319 AC_VERB_SET_POWER_STATE, parm);
3320 snd_hda_codec_write(codec, 0x35, 0,
3321 AC_VERB_SET_POWER_STATE, parm);
3322 } else {
3323 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3324 parm = AC_PWRST_D3;
3325 set_pin_power_state(codec, 0x25, &parm);
3326 snd_hda_codec_write(codec, 0x19, 0,
3327 AC_VERB_SET_POWER_STATE, parm);
3328 snd_hda_codec_write(codec, 0x35, 0,
3329 AC_VERB_SET_POWER_STATE, parm);
3330 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003331
3332 if (spec->hp_independent_mode)
3333 snd_hda_codec_write(codec, 0x9, 0,
3334 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3335
3336 /* Class-D */
3337 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3338 present = snd_hda_jack_detect(codec, 0x25);
3339
3340 parm = AC_PWRST_D3;
3341 set_pin_power_state(codec, 0x24, &parm);
3342 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003343 if (spec->codec_type == VT1802)
3344 snd_hda_codec_write(codec, 0x14, 0,
3345 AC_VERB_SET_POWER_STATE, parm);
3346 else
3347 snd_hda_codec_write(codec, 0x18, 0,
3348 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003349 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3350
3351 /* Mono Out */
3352 present = snd_hda_jack_detect(codec, 0x26);
3353
3354 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003355 if (spec->codec_type == VT1802) {
3356 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3357 snd_hda_codec_write(codec, 0x33, 0,
3358 AC_VERB_SET_POWER_STATE, parm);
3359 snd_hda_codec_write(codec, 0x1c, 0,
3360 AC_VERB_SET_POWER_STATE, parm);
3361 snd_hda_codec_write(codec, 0x3c, 0,
3362 AC_VERB_SET_POWER_STATE, parm);
3363 } else {
3364 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3365 snd_hda_codec_write(codec, 0x31, 0,
3366 AC_VERB_SET_POWER_STATE, parm);
3367 snd_hda_codec_write(codec, 0x17, 0,
3368 AC_VERB_SET_POWER_STATE, parm);
3369 snd_hda_codec_write(codec, 0x3b, 0,
3370 AC_VERB_SET_POWER_STATE, parm);
3371 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003372 /* MW9 (21h) */
3373 if (imux_is_smixer || !is_aa_path_mute(codec))
3374 snd_hda_codec_write(codec, 0x21, 0,
3375 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3376 else
3377 snd_hda_codec_write(codec, 0x21, 0,
3378 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3379}
Lydia Wang25eaba22009-10-10 19:08:43 +08003380
3381/* patch for vt2002P */
3382static int patch_vt2002P(struct hda_codec *codec)
3383{
3384 struct via_spec *spec;
3385 int err;
3386
3387 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003388 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003389 if (spec == NULL)
3390 return -ENOMEM;
3391
Takashi Iwai620e2b22011-06-17 17:19:19 +02003392 spec->aa_mix_nid = 0x21;
Lydia Wang5c9a5612011-07-08 14:03:43 +08003393 spec->dac_mixer_idx = 3;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003394 override_mic_boost(codec, 0x2b, 0, 3, 40);
3395 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003396
Lydia Wang25eaba22009-10-10 19:08:43 +08003397 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003398 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003399 if (err < 0) {
3400 via_free(codec);
3401 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003402 }
3403
Lydia Wang118909562011-03-23 17:57:34 +08003404 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003405 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003406 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003407 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003408
Lydia Wang25eaba22009-10-10 19:08:43 +08003409 codec->patch_ops = via_patch_ops;
3410
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003411 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003412 return 0;
3413}
Lydia Wangab6734e2009-10-10 19:08:46 +08003414
3415/* for vt1812 */
3416
Takashi Iwai096a8852011-06-20 12:09:02 +02003417static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003418 /* Enable Boost Volume backdoor */
3419 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003420 /* Enable AOW0 to MW9 */
3421 {0x1, 0xfb8, 0xa8},
3422 { }
3423};
3424
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003425static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3426{
3427 struct via_spec *spec = codec->spec;
3428 int imux_is_smixer =
3429 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3430 unsigned int parm;
3431 unsigned int present;
3432 /* MUX10 (1eh) = stereo mixer */
3433 imux_is_smixer =
3434 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3435 /* inputs */
3436 /* PW 5/6/7 (29h/2ah/2bh) */
3437 parm = AC_PWRST_D3;
3438 set_pin_power_state(codec, 0x29, &parm);
3439 set_pin_power_state(codec, 0x2a, &parm);
3440 set_pin_power_state(codec, 0x2b, &parm);
3441 parm = AC_PWRST_D0;
3442 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3443 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3444 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3445 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3446 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3447
3448 /* outputs */
3449 /* AOW0 (8h)*/
3450 snd_hda_codec_write(codec, 0x8, 0,
3451 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3452
3453 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3454 parm = AC_PWRST_D3;
3455 set_pin_power_state(codec, 0x28, &parm);
3456 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3457 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3458
3459 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3460 parm = AC_PWRST_D3;
3461 set_pin_power_state(codec, 0x25, &parm);
3462 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3463 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3464 if (spec->hp_independent_mode)
3465 snd_hda_codec_write(codec, 0x9, 0,
3466 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3467
3468 /* Internal Speaker */
3469 /* PW0 (24h), MW0(14h), MUX0(34h) */
3470 present = snd_hda_jack_detect(codec, 0x25);
3471
3472 parm = AC_PWRST_D3;
3473 set_pin_power_state(codec, 0x24, &parm);
3474 if (present) {
3475 snd_hda_codec_write(codec, 0x14, 0,
3476 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3477 snd_hda_codec_write(codec, 0x34, 0,
3478 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3479 } else {
3480 snd_hda_codec_write(codec, 0x14, 0,
3481 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3482 snd_hda_codec_write(codec, 0x34, 0,
3483 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3484 }
3485
3486
3487 /* Mono Out */
3488 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3489 present = snd_hda_jack_detect(codec, 0x28);
3490
3491 parm = AC_PWRST_D3;
3492 set_pin_power_state(codec, 0x31, &parm);
3493 if (present) {
3494 snd_hda_codec_write(codec, 0x1c, 0,
3495 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3496 snd_hda_codec_write(codec, 0x3c, 0,
3497 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3498 snd_hda_codec_write(codec, 0x3e, 0,
3499 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3500 } else {
3501 snd_hda_codec_write(codec, 0x1c, 0,
3502 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3503 snd_hda_codec_write(codec, 0x3c, 0,
3504 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3505 snd_hda_codec_write(codec, 0x3e, 0,
3506 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3507 }
3508
3509 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3510 parm = AC_PWRST_D3;
3511 set_pin_power_state(codec, 0x33, &parm);
3512 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3513 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3514
3515}
Lydia Wangab6734e2009-10-10 19:08:46 +08003516
3517/* patch for vt1812 */
3518static int patch_vt1812(struct hda_codec *codec)
3519{
3520 struct via_spec *spec;
3521 int err;
3522
3523 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003524 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003525 if (spec == NULL)
3526 return -ENOMEM;
3527
Takashi Iwai620e2b22011-06-17 17:19:19 +02003528 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003529 override_mic_boost(codec, 0x2b, 0, 3, 40);
3530 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003531
Lydia Wangab6734e2009-10-10 19:08:46 +08003532 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003533 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003534 if (err < 0) {
3535 via_free(codec);
3536 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003537 }
3538
Takashi Iwai096a8852011-06-20 12:09:02 +02003539 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003540
Lydia Wangab6734e2009-10-10 19:08:46 +08003541 codec->patch_ops = via_patch_ops;
3542
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003543 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003544 return 0;
3545}
3546
Joseph Chanc577b8a2006-11-29 15:29:40 +01003547/*
3548 * patch entries
3549 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003550static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003551 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3552 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3553 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3554 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3555 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003556 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003557 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003558 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003559 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003560 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003561 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003562 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003563 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003564 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003565 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003566 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003567 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003568 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003569 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003570 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003571 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003572 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003573 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003574 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003575 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003576 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003577 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003578 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003579 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003580 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003581 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003582 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003583 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003584 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003585 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003586 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003587 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003588 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003589 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003590 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003591 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003592 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003593 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003594 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003595 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003596 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003597 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003598 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003599 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003600 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003601 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003602 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003603 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003604 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003605 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003606 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003607 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003608 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003609 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003610 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003611 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003612 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003613 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003614 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003615 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003616 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003617 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003618 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003619 { .id = 0x11060428, .name = "VT1718S",
3620 .patch = patch_vt1718S},
3621 { .id = 0x11064428, .name = "VT1718S",
3622 .patch = patch_vt1718S},
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08003623 { .id = 0x11060441, .name = "VT2020",
3624 .patch = patch_vt1718S},
3625 { .id = 0x11064441, .name = "VT1828S",
3626 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003627 { .id = 0x11060433, .name = "VT1716S",
3628 .patch = patch_vt1716S},
3629 { .id = 0x1106a721, .name = "VT1716S",
3630 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003631 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3632 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003633 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003634 { .id = 0x11060440, .name = "VT1818S",
3635 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003636 { .id = 0x11060446, .name = "VT1802",
3637 .patch = patch_vt2002P},
3638 { .id = 0x11068446, .name = "VT1802",
3639 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003640 {} /* terminator */
3641};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003642
3643MODULE_ALIAS("snd-hda-codec-id:1106*");
3644
3645static struct hda_codec_preset_list via_list = {
3646 .preset = snd_hda_preset_via,
3647 .owner = THIS_MODULE,
3648};
3649
3650MODULE_LICENSE("GPL");
3651MODULE_DESCRIPTION("VIA HD-audio codec");
3652
3653static int __init patch_via_init(void)
3654{
3655 return snd_hda_add_codec_preset(&via_list);
3656}
3657
3658static void __exit patch_via_exit(void)
3659{
3660 snd_hda_delete_codec_preset(&via_list);
3661}
3662
3663module_init(patch_via_init)
3664module_exit(patch_via_exit)