blob: 42d5a91781fcca0cba00862455c8c9abacf9af06 [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
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200441/* enable/disable the output-route */
442static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
443 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200444{
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200445 int i;
446 for (i = 0; i < path->depth; i++) {
447 hda_nid_t src, dst;
448 int idx = path->idx[i];
449 src = path->path[i];
450 if (i < path->depth - 1)
451 dst = path->path[i + 1];
452 else
453 dst = 0;
454 if (enable && path->multi[i])
455 snd_hda_codec_write(codec, dst, 0,
456 AC_VERB_SET_CONNECT_SEL, idx);
Lydia Wangb89596a2011-07-04 17:01:33 +0800457 if (!force
458 && get_wcaps_type(get_wcaps(codec, src)) == AC_WID_AUD_OUT
459 && get_wcaps_type(get_wcaps(codec, dst)) == AC_WID_AUD_MIX)
Lydia Wange5e14682011-07-01 10:55:07 +0800460 continue;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200461 if (have_mute(codec, dst, HDA_INPUT)) {
462 int val = enable ? AMP_IN_UNMUTE(idx) :
463 AMP_IN_MUTE(idx);
464 snd_hda_codec_write(codec, dst, 0,
465 AC_VERB_SET_AMP_GAIN_MUTE, val);
466 }
467 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
468 continue;
469 if (have_mute(codec, src, HDA_OUTPUT)) {
470 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
471 snd_hda_codec_write(codec, src, 0,
472 AC_VERB_SET_AMP_GAIN_MUTE, val);
473 }
474 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200475}
476
477/* set the given pin as output */
478static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
479 int pin_type)
480{
481 if (!pin)
482 return;
483 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
484 pin_type);
485 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
486 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200487 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100488}
489
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200490static void via_auto_init_output(struct hda_codec *codec,
491 struct nid_path *path, int pin_type,
Takashi Iwaibac4b922011-07-04 17:35:51 +0200492 bool with_aa_mix, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200493{
494 struct via_spec *spec = codec->spec;
495 unsigned int caps;
Lydia Wangb89596a2011-07-04 17:01:33 +0800496 hda_nid_t pin, nid, pre_nid;
497 int i, idx, j, num;
Takashi Iwai5d417622011-06-20 11:32:27 +0200498
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200499 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200500 return;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200501 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200502
503 init_output_pin(codec, pin, pin_type);
504 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
505 if (caps & AC_AMPCAP_MUTE) {
506 unsigned int val;
507 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
508 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
509 AMP_OUT_MUTE | val);
510 }
511
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200512 activate_output_path(codec, path, true, force);
513
514 /* initialize the AA-path */
515 if (!spec->aa_mix_nid)
516 return;
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200517 for (i = path->depth - 1; i > 0; i--) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200518 nid = path->path[i];
Lydia Wangb89596a2011-07-04 17:01:33 +0800519 pre_nid = path->path[i - 1];
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200520 idx = get_connection_index(codec, nid, spec->aa_mix_nid);
521 if (idx >= 0) {
Lydia Wangb89596a2011-07-04 17:01:33 +0800522 if (have_mute(codec, nid, HDA_INPUT)) {
Takashi Iwaibac4b922011-07-04 17:35:51 +0200523 unsigned int mute = with_aa_mix ?
524 AMP_IN_UNMUTE(idx) : AMP_IN_MUTE(idx);
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200525 snd_hda_codec_write(codec, nid, 0,
526 AC_VERB_SET_AMP_GAIN_MUTE,
Takashi Iwaibac4b922011-07-04 17:35:51 +0200527 mute);
528 /* exclusively via aa-mix for front */
Lydia Wangb89596a2011-07-04 17:01:33 +0800529 if (pre_nid == spec->multiout.dac_nids[0]) {
530 num = snd_hda_get_conn_list(codec, nid,
531 NULL);
532 for (j = 0; j < num; j++) {
533 if (j == idx)
534 continue;
535 snd_hda_codec_write(codec,
536 nid, 0,
537 AC_VERB_SET_AMP_GAIN_MUTE,
538 AMP_IN_MUTE(j));
539 }
540 }
541 }
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200542 break;
543 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200544 }
545}
546
Joseph Chanc577b8a2006-11-29 15:29:40 +0100547static void via_auto_init_multi_out(struct hda_codec *codec)
548{
549 struct via_spec *spec = codec->spec;
550 int i;
551
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200552 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwaibac4b922011-07-04 17:35:51 +0200553 /* enable aa-mute only for the front channel */
554 via_auto_init_output(codec, &spec->out_path[i], PIN_OUT,
555 i == 0, true);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100556}
557
558static void via_auto_init_hp_out(struct hda_codec *codec)
559{
560 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100561
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200562 if (!spec->hp_dac_nid) {
Takashi Iwaibac4b922011-07-04 17:35:51 +0200563 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP,
564 true, true);
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200565 return;
566 }
567 if (spec->hp_independent_mode) {
568 activate_output_path(codec, &spec->hp_dep_path, false, false);
Takashi Iwaibac4b922011-07-04 17:35:51 +0200569 via_auto_init_output(codec, &spec->hp_path, PIN_HP,
570 true, true);
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200571 } else {
572 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwaibac4b922011-07-04 17:35:51 +0200573 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP,
574 true, true);
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200575 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100576}
577
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200578static void via_auto_init_speaker_out(struct hda_codec *codec)
579{
580 struct via_spec *spec = codec->spec;
581
582 if (spec->autocfg.speaker_outs)
Takashi Iwaibac4b922011-07-04 17:35:51 +0200583 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT,
584 true, true);
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200585}
586
Takashi Iwaif4a78282011-06-17 18:46:48 +0200587static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200588
Joseph Chanc577b8a2006-11-29 15:29:40 +0100589static void via_auto_init_analog_input(struct hda_codec *codec)
590{
591 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200592 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200593 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200594 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200595 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100596
Takashi Iwai096a8852011-06-20 12:09:02 +0200597 /* init ADCs */
598 for (i = 0; i < spec->num_adc_nids; i++) {
599 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
600 AC_VERB_SET_AMP_GAIN_MUTE,
601 AMP_IN_UNMUTE(0));
602 }
603
604 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200605 for (i = 0; i < cfg->num_inputs; i++) {
606 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200607 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200608 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100609 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200610 ctl = PIN_VREF50;
611 else
612 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100613 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200614 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100615 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200616
617 /* init input-src */
618 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200619 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
620 if (spec->mux_nids[adc_idx]) {
621 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
622 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
623 AC_VERB_SET_CONNECT_SEL,
624 mux_idx);
625 }
626 if (spec->dyn_adc_switch)
627 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200628 }
629
630 /* init aa-mixer */
631 if (!spec->aa_mix_nid)
632 return;
633 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
634 ARRAY_SIZE(conn));
635 for (i = 0; i < num_conns; i++) {
636 unsigned int caps = get_wcaps(codec, conn[i]);
637 if (get_wcaps_type(caps) == AC_WID_PIN)
638 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
639 AC_VERB_SET_AMP_GAIN_MUTE,
640 AMP_IN_MUTE(i));
641 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100642}
Lydia Wangf5271102009-10-10 19:07:35 +0800643
644static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
645 unsigned int *affected_parm)
646{
647 unsigned parm;
648 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
649 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
650 >> AC_DEFCFG_MISC_SHIFT
651 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800652 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200653 unsigned present = 0;
654
655 no_presence |= spec->no_pin_power_ctl;
656 if (!no_presence)
657 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200658 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800659 || ((no_presence || present)
660 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800661 *affected_parm = AC_PWRST_D0; /* if it's connected */
662 parm = AC_PWRST_D0;
663 } else
664 parm = AC_PWRST_D3;
665
666 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
667}
668
Takashi Iwai24088a52011-06-17 16:59:21 +0200669static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
670 struct snd_ctl_elem_info *uinfo)
671{
672 static const char * const texts[] = {
673 "Disabled", "Enabled"
674 };
675
676 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
677 uinfo->count = 1;
678 uinfo->value.enumerated.items = 2;
679 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
680 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
681 strcpy(uinfo->value.enumerated.name,
682 texts[uinfo->value.enumerated.item]);
683 return 0;
684}
685
686static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
687 struct snd_ctl_elem_value *ucontrol)
688{
689 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
690 struct via_spec *spec = codec->spec;
691 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
692 return 0;
693}
694
695static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
696 struct snd_ctl_elem_value *ucontrol)
697{
698 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
699 struct via_spec *spec = codec->spec;
700 unsigned int val = !ucontrol->value.enumerated.item[0];
701
702 if (val == spec->no_pin_power_ctl)
703 return 0;
704 spec->no_pin_power_ctl = val;
705 set_widgets_power_state(codec);
706 return 1;
707}
708
709static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
710 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
711 .name = "Dynamic Power-Control",
712 .info = via_pin_power_ctl_info,
713 .get = via_pin_power_ctl_get,
714 .put = via_pin_power_ctl_put,
715};
716
717
Harald Welte0aa62ae2008-09-09 15:58:27 +0800718static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
719 struct snd_ctl_elem_info *uinfo)
720{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200721 static const char * const texts[] = { "OFF", "ON" };
722
723 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
724 uinfo->count = 1;
725 uinfo->value.enumerated.items = 2;
726 if (uinfo->value.enumerated.item >= 2)
727 uinfo->value.enumerated.item = 1;
728 strcpy(uinfo->value.enumerated.name,
729 texts[uinfo->value.enumerated.item]);
730 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800731}
732
733static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
734 struct snd_ctl_elem_value *ucontrol)
735{
736 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800737 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800738
Takashi Iwaiece8d042011-06-19 16:24:21 +0200739 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800740 return 0;
741}
742
Harald Welte0aa62ae2008-09-09 15:58:27 +0800743static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
744 struct snd_ctl_elem_value *ucontrol)
745{
746 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
747 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +0200748 int cur;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200749
Takashi Iwai25250502011-06-30 17:24:47 +0200750 /* no independent-hp status change during PCM playback is running */
751 if (spec->num_active_streams)
752 return -EBUSY;
753
754 cur = !!ucontrol->value.enumerated.item[0];
755 if (spec->hp_independent_mode == cur)
756 return 0;
757 spec->hp_independent_mode = cur;
758 if (cur) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200759 activate_output_path(codec, &spec->hp_dep_path, false, false);
760 activate_output_path(codec, &spec->hp_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200761 if (spec->hp_indep_shared)
762 activate_output_path(codec, &spec->out_path[HDA_SIDE],
763 false, false);
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200764 } else {
765 activate_output_path(codec, &spec->hp_path, false, false);
766 activate_output_path(codec, &spec->hp_dep_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200767 if (spec->hp_indep_shared)
768 activate_output_path(codec, &spec->out_path[HDA_SIDE],
769 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200770 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800771
Lydia Wangce0e5a92011-03-22 16:22:37 +0800772 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800773 set_widgets_power_state(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200774 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800775}
776
Takashi Iwaiece8d042011-06-19 16:24:21 +0200777static const struct snd_kcontrol_new via_hp_mixer = {
778 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
779 .name = "Independent HP",
780 .info = via_independent_hp_info,
781 .get = via_independent_hp_get,
782 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800783};
784
Takashi Iwai3d83e572010-04-14 14:36:23 +0200785static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100786{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200787 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100788 struct snd_kcontrol_new *knew;
789 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100790
Takashi Iwaiece8d042011-06-19 16:24:21 +0200791 nid = spec->autocfg.hp_pins[0];
792 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200793 if (knew == NULL)
794 return -ENOMEM;
795
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100796 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100797
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100798 return 0;
799}
800
Lydia Wang1564b282009-10-10 19:07:52 +0800801static void notify_aa_path_ctls(struct hda_codec *codec)
802{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200803 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800804 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800805
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200806 for (i = 0; i < spec->smart51_nums; i++) {
807 struct snd_kcontrol *ctl;
808 struct snd_ctl_elem_id id;
809 memset(&id, 0, sizeof(id));
810 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
811 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800812 ctl = snd_hda_find_mixer_ctl(codec, id.name);
813 if (ctl)
814 snd_ctl_notify(codec->bus->card,
815 SNDRV_CTL_EVENT_MASK_VALUE,
816 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800817 }
818}
819
820static void mute_aa_path(struct hda_codec *codec, int mute)
821{
822 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200823 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800824 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200825
Lydia Wang1564b282009-10-10 19:07:52 +0800826 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200827 for (i = 0; i < spec->smart51_nums; i++) {
828 if (spec->smart51_idxs[i] < 0)
829 continue;
830 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
831 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800832 HDA_AMP_MUTE, val);
833 }
834}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200835
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200836static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
837{
838 struct via_spec *spec = codec->spec;
839 int i;
840
841 for (i = 0; i < spec->smart51_nums; i++)
842 if (spec->smart51_pins[i] == pin)
843 return true;
844 return false;
845}
846
Lydia Wang1564b282009-10-10 19:07:52 +0800847static int via_smart51_get(struct snd_kcontrol *kcontrol,
848 struct snd_ctl_elem_value *ucontrol)
849{
850 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
851 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800852
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200853 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800854 return 0;
855}
856
857static int via_smart51_put(struct snd_kcontrol *kcontrol,
858 struct snd_ctl_elem_value *ucontrol)
859{
860 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
861 struct via_spec *spec = codec->spec;
862 int out_in = *ucontrol->value.integer.value
863 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800864 int i;
865
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200866 for (i = 0; i < spec->smart51_nums; i++) {
867 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200868 unsigned int parm;
869
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200870 parm = snd_hda_codec_read(codec, nid, 0,
871 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
872 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
873 parm |= out_in;
874 snd_hda_codec_write(codec, nid, 0,
875 AC_VERB_SET_PIN_WIDGET_CONTROL,
876 parm);
877 if (out_in == AC_PINCTL_OUT_EN) {
878 mute_aa_path(codec, 1);
879 notify_aa_path_ctls(codec);
880 }
Lydia Wang1564b282009-10-10 19:07:52 +0800881 }
882 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800883 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800884 return 1;
885}
886
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200887static const struct snd_kcontrol_new via_smart51_mixer = {
888 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
889 .name = "Smart 5.1",
890 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200891 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200892 .get = via_smart51_get,
893 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800894};
895
Takashi Iwaif4a78282011-06-17 18:46:48 +0200896static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100897{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200898 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100899
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200900 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800901 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200902 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100903 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100904 return 0;
905}
906
Takashi Iwaiada509e2011-06-20 15:40:19 +0200907/* check AA path's mute status */
908static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800909{
Lydia Wangf5271102009-10-10 19:07:35 +0800910 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200911 const struct hda_amp_list *p;
912 int i, ch, v;
913
914 for (i = 0; i < spec->num_loopbacks; i++) {
915 p = &spec->loopback_list[i];
916 for (ch = 0; ch < 2; ch++) {
917 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
918 p->idx);
919 if (!(v & HDA_AMP_MUTE) && v > 0)
920 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800921 }
922 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200923 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800924}
925
926/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200927static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800928{
929 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200930 bool enable;
931 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800932
Takashi Iwaiada509e2011-06-20 15:40:19 +0200933 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800934
935 /* decide low current mode's verb & parameter */
936 switch (spec->codec_type) {
937 case VT1708B_8CH:
938 case VT1708B_4CH:
939 verb = 0xf70;
940 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
941 break;
942 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800943 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800944 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800945 verb = 0xf73;
946 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
947 break;
948 case VT1702:
949 verb = 0xf73;
950 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
951 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800952 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800953 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800954 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800955 verb = 0xf93;
956 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
957 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800958 default:
959 return; /* other codecs are not supported */
960 }
961 /* send verb */
962 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
963}
964
Joseph Chanc577b8a2006-11-29 15:29:40 +0100965/*
966 * generic initialization of ADC, input mixers and output mixers
967 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200968static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800969 /* power down jack detect function */
970 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +0100971 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100972};
973
Takashi Iwaiada509e2011-06-20 15:40:19 +0200974static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200975{
Takashi Iwaiada509e2011-06-20 15:40:19 +0200976 struct via_spec *spec = codec->spec;
977
978 if (active)
979 spec->num_active_streams++;
980 else
981 spec->num_active_streams--;
982 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200983}
984
Takashi Iwaiece8d042011-06-19 16:24:21 +0200985static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100986 struct hda_codec *codec,
987 struct snd_pcm_substream *substream)
988{
989 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +0200990 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200991 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +0200992
Takashi Iwai25250502011-06-30 17:24:47 +0200993 spec->multiout.hp_nid = 0;
994 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
995 if (!spec->hp_independent_mode) {
996 if (!spec->hp_indep_shared)
997 spec->multiout.hp_nid = spec->hp_dac_nid;
998 } else {
999 if (spec->hp_indep_shared)
1000 spec->multiout.num_dacs = cfg->line_outs - 1;
1001 }
1002 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001003 set_stream_active(codec, true);
1004 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1005 hinfo);
1006 if (err < 0) {
1007 spec->multiout.hp_nid = 0;
1008 set_stream_active(codec, false);
1009 return err;
1010 }
1011 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001012}
1013
Takashi Iwaiece8d042011-06-19 16:24:21 +02001014static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001015 struct hda_codec *codec,
1016 struct snd_pcm_substream *substream)
1017{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001018 struct via_spec *spec = codec->spec;
1019
1020 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001021 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001022 return 0;
1023}
1024
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001025static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1026 struct hda_codec *codec,
1027 struct snd_pcm_substream *substream)
1028{
1029 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001030
Takashi Iwaiece8d042011-06-19 16:24:21 +02001031 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001032 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001033 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1034 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001035 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001036 return 0;
1037}
1038
1039static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1040 struct hda_codec *codec,
1041 struct snd_pcm_substream *substream)
1042{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001043 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001044 return 0;
1045}
1046
1047static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1048 struct hda_codec *codec,
1049 unsigned int stream_tag,
1050 unsigned int format,
1051 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001052{
1053 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001054
Takashi Iwaiece8d042011-06-19 16:24:21 +02001055 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1056 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001057 vt1708_start_hp_work(spec);
1058 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001059}
1060
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001061static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1062 struct hda_codec *codec,
1063 unsigned int stream_tag,
1064 unsigned int format,
1065 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001066{
1067 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001068
Takashi Iwaiece8d042011-06-19 16:24:21 +02001069 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1070 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001071 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001072 return 0;
1073}
1074
1075static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1076 struct hda_codec *codec,
1077 struct snd_pcm_substream *substream)
1078{
1079 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001080
Takashi Iwaiece8d042011-06-19 16:24:21 +02001081 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001082 vt1708_stop_hp_work(spec);
1083 return 0;
1084}
1085
1086static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1087 struct hda_codec *codec,
1088 struct snd_pcm_substream *substream)
1089{
1090 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001091
Takashi Iwaiece8d042011-06-19 16:24:21 +02001092 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001093 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001094 return 0;
1095}
1096
Joseph Chanc577b8a2006-11-29 15:29:40 +01001097/*
1098 * Digital out
1099 */
1100static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1101 struct hda_codec *codec,
1102 struct snd_pcm_substream *substream)
1103{
1104 struct via_spec *spec = codec->spec;
1105 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1106}
1107
1108static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1109 struct hda_codec *codec,
1110 struct snd_pcm_substream *substream)
1111{
1112 struct via_spec *spec = codec->spec;
1113 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1114}
1115
Harald Welte5691ec72008-09-15 22:42:26 +08001116static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001117 struct hda_codec *codec,
1118 unsigned int stream_tag,
1119 unsigned int format,
1120 struct snd_pcm_substream *substream)
1121{
1122 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001123 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1124 stream_tag, format, substream);
1125}
Harald Welte5691ec72008-09-15 22:42:26 +08001126
Takashi Iwai9da29272009-05-07 16:31:14 +02001127static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1128 struct hda_codec *codec,
1129 struct snd_pcm_substream *substream)
1130{
1131 struct via_spec *spec = codec->spec;
1132 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001133 return 0;
1134}
1135
Joseph Chanc577b8a2006-11-29 15:29:40 +01001136/*
1137 * Analog capture
1138 */
1139static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1140 struct hda_codec *codec,
1141 unsigned int stream_tag,
1142 unsigned int format,
1143 struct snd_pcm_substream *substream)
1144{
1145 struct via_spec *spec = codec->spec;
1146
1147 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1148 stream_tag, 0, format);
1149 return 0;
1150}
1151
1152static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1153 struct hda_codec *codec,
1154 struct snd_pcm_substream *substream)
1155{
1156 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001157 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001158 return 0;
1159}
1160
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001161/* analog capture with dynamic ADC switching */
1162static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1163 struct hda_codec *codec,
1164 unsigned int stream_tag,
1165 unsigned int format,
1166 struct snd_pcm_substream *substream)
1167{
1168 struct via_spec *spec = codec->spec;
1169 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1170
1171 spec->cur_adc = spec->adc_nids[adc_idx];
1172 spec->cur_adc_stream_tag = stream_tag;
1173 spec->cur_adc_format = format;
1174 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
1175 return 0;
1176}
1177
1178static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1179 struct hda_codec *codec,
1180 struct snd_pcm_substream *substream)
1181{
1182 struct via_spec *spec = codec->spec;
1183
1184 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1185 spec->cur_adc = 0;
1186 return 0;
1187}
1188
1189/* re-setup the stream if running; called from input-src put */
1190static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1191{
1192 struct via_spec *spec = codec->spec;
1193 int adc_idx = spec->inputs[cur].adc_idx;
1194 hda_nid_t adc = spec->adc_nids[adc_idx];
1195
1196 if (spec->cur_adc && spec->cur_adc != adc) {
1197 /* stream is running, let's swap the current ADC */
1198 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1199 spec->cur_adc = adc;
1200 snd_hda_codec_setup_stream(codec, adc,
1201 spec->cur_adc_stream_tag, 0,
1202 spec->cur_adc_format);
1203 return true;
1204 }
1205 return false;
1206}
1207
Takashi Iwai9af74212011-06-18 16:17:45 +02001208static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001209 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001210 .channels_min = 2,
1211 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001212 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001213 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001214 .open = via_playback_multi_pcm_open,
1215 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001216 .prepare = via_playback_multi_pcm_prepare,
1217 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001218 },
1219};
1220
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001221static const struct hda_pcm_stream via_pcm_hp_playback = {
1222 .substreams = 1,
1223 .channels_min = 2,
1224 .channels_max = 2,
1225 /* NID is set in via_build_pcms */
1226 .ops = {
1227 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001228 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001229 .prepare = via_playback_hp_pcm_prepare,
1230 .cleanup = via_playback_hp_pcm_cleanup
1231 },
1232};
1233
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001234static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001235 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001236 .channels_min = 2,
1237 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001238 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001239 /* We got noisy outputs on the right channel on VT1708 when
1240 * 24bit samples are used. Until any workaround is found,
1241 * disable the 24bit format, so far.
1242 */
1243 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1244 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001245 .open = via_playback_multi_pcm_open,
1246 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001247 .prepare = via_playback_multi_pcm_prepare,
1248 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001249 },
1250};
1251
Takashi Iwai9af74212011-06-18 16:17:45 +02001252static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001253 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001254 .channels_min = 2,
1255 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001256 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001257 .ops = {
1258 .prepare = via_capture_pcm_prepare,
1259 .cleanup = via_capture_pcm_cleanup
1260 },
1261};
1262
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001263static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1264 .substreams = 1,
1265 .channels_min = 2,
1266 .channels_max = 2,
1267 /* NID is set in via_build_pcms */
1268 .ops = {
1269 .prepare = via_dyn_adc_capture_pcm_prepare,
1270 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1271 },
1272};
1273
Takashi Iwai9af74212011-06-18 16:17:45 +02001274static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001275 .substreams = 1,
1276 .channels_min = 2,
1277 .channels_max = 2,
1278 /* NID is set in via_build_pcms */
1279 .ops = {
1280 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001281 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001282 .prepare = via_dig_playback_pcm_prepare,
1283 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001284 },
1285};
1286
Takashi Iwai9af74212011-06-18 16:17:45 +02001287static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001288 .substreams = 1,
1289 .channels_min = 2,
1290 .channels_max = 2,
1291};
1292
Takashi Iwai370bafb2011-06-20 12:47:45 +02001293/*
1294 * slave controls for virtual master
1295 */
1296static const char * const via_slave_vols[] = {
1297 "Front Playback Volume",
1298 "Surround Playback Volume",
1299 "Center Playback Volume",
1300 "LFE Playback Volume",
1301 "Side Playback Volume",
1302 "Headphone Playback Volume",
1303 "Speaker Playback Volume",
1304 NULL,
1305};
1306
1307static const char * const via_slave_sws[] = {
1308 "Front Playback Switch",
1309 "Surround Playback Switch",
1310 "Center Playback Switch",
1311 "LFE Playback Switch",
1312 "Side Playback Switch",
1313 "Headphone Playback Switch",
1314 "Speaker Playback Switch",
1315 NULL,
1316};
1317
Joseph Chanc577b8a2006-11-29 15:29:40 +01001318static int via_build_controls(struct hda_codec *codec)
1319{
1320 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001321 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001322 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001323
Takashi Iwai24088a52011-06-17 16:59:21 +02001324 if (spec->set_widgets_power_state)
1325 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1326 return -ENOMEM;
1327
Joseph Chanc577b8a2006-11-29 15:29:40 +01001328 for (i = 0; i < spec->num_mixers; i++) {
1329 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1330 if (err < 0)
1331 return err;
1332 }
1333
1334 if (spec->multiout.dig_out_nid) {
1335 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001336 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001337 spec->multiout.dig_out_nid);
1338 if (err < 0)
1339 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001340 err = snd_hda_create_spdif_share_sw(codec,
1341 &spec->multiout);
1342 if (err < 0)
1343 return err;
1344 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001345 }
1346 if (spec->dig_in_nid) {
1347 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1348 if (err < 0)
1349 return err;
1350 }
Lydia Wang17314372009-10-10 19:07:37 +08001351
Takashi Iwai370bafb2011-06-20 12:47:45 +02001352 /* if we have no master control, let's create it */
1353 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1354 unsigned int vmaster_tlv[4];
1355 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1356 HDA_OUTPUT, vmaster_tlv);
1357 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1358 vmaster_tlv, via_slave_vols);
1359 if (err < 0)
1360 return err;
1361 }
1362 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1363 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1364 NULL, via_slave_sws);
1365 if (err < 0)
1366 return err;
1367 }
1368
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001369 /* assign Capture Source enums to NID */
1370 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1371 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001372 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001373 if (err < 0)
1374 return err;
1375 }
1376
Lydia Wang17314372009-10-10 19:07:37 +08001377 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001378 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001379 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001380
Takashi Iwai603c4012008-07-30 15:01:44 +02001381 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001382 return 0;
1383}
1384
1385static int via_build_pcms(struct hda_codec *codec)
1386{
1387 struct via_spec *spec = codec->spec;
1388 struct hda_pcm *info = spec->pcm_rec;
1389
1390 codec->num_pcms = 1;
1391 codec->pcm_info = info;
1392
Takashi Iwai82673bc2011-06-17 16:24:21 +02001393 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1394 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001395 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001396
1397 if (!spec->stream_analog_playback)
1398 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001399 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001400 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001401 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1402 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001403 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1404 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001405
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001406 if (!spec->stream_analog_capture) {
1407 if (spec->dyn_adc_switch)
1408 spec->stream_analog_capture =
1409 &via_pcm_dyn_adc_analog_capture;
1410 else
1411 spec->stream_analog_capture = &via_pcm_analog_capture;
1412 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001413 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1414 *spec->stream_analog_capture;
1415 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001416 if (!spec->dyn_adc_switch)
1417 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1418 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001419
1420 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1421 codec->num_pcms++;
1422 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001423 snprintf(spec->stream_name_digital,
1424 sizeof(spec->stream_name_digital),
1425 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001426 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001427 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001428 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001429 if (!spec->stream_digital_playback)
1430 spec->stream_digital_playback =
1431 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001432 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001433 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001434 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1435 spec->multiout.dig_out_nid;
1436 }
1437 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001438 if (!spec->stream_digital_capture)
1439 spec->stream_digital_capture =
1440 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001441 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001442 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001443 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1444 spec->dig_in_nid;
1445 }
1446 }
1447
Takashi Iwaiece8d042011-06-19 16:24:21 +02001448 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001449 codec->num_pcms++;
1450 info++;
1451 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1452 "%s HP", codec->chip_name);
1453 info->name = spec->stream_name_hp;
1454 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1455 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001456 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001457 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001458 return 0;
1459}
1460
1461static void via_free(struct hda_codec *codec)
1462{
1463 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001464
1465 if (!spec)
1466 return;
1467
Takashi Iwai603c4012008-07-30 15:01:44 +02001468 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001469 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001470 kfree(spec->bind_cap_vol);
1471 kfree(spec->bind_cap_sw);
1472 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001473}
1474
Takashi Iwai64be2852011-06-17 16:51:39 +02001475/* mute/unmute outputs */
1476static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1477 hda_nid_t *pins, bool mute)
1478{
1479 int i;
1480 for (i = 0; i < num_pins; i++)
1481 snd_hda_codec_write(codec, pins[i], 0,
1482 AC_VERB_SET_PIN_WIDGET_CONTROL,
1483 mute ? 0 : PIN_OUT);
1484}
1485
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001486/* mute internal speaker if line-out is plugged */
1487static void via_line_automute(struct hda_codec *codec, int present)
1488{
1489 struct via_spec *spec = codec->spec;
1490
1491 if (!spec->autocfg.speaker_outs)
1492 return;
1493 if (!present)
1494 present = snd_hda_jack_detect(codec,
1495 spec->autocfg.line_out_pins[0]);
1496 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1497 spec->autocfg.speaker_pins,
1498 present);
1499}
1500
Harald Welte69e52a82008-09-09 15:57:32 +08001501/* mute internal speaker if HP is plugged */
1502static void via_hp_automute(struct hda_codec *codec)
1503{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001504 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001505 struct via_spec *spec = codec->spec;
1506
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001507 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001508 int nums;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001509 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001510 if (spec->smart51_enabled)
1511 nums = spec->autocfg.line_outs + spec->smart51_nums;
1512 else
1513 nums = spec->autocfg.line_outs;
1514 toggle_output_mutes(codec, nums,
Takashi Iwai64be2852011-06-17 16:51:39 +02001515 spec->autocfg.line_out_pins,
1516 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001517 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001518 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001519}
1520
Harald Welte69e52a82008-09-09 15:57:32 +08001521static void via_gpio_control(struct hda_codec *codec)
1522{
1523 unsigned int gpio_data;
1524 unsigned int vol_counter;
1525 unsigned int vol;
1526 unsigned int master_vol;
1527
1528 struct via_spec *spec = codec->spec;
1529
1530 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1531 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1532
1533 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1534 0xF84, 0) & 0x3F0000) >> 16;
1535
1536 vol = vol_counter & 0x1F;
1537 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1538 AC_VERB_GET_AMP_GAIN_MUTE,
1539 AC_AMP_GET_INPUT);
1540
1541 if (gpio_data == 0x02) {
1542 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001543 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1544 AC_VERB_SET_PIN_WIDGET_CONTROL,
1545 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001546 if (vol_counter & 0x20) {
1547 /* decrease volume */
1548 if (vol > master_vol)
1549 vol = master_vol;
1550 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1551 0, HDA_AMP_VOLMASK,
1552 master_vol-vol);
1553 } else {
1554 /* increase volume */
1555 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1556 HDA_AMP_VOLMASK,
1557 ((master_vol+vol) > 0x2A) ? 0x2A :
1558 (master_vol+vol));
1559 }
1560 } else if (!(gpio_data & 0x02)) {
1561 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001562 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1563 AC_VERB_SET_PIN_WIDGET_CONTROL,
1564 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001565 }
1566}
1567
1568/* unsolicited event for jack sensing */
1569static void via_unsol_event(struct hda_codec *codec,
1570 unsigned int res)
1571{
1572 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001573
Lydia Wanga34df192009-10-10 19:08:01 +08001574 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001575 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001576
1577 res &= ~VIA_JACK_EVENT;
1578
1579 if (res == VIA_HP_EVENT)
1580 via_hp_automute(codec);
1581 else if (res == VIA_GPIO_EVENT)
1582 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001583 else if (res == VIA_LINE_EVENT)
1584 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001585}
1586
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001587#ifdef SND_HDA_NEEDS_RESUME
1588static int via_suspend(struct hda_codec *codec, pm_message_t state)
1589{
1590 struct via_spec *spec = codec->spec;
1591 vt1708_stop_hp_work(spec);
1592 return 0;
1593}
1594#endif
1595
Takashi Iwaicb53c622007-08-10 17:21:45 +02001596#ifdef CONFIG_SND_HDA_POWER_SAVE
1597static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1598{
1599 struct via_spec *spec = codec->spec;
1600 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1601}
1602#endif
1603
Joseph Chanc577b8a2006-11-29 15:29:40 +01001604/*
1605 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001606
1607static int via_init(struct hda_codec *codec);
1608
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001609static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001610 .build_controls = via_build_controls,
1611 .build_pcms = via_build_pcms,
1612 .init = via_init,
1613 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001614 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001615#ifdef SND_HDA_NEEDS_RESUME
1616 .suspend = via_suspend,
1617#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001618#ifdef CONFIG_SND_HDA_POWER_SAVE
1619 .check_power_status = via_check_power_status,
1620#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001621};
1622
Takashi Iwai4a796162011-06-17 17:53:38 +02001623static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001624{
Takashi Iwai4a796162011-06-17 17:53:38 +02001625 struct via_spec *spec = codec->spec;
1626 int i;
1627
1628 for (i = 0; i < spec->multiout.num_dacs; i++) {
1629 if (spec->multiout.dac_nids[i] == dac)
1630 return false;
1631 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001632 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001633 return false;
1634 return true;
1635}
1636
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001637static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001638 hda_nid_t target_dac, struct nid_path *path,
1639 int depth, int wid_type)
1640{
1641 hda_nid_t conn[8];
1642 int i, nums;
1643
1644 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1645 for (i = 0; i < nums; i++) {
1646 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1647 continue;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001648 if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
1649 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001650 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001651 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001652 return false;
1653 for (i = 0; i < nums; i++) {
1654 unsigned int type;
1655 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1656 if (type == AC_WID_AUD_OUT ||
1657 (wid_type != -1 && type != wid_type))
1658 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001659 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001660 path, depth + 1, AC_WID_AUD_SEL))
1661 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001662 }
1663 return false;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001664
1665 found:
1666 path->path[path->depth] = conn[i];
1667 path->idx[path->depth] = i;
1668 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1669 path->multi[path->depth] = 1;
1670 path->depth++;
1671 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001672}
1673
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001674static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1675 hda_nid_t target_dac, struct nid_path *path)
1676{
1677 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1678 path->path[path->depth] = nid;
1679 path->depth++;
1680 return true;
1681 }
1682 return false;
1683}
1684
Takashi Iwai4a796162011-06-17 17:53:38 +02001685static int via_auto_fill_dac_nids(struct hda_codec *codec)
1686{
1687 struct via_spec *spec = codec->spec;
1688 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001689 int i;
1690 hda_nid_t nid;
1691
Joseph Chanc577b8a2006-11-29 15:29:40 +01001692 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001693 spec->multiout.num_dacs = cfg->line_outs;
1694 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001695 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001696 if (!nid)
1697 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001698 if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
1699 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001700 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001701 return 0;
1702}
1703
Takashi Iwai4a796162011-06-17 17:53:38 +02001704static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001705 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001706{
Takashi Iwai4a796162011-06-17 17:53:38 +02001707 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001708 char name[32];
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001709 hda_nid_t dac, pin, sel, nid;
1710 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001711
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001712 dac = check_dac ? path->path[0] : 0;
1713 pin = path->path[path->depth - 1];
1714 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001715
Takashi Iwai8df2a312011-06-21 11:48:29 +02001716 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001717 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001718 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001719 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001720 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1721 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001722 else
1723 nid = 0;
1724 if (nid) {
1725 sprintf(name, "%s Playback Volume", pfx);
1726 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001727 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001728 if (err < 0)
1729 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001730 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001731 }
1732
Takashi Iwai8df2a312011-06-21 11:48:29 +02001733 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001734 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001735 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001736 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001737 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1738 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001739 else
1740 nid = 0;
1741 if (nid) {
1742 sprintf(name, "%s Playback Switch", pfx);
1743 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1744 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1745 if (err < 0)
1746 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001747 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001748 }
1749 return 0;
1750}
1751
Takashi Iwaif4a78282011-06-17 18:46:48 +02001752static void mangle_smart51(struct hda_codec *codec)
1753{
1754 struct via_spec *spec = codec->spec;
1755 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001756 struct auto_pin_cfg_item *ins = cfg->inputs;
1757 int i, j, nums, attr;
1758 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001759
Takashi Iwai0f98c242011-06-21 12:51:33 +02001760 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1761 nums = 0;
1762 for (i = 0; i < cfg->num_inputs; i++) {
1763 unsigned int def;
1764 if (ins[i].type > AUTO_PIN_LINE_IN)
1765 continue;
1766 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1767 if (snd_hda_get_input_pin_attr(def) != attr)
1768 continue;
1769 for (j = 0; j < nums; j++)
1770 if (ins[pins[j]].type < ins[i].type) {
1771 memmove(pins + j + 1, pins + j,
1772 (nums - j - 1) * sizeof(int));
1773 break;
1774 }
1775 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001776 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001777 }
1778 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001779 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001780 for (i = 0; i < nums; i++) {
1781 hda_nid_t pin = ins[pins[i]].pin;
1782 spec->smart51_pins[spec->smart51_nums++] = pin;
1783 cfg->line_out_pins[cfg->line_outs++] = pin;
1784 if (cfg->line_outs == 3)
1785 break;
1786 }
1787 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001788 }
1789}
1790
Takashi Iwai4a796162011-06-17 17:53:38 +02001791/* add playback controls from the parsed DAC table */
1792static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1793{
1794 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001795 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001796 static const char * const chname[4] = {
1797 "Front", "Surround", "C/LFE", "Side"
1798 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001799 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001800 int old_line_outs;
1801
1802 /* check smart51 */
1803 old_line_outs = cfg->line_outs;
1804 if (cfg->line_outs == 1)
1805 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001806
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001807 err = via_auto_fill_dac_nids(codec);
1808 if (err < 0)
1809 return err;
1810
Takashi Iwai4a796162011-06-17 17:53:38 +02001811 for (i = 0; i < cfg->line_outs; i++) {
1812 hda_nid_t pin, dac;
1813 pin = cfg->line_out_pins[i];
1814 dac = spec->multiout.dac_nids[i];
1815 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001816 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001817 if (i == HDA_CLFE) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001818 err = create_ch_ctls(codec, "Center", 1, true,
1819 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001820 if (err < 0)
1821 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001822 err = create_ch_ctls(codec, "LFE", 2, true,
1823 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001824 if (err < 0)
1825 return err;
1826 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001827 const char *pfx = chname[i];
1828 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1829 cfg->line_outs == 1)
1830 pfx = "Speaker";
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001831 err = create_ch_ctls(codec, pfx, 3, true,
1832 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001833 if (err < 0)
1834 return err;
1835 }
1836 }
1837
Takashi Iwai4a796162011-06-17 17:53:38 +02001838 idx = get_connection_index(codec, spec->aa_mix_nid,
1839 spec->multiout.dac_nids[0]);
Lydia Wangc4394f52011-07-04 16:54:15 +08001840 if (idx < 0 && spec->dac_mixer_idx)
1841 idx = spec->dac_mixer_idx;
Takashi Iwai4a796162011-06-17 17:53:38 +02001842 if (idx >= 0) {
1843 /* add control to mixer */
1844 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1845 "PCM Playback Volume",
1846 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1847 idx, HDA_INPUT));
1848 if (err < 0)
1849 return err;
1850 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1851 "PCM Playback Switch",
1852 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1853 idx, HDA_INPUT));
1854 if (err < 0)
1855 return err;
1856 }
1857
Takashi Iwaif4a78282011-06-17 18:46:48 +02001858 cfg->line_outs = old_line_outs;
1859
Joseph Chanc577b8a2006-11-29 15:29:40 +01001860 return 0;
1861}
1862
Takashi Iwai4a796162011-06-17 17:53:38 +02001863static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001864{
Takashi Iwai4a796162011-06-17 17:53:38 +02001865 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001866 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001867 bool check_dac;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001868 int err;
1869
1870 if (!pin)
1871 return 0;
1872
Takashi Iwai8df2a312011-06-21 11:48:29 +02001873 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001874 spec->hp_dac_nid = spec->hp_path.path[0];
Takashi Iwai25250502011-06-30 17:24:47 +02001875 else if (spec->multiout.dac_nids[HDA_SIDE] &&
1876 parse_output_path(codec, pin,
1877 spec->multiout.dac_nids[HDA_SIDE],
1878 &spec->hp_path)) {
1879 spec->hp_dac_nid = spec->hp_path.path[0];
1880 spec->hp_indep_shared = true;
1881 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001882
Takashi Iwaiece8d042011-06-19 16:24:21 +02001883 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001884 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001885 !spec->hp_dac_nid)
1886 return 0;
1887
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001888 if (spec->hp_dac_nid && !spec->hp_indep_shared) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001889 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001890 check_dac = true;
1891 } else {
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001892 path = &spec->hp_dep_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001893 check_dac = false;
1894 }
1895 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001896 if (err < 0)
1897 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001898 if (spec->hp_dac_nid) {
1899 spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
1900 spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
1901 }
Harald Welte0aa62ae2008-09-09 15:58:27 +08001902
Joseph Chanc577b8a2006-11-29 15:29:40 +01001903 return 0;
1904}
1905
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001906static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1907{
1908 struct via_spec *spec = codec->spec;
1909 hda_nid_t pin, dac;
1910
1911 pin = spec->autocfg.speaker_pins[0];
1912 if (!spec->autocfg.speaker_outs || !pin)
1913 return 0;
1914
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001915 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1916 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001917 spec->multiout.extra_out_nid[0] = dac;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001918 return create_ch_ctls(codec, "Speaker", 3, true,
1919 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001920 }
1921 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001922 &spec->speaker_path))
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001923 return create_ch_ctls(codec, "Speaker", 3, false,
1924 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001925
1926 return 0;
1927}
1928
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001929/* look for ADCs */
1930static int via_fill_adcs(struct hda_codec *codec)
1931{
1932 struct via_spec *spec = codec->spec;
1933 hda_nid_t nid = codec->start_nid;
1934 int i;
1935
1936 for (i = 0; i < codec->num_nodes; i++, nid++) {
1937 unsigned int wcaps = get_wcaps(codec, nid);
1938 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1939 continue;
1940 if (wcaps & AC_WCAP_DIGITAL)
1941 continue;
1942 if (!(wcaps & AC_WCAP_CONN_LIST))
1943 continue;
1944 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1945 return -ENOMEM;
1946 spec->adc_nids[spec->num_adc_nids++] = nid;
1947 }
1948 return 0;
1949}
1950
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001951/* input-src control */
1952static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
1953 struct snd_ctl_elem_info *uinfo)
1954{
1955 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1956 struct via_spec *spec = codec->spec;
1957
1958 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1959 uinfo->count = 1;
1960 uinfo->value.enumerated.items = spec->num_inputs;
1961 if (uinfo->value.enumerated.item >= spec->num_inputs)
1962 uinfo->value.enumerated.item = spec->num_inputs - 1;
1963 strcpy(uinfo->value.enumerated.name,
1964 spec->inputs[uinfo->value.enumerated.item].label);
1965 return 0;
1966}
1967
1968static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
1969 struct snd_ctl_elem_value *ucontrol)
1970{
1971 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1972 struct via_spec *spec = codec->spec;
1973 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1974
1975 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
1976 return 0;
1977}
1978
1979static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
1980 struct snd_ctl_elem_value *ucontrol)
1981{
1982 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1983 struct via_spec *spec = codec->spec;
1984 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1985 hda_nid_t mux;
1986 int cur;
1987
1988 cur = ucontrol->value.enumerated.item[0];
1989 if (cur < 0 || cur >= spec->num_inputs)
1990 return -EINVAL;
1991 if (spec->cur_mux[idx] == cur)
1992 return 0;
1993 spec->cur_mux[idx] = cur;
1994 if (spec->dyn_adc_switch) {
1995 int adc_idx = spec->inputs[cur].adc_idx;
1996 mux = spec->mux_nids[adc_idx];
1997 via_dyn_adc_pcm_resetup(codec, cur);
1998 } else {
1999 mux = spec->mux_nids[idx];
2000 if (snd_BUG_ON(!mux))
2001 return -EINVAL;
2002 }
2003
2004 if (mux) {
2005 /* switch to D0 beofre change index */
2006 if (snd_hda_codec_read(codec, mux, 0,
2007 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2008 snd_hda_codec_write(codec, mux, 0,
2009 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2010 snd_hda_codec_write(codec, mux, 0,
2011 AC_VERB_SET_CONNECT_SEL,
2012 spec->inputs[cur].mux_idx);
2013 }
2014
2015 /* update jack power state */
2016 set_widgets_power_state(codec);
2017 return 0;
2018}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002019
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002020static const struct snd_kcontrol_new via_input_src_ctl = {
2021 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2022 /* The multiple "Capture Source" controls confuse alsamixer
2023 * So call somewhat different..
2024 */
2025 /* .name = "Capture Source", */
2026 .name = "Input Source",
2027 .info = via_mux_enum_info,
2028 .get = via_mux_enum_get,
2029 .put = via_mux_enum_put,
2030};
2031
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002032static int create_input_src_ctls(struct hda_codec *codec, int count)
2033{
2034 struct via_spec *spec = codec->spec;
2035 struct snd_kcontrol_new *knew;
2036
2037 if (spec->num_inputs <= 1 || !count)
2038 return 0; /* no need for single src */
2039
2040 knew = via_clone_control(spec, &via_input_src_ctl);
2041 if (!knew)
2042 return -ENOMEM;
2043 knew->count = count;
2044 return 0;
2045}
2046
2047/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002048static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2049{
2050 struct hda_amp_list *list;
2051
2052 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2053 return;
2054 list = spec->loopback_list + spec->num_loopbacks;
2055 list->nid = mix;
2056 list->dir = HDA_INPUT;
2057 list->idx = idx;
2058 spec->num_loopbacks++;
2059 spec->loopback.amplist = spec->loopback_list;
2060}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002061
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002062static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002063 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002064{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002065 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002066}
2067
2068/* add the input-route to the given pin */
2069static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002070{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002071 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002072 int c, idx;
2073
2074 spec->inputs[spec->num_inputs].adc_idx = -1;
2075 spec->inputs[spec->num_inputs].pin = pin;
2076 for (c = 0; c < spec->num_adc_nids; c++) {
2077 if (spec->mux_nids[c]) {
2078 idx = get_connection_index(codec, spec->mux_nids[c],
2079 pin);
2080 if (idx < 0)
2081 continue;
2082 spec->inputs[spec->num_inputs].mux_idx = idx;
2083 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002084 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002085 continue;
2086 }
2087 spec->inputs[spec->num_inputs].adc_idx = c;
2088 /* Can primary ADC satisfy all inputs? */
2089 if (!spec->dyn_adc_switch &&
2090 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2091 snd_printd(KERN_INFO
2092 "via: dynamic ADC switching enabled\n");
2093 spec->dyn_adc_switch = 1;
2094 }
2095 return true;
2096 }
2097 return false;
2098}
2099
2100static int get_mux_nids(struct hda_codec *codec);
2101
2102/* parse input-routes; fill ADCs, MUXs and input-src entries */
2103static int parse_analog_inputs(struct hda_codec *codec)
2104{
2105 struct via_spec *spec = codec->spec;
2106 const struct auto_pin_cfg *cfg = &spec->autocfg;
2107 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002108
2109 err = via_fill_adcs(codec);
2110 if (err < 0)
2111 return err;
2112 err = get_mux_nids(codec);
2113 if (err < 0)
2114 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002115
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002116 /* fill all input-routes */
2117 for (i = 0; i < cfg->num_inputs; i++) {
2118 if (add_input_route(codec, cfg->inputs[i].pin))
2119 spec->inputs[spec->num_inputs++].label =
2120 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002121 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002122
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002123 /* check for internal loopback recording */
2124 if (spec->aa_mix_nid &&
2125 add_input_route(codec, spec->aa_mix_nid))
2126 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2127
2128 return 0;
2129}
2130
2131/* create analog-loopback volume/switch controls */
2132static int create_loopback_ctls(struct hda_codec *codec)
2133{
2134 struct via_spec *spec = codec->spec;
2135 const struct auto_pin_cfg *cfg = &spec->autocfg;
2136 const char *prev_label = NULL;
2137 int type_idx = 0;
2138 int i, j, err, idx;
2139
2140 if (!spec->aa_mix_nid)
2141 return 0;
2142
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002143 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002144 hda_nid_t pin = cfg->inputs[i].pin;
2145 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2146
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002147 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002148 type_idx++;
2149 else
2150 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002151 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002152 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2153 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002154 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002155 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002156 if (err < 0)
2157 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002158 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002159 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002160
2161 /* remember the label for smart51 control */
2162 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002163 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002164 spec->smart51_idxs[j] = idx;
2165 spec->smart51_labels[j] = label;
2166 break;
2167 }
2168 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002169 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002170 return 0;
2171}
2172
2173/* create mic-boost controls (if present) */
2174static int create_mic_boost_ctls(struct hda_codec *codec)
2175{
2176 struct via_spec *spec = codec->spec;
2177 const struct auto_pin_cfg *cfg = &spec->autocfg;
2178 int i, err;
2179
2180 for (i = 0; i < cfg->num_inputs; i++) {
2181 hda_nid_t pin = cfg->inputs[i].pin;
2182 unsigned int caps;
2183 const char *label;
2184 char name[32];
2185
2186 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2187 continue;
2188 caps = query_amp_caps(codec, pin, HDA_INPUT);
2189 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2190 continue;
2191 label = hda_get_autocfg_input_label(codec, cfg, i);
2192 snprintf(name, sizeof(name), "%s Boost Volume", label);
2193 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2194 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2195 if (err < 0)
2196 return err;
2197 }
2198 return 0;
2199}
2200
2201/* create capture and input-src controls for multiple streams */
2202static int create_multi_adc_ctls(struct hda_codec *codec)
2203{
2204 struct via_spec *spec = codec->spec;
2205 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002206
2207 /* create capture mixer elements */
2208 for (i = 0; i < spec->num_adc_nids; i++) {
2209 hda_nid_t adc = spec->adc_nids[i];
2210 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2211 "Capture Volume", i,
2212 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2213 HDA_INPUT));
2214 if (err < 0)
2215 return err;
2216 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2217 "Capture Switch", i,
2218 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2219 HDA_INPUT));
2220 if (err < 0)
2221 return err;
2222 }
2223
2224 /* input-source control */
2225 for (i = 0; i < spec->num_adc_nids; i++)
2226 if (!spec->mux_nids[i])
2227 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002228 err = create_input_src_ctls(codec, i);
2229 if (err < 0)
2230 return err;
2231 return 0;
2232}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002233
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002234/* bind capture volume/switch */
2235static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2236 HDA_BIND_VOL("Capture Volume", 0);
2237static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2238 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002239
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002240static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2241 struct hda_ctl_ops *ops)
2242{
2243 struct hda_bind_ctls *ctl;
2244 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002245
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002246 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2247 if (!ctl)
2248 return -ENOMEM;
2249 ctl->ops = ops;
2250 for (i = 0; i < spec->num_adc_nids; i++)
2251 ctl->values[i] =
2252 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2253 *ctl_ret = ctl;
2254 return 0;
2255}
2256
2257/* create capture and input-src controls for dynamic ADC-switch case */
2258static int create_dyn_adc_ctls(struct hda_codec *codec)
2259{
2260 struct via_spec *spec = codec->spec;
2261 struct snd_kcontrol_new *knew;
2262 int err;
2263
2264 /* set up the bind capture ctls */
2265 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2266 if (err < 0)
2267 return err;
2268 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2269 if (err < 0)
2270 return err;
2271
2272 /* create capture mixer elements */
2273 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2274 if (!knew)
2275 return -ENOMEM;
2276 knew->private_value = (long)spec->bind_cap_vol;
2277
2278 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2279 if (!knew)
2280 return -ENOMEM;
2281 knew->private_value = (long)spec->bind_cap_sw;
2282
2283 /* input-source control */
2284 err = create_input_src_ctls(codec, 1);
2285 if (err < 0)
2286 return err;
2287 return 0;
2288}
2289
2290/* parse and create capture-related stuff */
2291static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2292{
2293 struct via_spec *spec = codec->spec;
2294 int err;
2295
2296 err = parse_analog_inputs(codec);
2297 if (err < 0)
2298 return err;
2299 if (spec->dyn_adc_switch)
2300 err = create_dyn_adc_ctls(codec);
2301 else
2302 err = create_multi_adc_ctls(codec);
2303 if (err < 0)
2304 return err;
2305 err = create_loopback_ctls(codec);
2306 if (err < 0)
2307 return err;
2308 err = create_mic_boost_ctls(codec);
2309 if (err < 0)
2310 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002311 return 0;
2312}
2313
Harald Welte76d9b0d2008-09-09 15:50:37 +08002314static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2315{
2316 unsigned int def_conf;
2317 unsigned char seqassoc;
2318
Takashi Iwai2f334f92009-02-20 14:37:42 +01002319 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002320 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2321 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002322 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2323 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2324 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2325 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002326 }
2327
2328 return;
2329}
2330
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002331static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002332 struct snd_ctl_elem_value *ucontrol)
2333{
2334 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2335 struct via_spec *spec = codec->spec;
2336
2337 if (spec->codec_type != VT1708)
2338 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002339 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002340 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002341 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002342 return 0;
2343}
2344
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002345static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002346 struct snd_ctl_elem_value *ucontrol)
2347{
2348 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2349 struct via_spec *spec = codec->spec;
2350 int change;
2351
2352 if (spec->codec_type != VT1708)
2353 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002354 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002355 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002356 == !spec->vt1708_jack_detect;
2357 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002358 mute_aa_path(codec, 1);
2359 notify_aa_path_ctls(codec);
2360 }
2361 return change;
2362}
2363
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002364static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2365 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2366 .name = "Jack Detect",
2367 .count = 1,
2368 .info = snd_ctl_boolean_mono_info,
2369 .get = vt1708_jack_detect_get,
2370 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002371};
2372
Takashi Iwai12daef62011-06-18 17:45:49 +02002373static void fill_dig_outs(struct hda_codec *codec);
2374static void fill_dig_in(struct hda_codec *codec);
2375
2376static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002377{
2378 struct via_spec *spec = codec->spec;
2379 int err;
2380
2381 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2382 if (err < 0)
2383 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002384 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002385 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002386
Takashi Iwai4a796162011-06-17 17:53:38 +02002387 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002388 if (err < 0)
2389 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002390 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002391 if (err < 0)
2392 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002393 err = via_auto_create_speaker_ctls(codec);
2394 if (err < 0)
2395 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002396 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002397 if (err < 0)
2398 return err;
2399
2400 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2401
Takashi Iwai12daef62011-06-18 17:45:49 +02002402 fill_dig_outs(codec);
2403 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002404
Takashi Iwai603c4012008-07-30 15:01:44 +02002405 if (spec->kctls.list)
2406 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002407
Joseph Chanc577b8a2006-11-29 15:29:40 +01002408
Takashi Iwai8df2a312011-06-21 11:48:29 +02002409 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002410 err = via_hp_build(codec);
2411 if (err < 0)
2412 return err;
2413 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002414
Takashi Iwaif4a78282011-06-17 18:46:48 +02002415 err = via_smart51_build(codec);
2416 if (err < 0)
2417 return err;
2418
Takashi Iwai5d417622011-06-20 11:32:27 +02002419 /* assign slave outs */
2420 if (spec->slave_dig_outs[0])
2421 codec->slave_dig_outs = spec->slave_dig_outs;
2422
Joseph Chanc577b8a2006-11-29 15:29:40 +01002423 return 1;
2424}
2425
Takashi Iwai5d417622011-06-20 11:32:27 +02002426static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002427{
Lydia Wang25eaba22009-10-10 19:08:43 +08002428 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002429 if (spec->multiout.dig_out_nid)
2430 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2431 if (spec->slave_dig_outs[0])
2432 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2433}
Lydia Wang25eaba22009-10-10 19:08:43 +08002434
Takashi Iwai5d417622011-06-20 11:32:27 +02002435static void via_auto_init_dig_in(struct hda_codec *codec)
2436{
2437 struct via_spec *spec = codec->spec;
2438 if (!spec->dig_in_nid)
2439 return;
2440 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2441 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2442}
2443
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002444/* initialize the unsolicited events */
2445static void via_auto_init_unsol_event(struct hda_codec *codec)
2446{
2447 struct via_spec *spec = codec->spec;
2448 struct auto_pin_cfg *cfg = &spec->autocfg;
2449 unsigned int ev;
2450 int i;
2451
2452 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2453 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2454 AC_VERB_SET_UNSOLICITED_ENABLE,
2455 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2456
2457 if (cfg->speaker_pins[0])
2458 ev = VIA_LINE_EVENT;
2459 else
2460 ev = 0;
2461 for (i = 0; i < cfg->line_outs; i++) {
2462 if (cfg->line_out_pins[i] &&
2463 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002464 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002465 AC_VERB_SET_UNSOLICITED_ENABLE,
2466 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2467 }
2468
2469 for (i = 0; i < cfg->num_inputs; i++) {
2470 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2471 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2472 AC_VERB_SET_UNSOLICITED_ENABLE,
2473 AC_USRSP_EN | VIA_JACK_EVENT);
2474 }
2475}
2476
Takashi Iwai5d417622011-06-20 11:32:27 +02002477static int via_init(struct hda_codec *codec)
2478{
2479 struct via_spec *spec = codec->spec;
2480 int i;
2481
2482 for (i = 0; i < spec->num_iverbs; i++)
2483 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2484
Joseph Chanc577b8a2006-11-29 15:29:40 +01002485 via_auto_init_multi_out(codec);
2486 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002487 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002488 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002489 via_auto_init_dig_outs(codec);
2490 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002491
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002492 via_auto_init_unsol_event(codec);
2493
2494 via_hp_automute(codec);
2495 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002496
Joseph Chanc577b8a2006-11-29 15:29:40 +01002497 return 0;
2498}
2499
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002500static void vt1708_update_hp_jack_state(struct work_struct *work)
2501{
2502 struct via_spec *spec = container_of(work, struct via_spec,
2503 vt1708_hp_work.work);
2504 if (spec->codec_type != VT1708)
2505 return;
2506 /* if jack state toggled */
2507 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002508 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002509 spec->vt1708_hp_present ^= 1;
2510 via_hp_automute(spec->codec);
2511 }
2512 vt1708_start_hp_work(spec);
2513}
2514
Takashi Iwai337b9d02009-07-07 18:18:59 +02002515static int get_mux_nids(struct hda_codec *codec)
2516{
2517 struct via_spec *spec = codec->spec;
2518 hda_nid_t nid, conn[8];
2519 unsigned int type;
2520 int i, n;
2521
2522 for (i = 0; i < spec->num_adc_nids; i++) {
2523 nid = spec->adc_nids[i];
2524 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002525 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002526 if (type == AC_WID_PIN)
2527 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002528 n = snd_hda_get_connections(codec, nid, conn,
2529 ARRAY_SIZE(conn));
2530 if (n <= 0)
2531 break;
2532 if (n > 1) {
2533 spec->mux_nids[i] = nid;
2534 break;
2535 }
2536 nid = conn[0];
2537 }
2538 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002539 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002540}
2541
Joseph Chanc577b8a2006-11-29 15:29:40 +01002542static int patch_vt1708(struct hda_codec *codec)
2543{
2544 struct via_spec *spec;
2545 int err;
2546
2547 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002548 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002549 if (spec == NULL)
2550 return -ENOMEM;
2551
Takashi Iwai620e2b22011-06-17 17:19:19 +02002552 spec->aa_mix_nid = 0x17;
2553
Takashi Iwai12daef62011-06-18 17:45:49 +02002554 /* Add HP and CD pin config connect bit re-config action */
2555 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2556 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2557
Joseph Chanc577b8a2006-11-29 15:29:40 +01002558 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002559 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002560 if (err < 0) {
2561 via_free(codec);
2562 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002563 }
2564
Takashi Iwai12daef62011-06-18 17:45:49 +02002565 /* add jack detect on/off control */
2566 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2567 return -ENOMEM;
2568
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002569 /* disable 32bit format on VT1708 */
2570 if (codec->vendor_id == 0x11061708)
2571 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002572
Lydia Wange322a362011-06-29 13:52:02 +08002573 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2574
Joseph Chanc577b8a2006-11-29 15:29:40 +01002575 codec->patch_ops = via_patch_ops;
2576
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002577 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002578 return 0;
2579}
2580
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002581static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002582{
2583 struct via_spec *spec;
2584 int err;
2585
2586 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002587 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002588 if (spec == NULL)
2589 return -ENOMEM;
2590
Takashi Iwai620e2b22011-06-17 17:19:19 +02002591 spec->aa_mix_nid = 0x18;
2592
Takashi Iwai12daef62011-06-18 17:45:49 +02002593 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002594 if (err < 0) {
2595 via_free(codec);
2596 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002597 }
2598
Joseph Chanc577b8a2006-11-29 15:29:40 +01002599 codec->patch_ops = via_patch_ops;
2600
Josepch Chanf7278fd2007-12-13 16:40:40 +01002601 return 0;
2602}
2603
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002604static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2605{
2606 struct via_spec *spec = codec->spec;
2607 int imux_is_smixer;
2608 unsigned int parm;
2609 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002610 if ((spec->codec_type != VT1708B_4CH) &&
2611 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002612 is_8ch = 1;
2613
2614 /* SW0 (17h) = stereo mixer */
2615 imux_is_smixer =
2616 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2617 == ((spec->codec_type == VT1708S) ? 5 : 0));
2618 /* inputs */
2619 /* PW 1/2/5 (1ah/1bh/1eh) */
2620 parm = AC_PWRST_D3;
2621 set_pin_power_state(codec, 0x1a, &parm);
2622 set_pin_power_state(codec, 0x1b, &parm);
2623 set_pin_power_state(codec, 0x1e, &parm);
2624 if (imux_is_smixer)
2625 parm = AC_PWRST_D0;
2626 /* SW0 (17h), AIW 0/1 (13h/14h) */
2627 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2628 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2629 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2630
2631 /* outputs */
2632 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2633 parm = AC_PWRST_D3;
2634 set_pin_power_state(codec, 0x19, &parm);
2635 if (spec->smart51_enabled)
2636 set_pin_power_state(codec, 0x1b, &parm);
2637 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2638 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2639
2640 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2641 if (is_8ch) {
2642 parm = AC_PWRST_D3;
2643 set_pin_power_state(codec, 0x22, &parm);
2644 if (spec->smart51_enabled)
2645 set_pin_power_state(codec, 0x1a, &parm);
2646 snd_hda_codec_write(codec, 0x26, 0,
2647 AC_VERB_SET_POWER_STATE, parm);
2648 snd_hda_codec_write(codec, 0x24, 0,
2649 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002650 } else if (codec->vendor_id == 0x11064397) {
2651 /* PW7(23h), SW2(27h), AOW2(25h) */
2652 parm = AC_PWRST_D3;
2653 set_pin_power_state(codec, 0x23, &parm);
2654 if (spec->smart51_enabled)
2655 set_pin_power_state(codec, 0x1a, &parm);
2656 snd_hda_codec_write(codec, 0x27, 0,
2657 AC_VERB_SET_POWER_STATE, parm);
2658 snd_hda_codec_write(codec, 0x25, 0,
2659 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002660 }
2661
2662 /* PW 3/4/7 (1ch/1dh/23h) */
2663 parm = AC_PWRST_D3;
2664 /* force to D0 for internal Speaker */
2665 set_pin_power_state(codec, 0x1c, &parm);
2666 set_pin_power_state(codec, 0x1d, &parm);
2667 if (is_8ch)
2668 set_pin_power_state(codec, 0x23, &parm);
2669
2670 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2671 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2672 imux_is_smixer ? AC_PWRST_D0 : parm);
2673 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2674 if (is_8ch) {
2675 snd_hda_codec_write(codec, 0x25, 0,
2676 AC_VERB_SET_POWER_STATE, parm);
2677 snd_hda_codec_write(codec, 0x27, 0,
2678 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002679 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2680 snd_hda_codec_write(codec, 0x25, 0,
2681 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002682}
2683
Lydia Wang518bf3b2009-10-10 19:07:29 +08002684static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002685static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002686{
2687 struct via_spec *spec;
2688 int err;
2689
Lydia Wang518bf3b2009-10-10 19:07:29 +08002690 if (get_codec_type(codec) == VT1708BCE)
2691 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002692
Josepch Chanf7278fd2007-12-13 16:40:40 +01002693 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002694 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002695 if (spec == NULL)
2696 return -ENOMEM;
2697
Takashi Iwai620e2b22011-06-17 17:19:19 +02002698 spec->aa_mix_nid = 0x16;
2699
Josepch Chanf7278fd2007-12-13 16:40:40 +01002700 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002701 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002702 if (err < 0) {
2703 via_free(codec);
2704 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002705 }
2706
Josepch Chanf7278fd2007-12-13 16:40:40 +01002707 codec->patch_ops = via_patch_ops;
2708
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002709 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2710
Josepch Chanf7278fd2007-12-13 16:40:40 +01002711 return 0;
2712}
2713
Harald Welted949cac2008-09-09 15:56:01 +08002714/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002715static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002716 /* Enable Mic Boost Volume backdoor */
2717 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002718 /* don't bybass mixer */
2719 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002720 { }
2721};
2722
Takashi Iwai9da29272009-05-07 16:31:14 +02002723/* fill out digital output widgets; one for master and one for slave outputs */
2724static void fill_dig_outs(struct hda_codec *codec)
2725{
2726 struct via_spec *spec = codec->spec;
2727 int i;
2728
2729 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2730 hda_nid_t nid;
2731 int conn;
2732
2733 nid = spec->autocfg.dig_out_pins[i];
2734 if (!nid)
2735 continue;
2736 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2737 if (conn < 1)
2738 continue;
2739 if (!spec->multiout.dig_out_nid)
2740 spec->multiout.dig_out_nid = nid;
2741 else {
2742 spec->slave_dig_outs[0] = nid;
2743 break; /* at most two dig outs */
2744 }
2745 }
2746}
2747
Takashi Iwai12daef62011-06-18 17:45:49 +02002748static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002749{
2750 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002751 hda_nid_t dig_nid;
2752 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002753
Takashi Iwai12daef62011-06-18 17:45:49 +02002754 if (!spec->autocfg.dig_in_pin)
2755 return;
Harald Welted949cac2008-09-09 15:56:01 +08002756
Takashi Iwai12daef62011-06-18 17:45:49 +02002757 dig_nid = codec->start_nid;
2758 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2759 unsigned int wcaps = get_wcaps(codec, dig_nid);
2760 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2761 continue;
2762 if (!(wcaps & AC_WCAP_DIGITAL))
2763 continue;
2764 if (!(wcaps & AC_WCAP_CONN_LIST))
2765 continue;
2766 err = get_connection_index(codec, dig_nid,
2767 spec->autocfg.dig_in_pin);
2768 if (err >= 0) {
2769 spec->dig_in_nid = dig_nid;
2770 break;
2771 }
2772 }
Harald Welted949cac2008-09-09 15:56:01 +08002773}
2774
Lydia Wang6369bcf2009-10-10 19:08:31 +08002775static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2776 int offset, int num_steps, int step_size)
2777{
2778 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2779 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2780 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2781 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2782 (0 << AC_AMPCAP_MUTE_SHIFT));
2783}
2784
Harald Welted949cac2008-09-09 15:56:01 +08002785static int patch_vt1708S(struct hda_codec *codec)
2786{
2787 struct via_spec *spec;
2788 int err;
2789
2790 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002791 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002792 if (spec == NULL)
2793 return -ENOMEM;
2794
Takashi Iwai620e2b22011-06-17 17:19:19 +02002795 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002796 override_mic_boost(codec, 0x1a, 0, 3, 40);
2797 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002798
Harald Welted949cac2008-09-09 15:56:01 +08002799 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002800 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002801 if (err < 0) {
2802 via_free(codec);
2803 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002804 }
2805
Takashi Iwai096a8852011-06-20 12:09:02 +02002806 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002807
Harald Welted949cac2008-09-09 15:56:01 +08002808 codec->patch_ops = via_patch_ops;
2809
Lydia Wang518bf3b2009-10-10 19:07:29 +08002810 /* correct names for VT1708BCE */
2811 if (get_codec_type(codec) == VT1708BCE) {
2812 kfree(codec->chip_name);
2813 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2814 snprintf(codec->bus->card->mixername,
2815 sizeof(codec->bus->card->mixername),
2816 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002817 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002818 /* correct names for VT1705 */
2819 if (codec->vendor_id == 0x11064397) {
2820 kfree(codec->chip_name);
2821 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2822 snprintf(codec->bus->card->mixername,
2823 sizeof(codec->bus->card->mixername),
2824 "%s %s", codec->vendor_name, codec->chip_name);
2825 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002826 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002827 return 0;
2828}
2829
2830/* Patch for VT1702 */
2831
Takashi Iwai096a8852011-06-20 12:09:02 +02002832static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002833 /* mixer enable */
2834 {0x1, 0xF88, 0x3},
2835 /* GPIO 0~2 */
2836 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002837 { }
2838};
2839
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002840static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2841{
2842 int imux_is_smixer =
2843 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2844 unsigned int parm;
2845 /* inputs */
2846 /* PW 1/2/5 (14h/15h/18h) */
2847 parm = AC_PWRST_D3;
2848 set_pin_power_state(codec, 0x14, &parm);
2849 set_pin_power_state(codec, 0x15, &parm);
2850 set_pin_power_state(codec, 0x18, &parm);
2851 if (imux_is_smixer)
2852 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2853 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2854 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2855 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2856 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2857 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2858
2859 /* outputs */
2860 /* PW 3/4 (16h/17h) */
2861 parm = AC_PWRST_D3;
2862 set_pin_power_state(codec, 0x17, &parm);
2863 set_pin_power_state(codec, 0x16, &parm);
2864 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2865 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2866 imux_is_smixer ? AC_PWRST_D0 : parm);
2867 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2868 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2869}
2870
Harald Welted949cac2008-09-09 15:56:01 +08002871static int patch_vt1702(struct hda_codec *codec)
2872{
2873 struct via_spec *spec;
2874 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002875
2876 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002877 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002878 if (spec == NULL)
2879 return -ENOMEM;
2880
Takashi Iwai620e2b22011-06-17 17:19:19 +02002881 spec->aa_mix_nid = 0x1a;
2882
Takashi Iwai12daef62011-06-18 17:45:49 +02002883 /* limit AA path volume to 0 dB */
2884 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2885 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2886 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2887 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2888 (1 << AC_AMPCAP_MUTE_SHIFT));
2889
Harald Welted949cac2008-09-09 15:56:01 +08002890 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002891 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002892 if (err < 0) {
2893 via_free(codec);
2894 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002895 }
2896
Takashi Iwai096a8852011-06-20 12:09:02 +02002897 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002898
Harald Welted949cac2008-09-09 15:56:01 +08002899 codec->patch_ops = via_patch_ops;
2900
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002901 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002902 return 0;
2903}
2904
Lydia Wangeb7188c2009-10-10 19:08:34 +08002905/* Patch for VT1718S */
2906
Takashi Iwai096a8852011-06-20 12:09:02 +02002907static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002908 /* Enable MW0 adjust Gain 5 */
2909 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002910 /* Enable Boost Volume backdoor */
2911 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002912
Lydia Wangeb7188c2009-10-10 19:08:34 +08002913 { }
2914};
2915
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002916static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2917{
2918 struct via_spec *spec = codec->spec;
2919 int imux_is_smixer;
2920 unsigned int parm;
2921 /* MUX6 (1eh) = stereo mixer */
2922 imux_is_smixer =
2923 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2924 /* inputs */
2925 /* PW 5/6/7 (29h/2ah/2bh) */
2926 parm = AC_PWRST_D3;
2927 set_pin_power_state(codec, 0x29, &parm);
2928 set_pin_power_state(codec, 0x2a, &parm);
2929 set_pin_power_state(codec, 0x2b, &parm);
2930 if (imux_is_smixer)
2931 parm = AC_PWRST_D0;
2932 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2933 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2934 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2935 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2936 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2937
2938 /* outputs */
2939 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2940 parm = AC_PWRST_D3;
2941 set_pin_power_state(codec, 0x27, &parm);
2942 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2943 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2944
2945 /* PW2 (26h), AOW2 (ah) */
2946 parm = AC_PWRST_D3;
2947 set_pin_power_state(codec, 0x26, &parm);
2948 if (spec->smart51_enabled)
2949 set_pin_power_state(codec, 0x2b, &parm);
2950 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2951
2952 /* PW0 (24h), AOW0 (8h) */
2953 parm = AC_PWRST_D3;
2954 set_pin_power_state(codec, 0x24, &parm);
2955 if (!spec->hp_independent_mode) /* check for redirected HP */
2956 set_pin_power_state(codec, 0x28, &parm);
2957 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2958 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2959 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2960 imux_is_smixer ? AC_PWRST_D0 : parm);
2961
2962 /* PW1 (25h), AOW1 (9h) */
2963 parm = AC_PWRST_D3;
2964 set_pin_power_state(codec, 0x25, &parm);
2965 if (spec->smart51_enabled)
2966 set_pin_power_state(codec, 0x2a, &parm);
2967 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2968
2969 if (spec->hp_independent_mode) {
2970 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2971 parm = AC_PWRST_D3;
2972 set_pin_power_state(codec, 0x28, &parm);
2973 snd_hda_codec_write(codec, 0x1b, 0,
2974 AC_VERB_SET_POWER_STATE, parm);
2975 snd_hda_codec_write(codec, 0x34, 0,
2976 AC_VERB_SET_POWER_STATE, parm);
2977 snd_hda_codec_write(codec, 0xc, 0,
2978 AC_VERB_SET_POWER_STATE, parm);
2979 }
2980}
2981
Lydia Wangeb7188c2009-10-10 19:08:34 +08002982static int patch_vt1718S(struct hda_codec *codec)
2983{
2984 struct via_spec *spec;
2985 int err;
2986
2987 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002988 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002989 if (spec == NULL)
2990 return -ENOMEM;
2991
Takashi Iwai620e2b22011-06-17 17:19:19 +02002992 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002993 override_mic_boost(codec, 0x2b, 0, 3, 40);
2994 override_mic_boost(codec, 0x29, 0, 3, 40);
Lydia Wangc4394f52011-07-04 16:54:15 +08002995 spec->dac_mixer_idx = 5;
Takashi Iwai620e2b22011-06-17 17:19:19 +02002996
Lydia Wangeb7188c2009-10-10 19:08:34 +08002997 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002998 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002999 if (err < 0) {
3000 via_free(codec);
3001 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003002 }
3003
Takashi Iwai096a8852011-06-20 12:09:02 +02003004 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003005
Lydia Wangeb7188c2009-10-10 19:08:34 +08003006 codec->patch_ops = via_patch_ops;
3007
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003008 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3009
Lydia Wangeb7188c2009-10-10 19:08:34 +08003010 return 0;
3011}
Lydia Wangf3db4232009-10-10 19:08:41 +08003012
3013/* Patch for VT1716S */
3014
3015static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3016 struct snd_ctl_elem_info *uinfo)
3017{
3018 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3019 uinfo->count = 1;
3020 uinfo->value.integer.min = 0;
3021 uinfo->value.integer.max = 1;
3022 return 0;
3023}
3024
3025static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3026 struct snd_ctl_elem_value *ucontrol)
3027{
3028 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3029 int index = 0;
3030
3031 index = snd_hda_codec_read(codec, 0x26, 0,
3032 AC_VERB_GET_CONNECT_SEL, 0);
3033 if (index != -1)
3034 *ucontrol->value.integer.value = index;
3035
3036 return 0;
3037}
3038
3039static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3040 struct snd_ctl_elem_value *ucontrol)
3041{
3042 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3043 struct via_spec *spec = codec->spec;
3044 int index = *ucontrol->value.integer.value;
3045
3046 snd_hda_codec_write(codec, 0x26, 0,
3047 AC_VERB_SET_CONNECT_SEL, index);
3048 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003049 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003050 return 1;
3051}
3052
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003053static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003054 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3055 {
3056 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3057 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003058 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003059 .count = 1,
3060 .info = vt1716s_dmic_info,
3061 .get = vt1716s_dmic_get,
3062 .put = vt1716s_dmic_put,
3063 },
3064 {} /* end */
3065};
3066
3067
3068/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003069static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003070 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3071 { } /* end */
3072};
3073
Takashi Iwai096a8852011-06-20 12:09:02 +02003074static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003075 /* Enable Boost Volume backdoor */
3076 {0x1, 0xf8a, 0x80},
3077 /* don't bybass mixer */
3078 {0x1, 0xf88, 0xc0},
3079 /* Enable mono output */
3080 {0x1, 0xf90, 0x08},
3081 { }
3082};
3083
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003084static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3085{
3086 struct via_spec *spec = codec->spec;
3087 int imux_is_smixer;
3088 unsigned int parm;
3089 unsigned int mono_out, present;
3090 /* SW0 (17h) = stereo mixer */
3091 imux_is_smixer =
3092 (snd_hda_codec_read(codec, 0x17, 0,
3093 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3094 /* inputs */
3095 /* PW 1/2/5 (1ah/1bh/1eh) */
3096 parm = AC_PWRST_D3;
3097 set_pin_power_state(codec, 0x1a, &parm);
3098 set_pin_power_state(codec, 0x1b, &parm);
3099 set_pin_power_state(codec, 0x1e, &parm);
3100 if (imux_is_smixer)
3101 parm = AC_PWRST_D0;
3102 /* SW0 (17h), AIW0(13h) */
3103 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3104 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3105
3106 parm = AC_PWRST_D3;
3107 set_pin_power_state(codec, 0x1e, &parm);
3108 /* PW11 (22h) */
3109 if (spec->dmic_enabled)
3110 set_pin_power_state(codec, 0x22, &parm);
3111 else
3112 snd_hda_codec_write(codec, 0x22, 0,
3113 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3114
3115 /* SW2(26h), AIW1(14h) */
3116 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3117 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3118
3119 /* outputs */
3120 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3121 parm = AC_PWRST_D3;
3122 set_pin_power_state(codec, 0x19, &parm);
3123 /* Smart 5.1 PW2(1bh) */
3124 if (spec->smart51_enabled)
3125 set_pin_power_state(codec, 0x1b, &parm);
3126 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3127 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3128
3129 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3130 parm = AC_PWRST_D3;
3131 set_pin_power_state(codec, 0x23, &parm);
3132 /* Smart 5.1 PW1(1ah) */
3133 if (spec->smart51_enabled)
3134 set_pin_power_state(codec, 0x1a, &parm);
3135 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3136
3137 /* Smart 5.1 PW5(1eh) */
3138 if (spec->smart51_enabled)
3139 set_pin_power_state(codec, 0x1e, &parm);
3140 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3141
3142 /* Mono out */
3143 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3144 present = snd_hda_jack_detect(codec, 0x1c);
3145
3146 if (present)
3147 mono_out = 0;
3148 else {
3149 present = snd_hda_jack_detect(codec, 0x1d);
3150 if (!spec->hp_independent_mode && present)
3151 mono_out = 0;
3152 else
3153 mono_out = 1;
3154 }
3155 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3156 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3157 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3158 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3159
3160 /* PW 3/4 (1ch/1dh) */
3161 parm = AC_PWRST_D3;
3162 set_pin_power_state(codec, 0x1c, &parm);
3163 set_pin_power_state(codec, 0x1d, &parm);
3164 /* HP Independent Mode, power on AOW3 */
3165 if (spec->hp_independent_mode)
3166 snd_hda_codec_write(codec, 0x25, 0,
3167 AC_VERB_SET_POWER_STATE, parm);
3168
3169 /* force to D0 for internal Speaker */
3170 /* MW0 (16h), AOW0 (10h) */
3171 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3172 imux_is_smixer ? AC_PWRST_D0 : parm);
3173 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3174 mono_out ? AC_PWRST_D0 : parm);
3175}
3176
Lydia Wangf3db4232009-10-10 19:08:41 +08003177static int patch_vt1716S(struct hda_codec *codec)
3178{
3179 struct via_spec *spec;
3180 int err;
3181
3182 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003183 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003184 if (spec == NULL)
3185 return -ENOMEM;
3186
Takashi Iwai620e2b22011-06-17 17:19:19 +02003187 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003188 override_mic_boost(codec, 0x1a, 0, 3, 40);
3189 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003190
Lydia Wangf3db4232009-10-10 19:08:41 +08003191 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003192 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003193 if (err < 0) {
3194 via_free(codec);
3195 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003196 }
3197
Takashi Iwai096a8852011-06-20 12:09:02 +02003198 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003199
Lydia Wangf3db4232009-10-10 19:08:41 +08003200 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3201 spec->num_mixers++;
3202
3203 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3204
3205 codec->patch_ops = via_patch_ops;
3206
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003207 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003208 return 0;
3209}
Lydia Wang25eaba22009-10-10 19:08:43 +08003210
3211/* for vt2002P */
3212
Takashi Iwai096a8852011-06-20 12:09:02 +02003213static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003214 /* Class-D speaker related verbs */
3215 {0x1, 0xfe0, 0x4},
3216 {0x1, 0xfe9, 0x80},
3217 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003218 /* Enable Boost Volume backdoor */
3219 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003220 /* Enable AOW0 to MW9 */
3221 {0x1, 0xfb8, 0x88},
3222 { }
3223};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003224
Takashi Iwai096a8852011-06-20 12:09:02 +02003225static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003226 /* Enable Boost Volume backdoor */
3227 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003228 /* Enable AOW0 to MW9 */
3229 {0x1, 0xfb8, 0x88},
3230 { }
3231};
Lydia Wang25eaba22009-10-10 19:08:43 +08003232
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003233static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3234{
3235 struct via_spec *spec = codec->spec;
3236 int imux_is_smixer;
3237 unsigned int parm;
3238 unsigned int present;
3239 /* MUX9 (1eh) = stereo mixer */
3240 imux_is_smixer =
3241 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3242 /* inputs */
3243 /* PW 5/6/7 (29h/2ah/2bh) */
3244 parm = AC_PWRST_D3;
3245 set_pin_power_state(codec, 0x29, &parm);
3246 set_pin_power_state(codec, 0x2a, &parm);
3247 set_pin_power_state(codec, 0x2b, &parm);
3248 parm = AC_PWRST_D0;
3249 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3250 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3251 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3252 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3253 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3254
3255 /* outputs */
3256 /* AOW0 (8h)*/
3257 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3258
Lydia Wang118909562011-03-23 17:57:34 +08003259 if (spec->codec_type == VT1802) {
3260 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3261 parm = AC_PWRST_D3;
3262 set_pin_power_state(codec, 0x28, &parm);
3263 snd_hda_codec_write(codec, 0x18, 0,
3264 AC_VERB_SET_POWER_STATE, parm);
3265 snd_hda_codec_write(codec, 0x38, 0,
3266 AC_VERB_SET_POWER_STATE, parm);
3267 } else {
3268 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3269 parm = AC_PWRST_D3;
3270 set_pin_power_state(codec, 0x26, &parm);
3271 snd_hda_codec_write(codec, 0x1c, 0,
3272 AC_VERB_SET_POWER_STATE, parm);
3273 snd_hda_codec_write(codec, 0x37, 0,
3274 AC_VERB_SET_POWER_STATE, parm);
3275 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003276
Lydia Wang118909562011-03-23 17:57:34 +08003277 if (spec->codec_type == VT1802) {
3278 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3279 parm = AC_PWRST_D3;
3280 set_pin_power_state(codec, 0x25, &parm);
3281 snd_hda_codec_write(codec, 0x15, 0,
3282 AC_VERB_SET_POWER_STATE, parm);
3283 snd_hda_codec_write(codec, 0x35, 0,
3284 AC_VERB_SET_POWER_STATE, parm);
3285 } else {
3286 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3287 parm = AC_PWRST_D3;
3288 set_pin_power_state(codec, 0x25, &parm);
3289 snd_hda_codec_write(codec, 0x19, 0,
3290 AC_VERB_SET_POWER_STATE, parm);
3291 snd_hda_codec_write(codec, 0x35, 0,
3292 AC_VERB_SET_POWER_STATE, parm);
3293 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003294
3295 if (spec->hp_independent_mode)
3296 snd_hda_codec_write(codec, 0x9, 0,
3297 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3298
3299 /* Class-D */
3300 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3301 present = snd_hda_jack_detect(codec, 0x25);
3302
3303 parm = AC_PWRST_D3;
3304 set_pin_power_state(codec, 0x24, &parm);
3305 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003306 if (spec->codec_type == VT1802)
3307 snd_hda_codec_write(codec, 0x14, 0,
3308 AC_VERB_SET_POWER_STATE, parm);
3309 else
3310 snd_hda_codec_write(codec, 0x18, 0,
3311 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003312 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3313
3314 /* Mono Out */
3315 present = snd_hda_jack_detect(codec, 0x26);
3316
3317 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003318 if (spec->codec_type == VT1802) {
3319 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3320 snd_hda_codec_write(codec, 0x33, 0,
3321 AC_VERB_SET_POWER_STATE, parm);
3322 snd_hda_codec_write(codec, 0x1c, 0,
3323 AC_VERB_SET_POWER_STATE, parm);
3324 snd_hda_codec_write(codec, 0x3c, 0,
3325 AC_VERB_SET_POWER_STATE, parm);
3326 } else {
3327 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3328 snd_hda_codec_write(codec, 0x31, 0,
3329 AC_VERB_SET_POWER_STATE, parm);
3330 snd_hda_codec_write(codec, 0x17, 0,
3331 AC_VERB_SET_POWER_STATE, parm);
3332 snd_hda_codec_write(codec, 0x3b, 0,
3333 AC_VERB_SET_POWER_STATE, parm);
3334 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003335 /* MW9 (21h) */
3336 if (imux_is_smixer || !is_aa_path_mute(codec))
3337 snd_hda_codec_write(codec, 0x21, 0,
3338 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3339 else
3340 snd_hda_codec_write(codec, 0x21, 0,
3341 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3342}
Lydia Wang25eaba22009-10-10 19:08:43 +08003343
3344/* patch for vt2002P */
3345static int patch_vt2002P(struct hda_codec *codec)
3346{
3347 struct via_spec *spec;
3348 int err;
3349
3350 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003351 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003352 if (spec == NULL)
3353 return -ENOMEM;
3354
Takashi Iwai620e2b22011-06-17 17:19:19 +02003355 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003356 override_mic_boost(codec, 0x2b, 0, 3, 40);
3357 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003358
Lydia Wang25eaba22009-10-10 19:08:43 +08003359 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003360 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003361 if (err < 0) {
3362 via_free(codec);
3363 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003364 }
3365
Lydia Wang118909562011-03-23 17:57:34 +08003366 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003367 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003368 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003369 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003370
Lydia Wang25eaba22009-10-10 19:08:43 +08003371 codec->patch_ops = via_patch_ops;
3372
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003373 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003374 return 0;
3375}
Lydia Wangab6734e2009-10-10 19:08:46 +08003376
3377/* for vt1812 */
3378
Takashi Iwai096a8852011-06-20 12:09:02 +02003379static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003380 /* Enable Boost Volume backdoor */
3381 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003382 /* Enable AOW0 to MW9 */
3383 {0x1, 0xfb8, 0xa8},
3384 { }
3385};
3386
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003387static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3388{
3389 struct via_spec *spec = codec->spec;
3390 int imux_is_smixer =
3391 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3392 unsigned int parm;
3393 unsigned int present;
3394 /* MUX10 (1eh) = stereo mixer */
3395 imux_is_smixer =
3396 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3397 /* inputs */
3398 /* PW 5/6/7 (29h/2ah/2bh) */
3399 parm = AC_PWRST_D3;
3400 set_pin_power_state(codec, 0x29, &parm);
3401 set_pin_power_state(codec, 0x2a, &parm);
3402 set_pin_power_state(codec, 0x2b, &parm);
3403 parm = AC_PWRST_D0;
3404 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3405 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3406 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3407 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3408 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3409
3410 /* outputs */
3411 /* AOW0 (8h)*/
3412 snd_hda_codec_write(codec, 0x8, 0,
3413 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3414
3415 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3416 parm = AC_PWRST_D3;
3417 set_pin_power_state(codec, 0x28, &parm);
3418 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3419 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3420
3421 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3422 parm = AC_PWRST_D3;
3423 set_pin_power_state(codec, 0x25, &parm);
3424 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3425 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3426 if (spec->hp_independent_mode)
3427 snd_hda_codec_write(codec, 0x9, 0,
3428 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3429
3430 /* Internal Speaker */
3431 /* PW0 (24h), MW0(14h), MUX0(34h) */
3432 present = snd_hda_jack_detect(codec, 0x25);
3433
3434 parm = AC_PWRST_D3;
3435 set_pin_power_state(codec, 0x24, &parm);
3436 if (present) {
3437 snd_hda_codec_write(codec, 0x14, 0,
3438 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3439 snd_hda_codec_write(codec, 0x34, 0,
3440 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3441 } else {
3442 snd_hda_codec_write(codec, 0x14, 0,
3443 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3444 snd_hda_codec_write(codec, 0x34, 0,
3445 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3446 }
3447
3448
3449 /* Mono Out */
3450 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3451 present = snd_hda_jack_detect(codec, 0x28);
3452
3453 parm = AC_PWRST_D3;
3454 set_pin_power_state(codec, 0x31, &parm);
3455 if (present) {
3456 snd_hda_codec_write(codec, 0x1c, 0,
3457 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3458 snd_hda_codec_write(codec, 0x3c, 0,
3459 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3460 snd_hda_codec_write(codec, 0x3e, 0,
3461 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3462 } else {
3463 snd_hda_codec_write(codec, 0x1c, 0,
3464 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3465 snd_hda_codec_write(codec, 0x3c, 0,
3466 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3467 snd_hda_codec_write(codec, 0x3e, 0,
3468 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3469 }
3470
3471 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3472 parm = AC_PWRST_D3;
3473 set_pin_power_state(codec, 0x33, &parm);
3474 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3475 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3476
3477}
Lydia Wangab6734e2009-10-10 19:08:46 +08003478
3479/* patch for vt1812 */
3480static int patch_vt1812(struct hda_codec *codec)
3481{
3482 struct via_spec *spec;
3483 int err;
3484
3485 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003486 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003487 if (spec == NULL)
3488 return -ENOMEM;
3489
Takashi Iwai620e2b22011-06-17 17:19:19 +02003490 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003491 override_mic_boost(codec, 0x2b, 0, 3, 40);
3492 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003493
Lydia Wangab6734e2009-10-10 19:08:46 +08003494 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003495 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003496 if (err < 0) {
3497 via_free(codec);
3498 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003499 }
3500
Takashi Iwai096a8852011-06-20 12:09:02 +02003501 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003502
Lydia Wangab6734e2009-10-10 19:08:46 +08003503 codec->patch_ops = via_patch_ops;
3504
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003505 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003506 return 0;
3507}
3508
Joseph Chanc577b8a2006-11-29 15:29:40 +01003509/*
3510 * patch entries
3511 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003512static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003513 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3514 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3515 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3516 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3517 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003518 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003519 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003520 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003521 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003522 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003523 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003524 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003525 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003526 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003527 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003528 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003529 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003530 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003531 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003532 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003533 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003534 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003535 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003536 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003537 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003538 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003539 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003540 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003541 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003542 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003543 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003544 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003545 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003546 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003547 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003548 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003549 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003550 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003551 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003552 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003553 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003554 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003555 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003556 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003557 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003558 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003559 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003560 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003561 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003562 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003563 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003564 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003565 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003566 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003567 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003568 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003569 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003570 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003571 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003572 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003573 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003574 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003575 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003576 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003577 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003578 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003579 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003580 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003581 { .id = 0x11060428, .name = "VT1718S",
3582 .patch = patch_vt1718S},
3583 { .id = 0x11064428, .name = "VT1718S",
3584 .patch = patch_vt1718S},
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08003585 { .id = 0x11060441, .name = "VT2020",
3586 .patch = patch_vt1718S},
3587 { .id = 0x11064441, .name = "VT1828S",
3588 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003589 { .id = 0x11060433, .name = "VT1716S",
3590 .patch = patch_vt1716S},
3591 { .id = 0x1106a721, .name = "VT1716S",
3592 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003593 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3594 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003595 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003596 { .id = 0x11060440, .name = "VT1818S",
3597 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003598 { .id = 0x11060446, .name = "VT1802",
3599 .patch = patch_vt2002P},
3600 { .id = 0x11068446, .name = "VT1802",
3601 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003602 {} /* terminator */
3603};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003604
3605MODULE_ALIAS("snd-hda-codec-id:1106*");
3606
3607static struct hda_codec_preset_list via_list = {
3608 .preset = snd_hda_preset_via,
3609 .owner = THIS_MODULE,
3610};
3611
3612MODULE_LICENSE("GPL");
3613MODULE_DESCRIPTION("VIA HD-audio codec");
3614
3615static int __init patch_via_init(void)
3616{
3617 return snd_hda_add_codec_preset(&via_list);
3618}
3619
3620static void __exit patch_via_exit(void)
3621{
3622 snd_hda_delete_codec_preset(&via_list);
3623}
3624
3625module_init(patch_via_init)
3626module_exit(patch_via_exit)