blob: 7aa7dcc206e2b7ba65dfe1b7b055302a02b6c547 [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>
Paul Gortmakerda155d52011-07-15 12:38:28 -040052#include <linux/module.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010053#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080054#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010055#include "hda_codec.h"
56#include "hda_local.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010057
Joseph Chanc577b8a2006-11-29 15:29:40 +010058/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080059#define VT1708_HP_PIN_NID 0x20
60#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010061
Harald Welted7426322008-09-15 22:43:23 +080062enum VIA_HDA_CODEC {
63 UNKNOWN = -1,
64 VT1708,
65 VT1709_10CH,
66 VT1709_6CH,
67 VT1708B_8CH,
68 VT1708B_4CH,
69 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080070 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080071 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080072 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080073 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080074 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080075 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080076 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080077 CODEC_TYPES,
78};
79
Lydia Wang118909562011-03-23 17:57:34 +080080#define VT2002P_COMPATIBLE(spec) \
81 ((spec)->codec_type == VT2002P ||\
82 (spec)->codec_type == VT1812 ||\
83 (spec)->codec_type == VT1802)
84
Takashi Iwai8e3679d2011-06-21 09:01:36 +020085#define MAX_NID_PATH_DEPTH 5
86
Takashi Iwai09a9ad692011-06-21 15:57:44 +020087/* output-path: DAC -> ... -> pin
88 * idx[] contains the source index number of the next widget;
89 * e.g. idx[0] is the index of the DAC selected by path[1] widget
90 * multi[] indicates whether it's a selector widget with multi-connectors
91 * (i.e. the connection selection is mandatory)
92 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
93 */
Takashi Iwai4a796162011-06-17 17:53:38 +020094struct nid_path {
95 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020096 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad692011-06-21 15:57:44 +020097 unsigned char idx[MAX_NID_PATH_DEPTH];
98 unsigned char multi[MAX_NID_PATH_DEPTH];
99 unsigned int vol_ctl;
100 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200101};
102
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200103/* input-path */
104struct via_input {
105 hda_nid_t pin; /* input-pin or aa-mix */
106 int adc_idx; /* ADC index to be used */
107 int mux_idx; /* MUX index (if any) */
108 const char *label; /* input-source label */
109};
110
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200111#define VIA_MAX_ADCS 3
112
Takashi Iwai3b607e32011-07-18 16:54:40 +0200113enum {
114 STREAM_MULTI_OUT = (1 << 0),
115 STREAM_INDEP_HP = (1 << 1),
116};
117
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800118struct via_spec {
119 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200120 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800121 unsigned int num_mixers;
122
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200123 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800124 unsigned int num_iverbs;
125
Takashi Iwai82673bc2011-06-17 16:24:21 +0200126 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200127 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200128 const struct hda_pcm_stream *stream_analog_playback;
129 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800130
Takashi Iwai82673bc2011-06-17 16:24:21 +0200131 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200132 const struct hda_pcm_stream *stream_digital_playback;
133 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800134
135 /* playback */
136 struct hda_multi_out multiout;
137 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200138 hda_nid_t hp_dac_nid;
Takashi Iwai3214b962011-07-18 12:49:25 +0200139 hda_nid_t speaker_dac_nid;
140 int hp_indep_shared; /* indep HP-DAC is shared with side ch */
Takashi Iwai3b607e32011-07-18 16:54:40 +0200141 int opened_streams; /* STREAM_* bits */
142 int active_streams; /* STREAM_* bits */
Takashi Iwai3214b962011-07-18 12:49:25 +0200143 int aamix_mode; /* loopback is enabled for output-path? */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800144
Takashi Iwai3214b962011-07-18 12:49:25 +0200145 /* Output-paths:
146 * There are different output-paths depending on the setup.
147 * out_path, hp_path and speaker_path are primary paths. If both
148 * direct DAC and aa-loopback routes are available, these contain
149 * the former paths. Meanwhile *_mix_path contain the paths with
150 * loopback mixer. (Since the loopback is only for front channel,
151 * no out_mix_path for surround channels.)
152 * The HP output has another path, hp_indep_path, which is used in
153 * the independent-HP mode.
154 */
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200155 struct nid_path out_path[HDA_SIDE + 1];
Takashi Iwai3214b962011-07-18 12:49:25 +0200156 struct nid_path out_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200157 struct nid_path hp_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200158 struct nid_path hp_mix_path;
159 struct nid_path hp_indep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200160 struct nid_path speaker_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200161 struct nid_path speaker_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200162
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800163 /* capture */
164 unsigned int num_adc_nids;
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200165 hda_nid_t adc_nids[VIA_MAX_ADCS];
166 hda_nid_t mux_nids[VIA_MAX_ADCS];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200167 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800168 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800169
170 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200171 bool dyn_adc_switch;
172 int num_inputs;
173 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200174 unsigned int cur_mux[VIA_MAX_ADCS];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800175
Takashi Iwai3b607e32011-07-18 16:54:40 +0200176 /* dynamic DAC switching */
177 unsigned int cur_dac_stream_tag;
178 unsigned int cur_dac_format;
179 unsigned int cur_hp_stream_tag;
180 unsigned int cur_hp_format;
181
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200182 /* dynamic ADC switching */
183 hda_nid_t cur_adc;
184 unsigned int cur_adc_stream_tag;
185 unsigned int cur_adc_format;
186
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800187 /* PCM information */
188 struct hda_pcm pcm_rec[3];
189
190 /* dynamic controls, init_verbs and input_mux */
191 struct auto_pin_cfg autocfg;
192 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800193 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
194
195 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800196 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800197 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200198 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800199 enum VIA_HDA_CODEC codec_type;
200
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200201 /* smart51 setup */
202 unsigned int smart51_nums;
203 hda_nid_t smart51_pins[2];
204 int smart51_idxs[2];
205 const char *smart51_labels[2];
206 unsigned int smart51_enabled;
207
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800208 /* work to check hp jack state */
209 struct hda_codec *codec;
210 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200211 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800212 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800213
214 void (*set_widgets_power_state)(struct hda_codec *codec);
215
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800216 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200217 int num_loopbacks;
218 struct hda_amp_list loopback_list[8];
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200219
220 /* bind capture-volume */
221 struct hda_bind_ctls *bind_cap_vol;
222 struct hda_bind_ctls *bind_cap_sw;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200223
224 struct mutex config_mutex;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800225};
226
Lydia Wang0341ccd2011-03-22 16:25:03 +0800227static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100228static struct via_spec * via_new_spec(struct hda_codec *codec)
229{
230 struct via_spec *spec;
231
232 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
233 if (spec == NULL)
234 return NULL;
235
Takashi Iwai3b607e32011-07-18 16:54:40 +0200236 mutex_init(&spec->config_mutex);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100237 codec->spec = spec;
238 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800239 spec->codec_type = get_codec_type(codec);
240 /* VT1708BCE & VT1708S are almost same */
241 if (spec->codec_type == VT1708BCE)
242 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100243 return spec;
244}
245
Lydia Wang744ff5f2009-10-10 19:07:26 +0800246static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800247{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800248 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800249 u16 ven_id = vendor_id >> 16;
250 u16 dev_id = vendor_id & 0xffff;
251 enum VIA_HDA_CODEC codec_type;
252
253 /* get codec type */
254 if (ven_id != 0x1106)
255 codec_type = UNKNOWN;
256 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
257 codec_type = VT1708;
258 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
259 codec_type = VT1709_10CH;
260 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
261 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800262 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800263 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800264 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
265 codec_type = VT1708BCE;
266 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800267 codec_type = VT1708B_4CH;
268 else if ((dev_id & 0xfff) == 0x397
269 && (dev_id >> 12) < 8)
270 codec_type = VT1708S;
271 else if ((dev_id & 0xfff) == 0x398
272 && (dev_id >> 12) < 8)
273 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800274 else if ((dev_id & 0xfff) == 0x428
275 && (dev_id >> 12) < 8)
276 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800277 else if (dev_id == 0x0433 || dev_id == 0xa721)
278 codec_type = VT1716S;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +0800279 else if (dev_id == 0x0441 || dev_id == 0x4441)
280 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800281 else if (dev_id == 0x0438 || dev_id == 0x4438)
282 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800283 else if (dev_id == 0x0448)
284 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800285 else if (dev_id == 0x0440)
286 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800287 else if ((dev_id & 0xfff) == 0x446)
288 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800289 else
290 codec_type = UNKNOWN;
291 return codec_type;
292};
293
Lydia Wangec7e7e42011-03-24 12:43:44 +0800294#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800295#define VIA_HP_EVENT 0x01
296#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200297#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800298
Joseph Chanc577b8a2006-11-29 15:29:40 +0100299enum {
300 VIA_CTL_WIDGET_VOL,
301 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800302 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100303};
304
Takashi Iwaiada509e2011-06-20 15:40:19 +0200305static void analog_low_current_mode(struct hda_codec *codec);
306static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800307
308static void vt1708_start_hp_work(struct via_spec *spec)
309{
310 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
311 return;
312 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200313 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800314 if (!delayed_work_pending(&spec->vt1708_hp_work))
315 schedule_delayed_work(&spec->vt1708_hp_work,
316 msecs_to_jiffies(100));
317}
318
319static void vt1708_stop_hp_work(struct via_spec *spec)
320{
321 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
322 return;
323 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
324 && !is_aa_path_mute(spec->codec))
325 return;
326 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200327 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100328 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800329}
Lydia Wangf5271102009-10-10 19:07:35 +0800330
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800331static void set_widgets_power_state(struct hda_codec *codec)
332{
333 struct via_spec *spec = codec->spec;
334 if (spec->set_widgets_power_state)
335 spec->set_widgets_power_state(codec);
336}
Lydia Wang25eaba22009-10-10 19:08:43 +0800337
Lydia Wangf5271102009-10-10 19:07:35 +0800338static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
339 struct snd_ctl_elem_value *ucontrol)
340{
341 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
342 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
343
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800344 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200345 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800346 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
347 if (is_aa_path_mute(codec))
348 vt1708_start_hp_work(codec->spec);
349 else
350 vt1708_stop_hp_work(codec->spec);
351 }
Lydia Wangf5271102009-10-10 19:07:35 +0800352 return change;
353}
354
355/* modify .put = snd_hda_mixer_amp_switch_put */
356#define ANALOG_INPUT_MUTE \
357 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
358 .name = NULL, \
359 .index = 0, \
360 .info = snd_hda_mixer_amp_switch_info, \
361 .get = snd_hda_mixer_amp_switch_get, \
362 .put = analog_input_switch_put, \
363 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
364
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200365static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100366 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
367 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800368 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100369};
370
Lydia Wangab6734e2009-10-10 19:08:46 +0800371
Joseph Chanc577b8a2006-11-29 15:29:40 +0100372/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200373static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
374 const struct snd_kcontrol_new *tmpl,
375 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100376{
377 struct snd_kcontrol_new *knew;
378
Takashi Iwai603c4012008-07-30 15:01:44 +0200379 snd_array_init(&spec->kctls, sizeof(*knew), 32);
380 knew = snd_array_new(&spec->kctls);
381 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200382 return NULL;
383 *knew = *tmpl;
384 if (!name)
385 name = tmpl->name;
386 if (name) {
387 knew->name = kstrdup(name, GFP_KERNEL);
388 if (!knew->name)
389 return NULL;
390 }
391 return knew;
392}
393
394static int __via_add_control(struct via_spec *spec, int type, const char *name,
395 int idx, unsigned long val)
396{
397 struct snd_kcontrol_new *knew;
398
399 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
400 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100401 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200402 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100403 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100404 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100405 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100406 return 0;
407}
408
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200409#define via_add_control(spec, type, name, val) \
410 __via_add_control(spec, type, name, 0, val)
411
Takashi Iwai291c9e32011-06-17 16:15:26 +0200412#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100413
Takashi Iwai603c4012008-07-30 15:01:44 +0200414static void via_free_kctls(struct hda_codec *codec)
415{
416 struct via_spec *spec = codec->spec;
417
418 if (spec->kctls.list) {
419 struct snd_kcontrol_new *kctl = spec->kctls.list;
420 int i;
421 for (i = 0; i < spec->kctls.used; i++)
422 kfree(kctl[i].name);
423 }
424 snd_array_free(&spec->kctls);
425}
426
Joseph Chanc577b8a2006-11-29 15:29:40 +0100427/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800428static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200429 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100430{
431 char name[32];
432 int err;
433
434 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200435 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100436 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
437 if (err < 0)
438 return err;
439 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200440 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100441 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
442 if (err < 0)
443 return err;
444 return 0;
445}
446
Takashi Iwai5d417622011-06-20 11:32:27 +0200447#define get_connection_index(codec, mux, nid) \
Takashi Iwai8d087c72011-06-28 12:45:47 +0200448 snd_hda_get_conn_index(codec, mux, nid, 0)
Takashi Iwai5d417622011-06-20 11:32:27 +0200449
Takashi Iwai8df2a312011-06-21 11:48:29 +0200450static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
451 unsigned int mask)
452{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200453 unsigned int caps;
454 if (!nid)
455 return false;
456 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200457 if (dir == HDA_INPUT)
458 caps &= AC_WCAP_IN_AMP;
459 else
460 caps &= AC_WCAP_OUT_AMP;
461 if (!caps)
462 return false;
463 if (query_amp_caps(codec, nid, dir) & mask)
464 return true;
465 return false;
466}
467
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200468#define have_mute(codec, nid, dir) \
469 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200470
Lydia Wangd69607b2011-07-08 14:02:52 +0800471/* enable/disable the output-route mixers */
472static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
Takashi Iwai3214b962011-07-18 12:49:25 +0200473 hda_nid_t mix_nid, int idx, bool enable)
Lydia Wangd69607b2011-07-08 14:02:52 +0800474{
475 int i, num, val;
Lydia Wangd69607b2011-07-08 14:02:52 +0800476
477 if (!path)
478 return;
479 num = snd_hda_get_conn_list(codec, mix_nid, NULL);
Lydia Wangd69607b2011-07-08 14:02:52 +0800480 for (i = 0; i < num; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +0200481 if (i == idx)
482 val = AMP_IN_UNMUTE(i);
483 else
484 val = AMP_IN_MUTE(i);
Lydia Wangd69607b2011-07-08 14:02:52 +0800485 snd_hda_codec_write(codec, mix_nid, 0,
486 AC_VERB_SET_AMP_GAIN_MUTE, val);
487 }
488}
489
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200490/* enable/disable the output-route */
491static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
492 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200493{
Lydia Wangd69607b2011-07-08 14:02:52 +0800494 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200495 int i;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200496 for (i = 0; i < path->depth; i++) {
497 hda_nid_t src, dst;
498 int idx = path->idx[i];
499 src = path->path[i];
500 if (i < path->depth - 1)
501 dst = path->path[i + 1];
502 else
503 dst = 0;
504 if (enable && path->multi[i])
505 snd_hda_codec_write(codec, dst, 0,
506 AC_VERB_SET_CONNECT_SEL, idx);
Takashi Iwai3214b962011-07-18 12:49:25 +0200507 if (!force && (dst == spec->aa_mix_nid))
Lydia Wange5e14682011-07-01 10:55:07 +0800508 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +0200509 if (have_mute(codec, dst, HDA_INPUT))
510 activate_output_mix(codec, path, dst, idx, enable);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200511 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
512 continue;
513 if (have_mute(codec, src, HDA_OUTPUT)) {
514 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
515 snd_hda_codec_write(codec, src, 0,
516 AC_VERB_SET_AMP_GAIN_MUTE, val);
517 }
518 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200519}
520
521/* set the given pin as output */
522static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
523 int pin_type)
524{
525 if (!pin)
526 return;
527 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
528 pin_type);
529 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
530 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200531 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100532}
533
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200534static void via_auto_init_output(struct hda_codec *codec,
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200535 struct nid_path *path, int pin_type)
Takashi Iwai5d417622011-06-20 11:32:27 +0200536{
Takashi Iwai5d417622011-06-20 11:32:27 +0200537 unsigned int caps;
Lydia Wangd69607b2011-07-08 14:02:52 +0800538 hda_nid_t pin;
Takashi Iwai5d417622011-06-20 11:32:27 +0200539
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200540 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200541 return;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200542 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200543
544 init_output_pin(codec, pin, pin_type);
545 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
546 if (caps & AC_AMPCAP_MUTE) {
547 unsigned int val;
548 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
549 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
550 AMP_OUT_MUTE | val);
551 }
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200552 activate_output_path(codec, path, true, true); /* force on */
Takashi Iwai5d417622011-06-20 11:32:27 +0200553}
554
Joseph Chanc577b8a2006-11-29 15:29:40 +0100555static void via_auto_init_multi_out(struct hda_codec *codec)
556{
557 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200558 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100559 int i;
560
Takashi Iwai3214b962011-07-18 12:49:25 +0200561 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) {
562 path = &spec->out_path[i];
563 if (!i && spec->aamix_mode && spec->out_mix_path.depth)
564 path = &spec->out_mix_path;
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200565 via_auto_init_output(codec, path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200566 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100567}
568
Takashi Iwai020066d2011-07-21 13:45:56 +0200569/* deactivate the inactive headphone-paths */
570static void deactivate_hp_paths(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100571{
572 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200573 int shared = spec->hp_indep_shared;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100574
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200575 if (spec->hp_independent_mode) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200576 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200577 activate_output_path(codec, &spec->hp_mix_path, false, false);
578 if (shared)
579 activate_output_path(codec, &spec->out_path[shared],
580 false, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200581 } else if (spec->aamix_mode || !spec->hp_path.depth) {
582 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200583 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200584 } else {
Takashi Iwai020066d2011-07-21 13:45:56 +0200585 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200586 activate_output_path(codec, &spec->hp_mix_path, false, false);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200587 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100588}
589
Takashi Iwai020066d2011-07-21 13:45:56 +0200590static void via_auto_init_hp_out(struct hda_codec *codec)
591{
592 struct via_spec *spec = codec->spec;
593
594 if (!spec->hp_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200595 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200596 return;
597 }
598 deactivate_hp_paths(codec);
599 if (spec->hp_independent_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200600 via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200601 else if (spec->aamix_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200602 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200603 else
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200604 via_auto_init_output(codec, &spec->hp_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200605}
606
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200607static void via_auto_init_speaker_out(struct hda_codec *codec)
608{
609 struct via_spec *spec = codec->spec;
610
Takashi Iwai3214b962011-07-18 12:49:25 +0200611 if (!spec->autocfg.speaker_outs)
612 return;
613 if (!spec->speaker_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200614 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200615 return;
616 }
617 if (!spec->aamix_mode) {
618 activate_output_path(codec, &spec->speaker_mix_path,
619 false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200620 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200621 } else {
622 activate_output_path(codec, &spec->speaker_path, false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200623 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200624 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200625}
626
Takashi Iwaif4a78282011-06-17 18:46:48 +0200627static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200628static void via_hp_automute(struct hda_codec *codec);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200629
Joseph Chanc577b8a2006-11-29 15:29:40 +0100630static void via_auto_init_analog_input(struct hda_codec *codec)
631{
632 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200633 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200634 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200635 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200636 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100637
Takashi Iwai096a8852011-06-20 12:09:02 +0200638 /* init ADCs */
639 for (i = 0; i < spec->num_adc_nids; i++) {
640 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
641 AC_VERB_SET_AMP_GAIN_MUTE,
642 AMP_IN_UNMUTE(0));
643 }
644
645 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200646 for (i = 0; i < cfg->num_inputs; i++) {
647 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200648 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200649 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100650 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200651 ctl = PIN_VREF50;
652 else
653 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100654 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200655 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100656 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200657
658 /* init input-src */
659 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200660 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
661 if (spec->mux_nids[adc_idx]) {
662 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
663 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
664 AC_VERB_SET_CONNECT_SEL,
665 mux_idx);
666 }
667 if (spec->dyn_adc_switch)
668 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200669 }
670
671 /* init aa-mixer */
672 if (!spec->aa_mix_nid)
673 return;
674 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
675 ARRAY_SIZE(conn));
676 for (i = 0; i < num_conns; i++) {
677 unsigned int caps = get_wcaps(codec, conn[i]);
678 if (get_wcaps_type(caps) == AC_WID_PIN)
679 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
680 AC_VERB_SET_AMP_GAIN_MUTE,
681 AMP_IN_MUTE(i));
682 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100683}
Lydia Wangf5271102009-10-10 19:07:35 +0800684
685static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
686 unsigned int *affected_parm)
687{
688 unsigned parm;
689 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
690 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
691 >> AC_DEFCFG_MISC_SHIFT
692 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800693 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200694 unsigned present = 0;
695
696 no_presence |= spec->no_pin_power_ctl;
697 if (!no_presence)
698 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200699 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800700 || ((no_presence || present)
701 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800702 *affected_parm = AC_PWRST_D0; /* if it's connected */
703 parm = AC_PWRST_D0;
704 } else
705 parm = AC_PWRST_D3;
706
707 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
708}
709
Takashi Iwai24088a52011-06-17 16:59:21 +0200710static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
711 struct snd_ctl_elem_info *uinfo)
712{
713 static const char * const texts[] = {
714 "Disabled", "Enabled"
715 };
716
717 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
718 uinfo->count = 1;
719 uinfo->value.enumerated.items = 2;
720 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
721 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
722 strcpy(uinfo->value.enumerated.name,
723 texts[uinfo->value.enumerated.item]);
724 return 0;
725}
726
727static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
728 struct snd_ctl_elem_value *ucontrol)
729{
730 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
731 struct via_spec *spec = codec->spec;
732 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
733 return 0;
734}
735
736static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
737 struct snd_ctl_elem_value *ucontrol)
738{
739 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
740 struct via_spec *spec = codec->spec;
741 unsigned int val = !ucontrol->value.enumerated.item[0];
742
743 if (val == spec->no_pin_power_ctl)
744 return 0;
745 spec->no_pin_power_ctl = val;
746 set_widgets_power_state(codec);
747 return 1;
748}
749
750static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
751 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
752 .name = "Dynamic Power-Control",
753 .info = via_pin_power_ctl_info,
754 .get = via_pin_power_ctl_get,
755 .put = via_pin_power_ctl_put,
756};
757
758
Harald Welte0aa62ae2008-09-09 15:58:27 +0800759static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
760 struct snd_ctl_elem_info *uinfo)
761{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200762 static const char * const texts[] = { "OFF", "ON" };
763
764 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
765 uinfo->count = 1;
766 uinfo->value.enumerated.items = 2;
767 if (uinfo->value.enumerated.item >= 2)
768 uinfo->value.enumerated.item = 1;
769 strcpy(uinfo->value.enumerated.name,
770 texts[uinfo->value.enumerated.item]);
771 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800772}
773
774static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
775 struct snd_ctl_elem_value *ucontrol)
776{
777 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800778 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800779
Takashi Iwaiece8d042011-06-19 16:24:21 +0200780 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800781 return 0;
782}
783
Takashi Iwai3b607e32011-07-18 16:54:40 +0200784/* adjust spec->multiout setup according to the current flags */
785static void setup_playback_multi_pcm(struct via_spec *spec)
786{
787 const struct auto_pin_cfg *cfg = &spec->autocfg;
788 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
789 spec->multiout.hp_nid = 0;
790 if (!spec->hp_independent_mode) {
791 if (!spec->hp_indep_shared)
792 spec->multiout.hp_nid = spec->hp_dac_nid;
793 } else {
794 if (spec->hp_indep_shared)
795 spec->multiout.num_dacs = cfg->line_outs - 1;
796 }
797}
798
799/* update DAC setups according to indep-HP switch;
800 * this function is called only when indep-HP is modified
801 */
802static void switch_indep_hp_dacs(struct hda_codec *codec)
803{
804 struct via_spec *spec = codec->spec;
805 int shared = spec->hp_indep_shared;
806 hda_nid_t shared_dac, hp_dac;
807
808 if (!spec->opened_streams)
809 return;
810
811 shared_dac = shared ? spec->multiout.dac_nids[shared] : 0;
812 hp_dac = spec->hp_dac_nid;
813 if (spec->hp_independent_mode) {
814 /* switch to indep-HP mode */
815 if (spec->active_streams & STREAM_MULTI_OUT) {
816 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
817 __snd_hda_codec_cleanup_stream(codec, shared_dac, 1);
818 }
819 if (spec->active_streams & STREAM_INDEP_HP)
820 snd_hda_codec_setup_stream(codec, hp_dac,
821 spec->cur_hp_stream_tag, 0,
822 spec->cur_hp_format);
823 } else {
824 /* back to HP or shared-DAC */
825 if (spec->active_streams & STREAM_INDEP_HP)
826 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
827 if (spec->active_streams & STREAM_MULTI_OUT) {
828 hda_nid_t dac;
829 int ch;
830 if (shared_dac) { /* reset mutli-ch DAC */
831 dac = shared_dac;
832 ch = shared * 2;
833 } else { /* reset HP DAC */
834 dac = hp_dac;
835 ch = 0;
836 }
837 snd_hda_codec_setup_stream(codec, dac,
838 spec->cur_dac_stream_tag, ch,
839 spec->cur_dac_format);
840 }
841 }
842 setup_playback_multi_pcm(spec);
843}
844
Harald Welte0aa62ae2008-09-09 15:58:27 +0800845static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
846 struct snd_ctl_elem_value *ucontrol)
847{
848 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
849 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200850 int cur, shared;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200851
Takashi Iwai3b607e32011-07-18 16:54:40 +0200852 mutex_lock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200853 cur = !!ucontrol->value.enumerated.item[0];
Takashi Iwai3b607e32011-07-18 16:54:40 +0200854 if (spec->hp_independent_mode == cur) {
855 mutex_unlock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200856 return 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200857 }
Takashi Iwai25250502011-06-30 17:24:47 +0200858 spec->hp_independent_mode = cur;
Takashi Iwai3214b962011-07-18 12:49:25 +0200859 shared = spec->hp_indep_shared;
Takashi Iwai020066d2011-07-21 13:45:56 +0200860 deactivate_hp_paths(codec);
861 if (cur)
862 activate_output_path(codec, &spec->hp_indep_path, true, false);
863 else {
Takashi Iwai3214b962011-07-18 12:49:25 +0200864 if (shared)
865 activate_output_path(codec, &spec->out_path[shared],
Takashi Iwai25250502011-06-30 17:24:47 +0200866 true, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200867 if (spec->aamix_mode || !spec->hp_path.depth)
868 activate_output_path(codec, &spec->hp_mix_path,
869 true, false);
870 else
871 activate_output_path(codec, &spec->hp_path,
872 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200873 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800874
Takashi Iwai3b607e32011-07-18 16:54:40 +0200875 switch_indep_hp_dacs(codec);
876 mutex_unlock(&spec->config_mutex);
877
Lydia Wangce0e5a92011-03-22 16:22:37 +0800878 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800879 set_widgets_power_state(codec);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200880 via_hp_automute(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200881 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800882}
883
Takashi Iwaiece8d042011-06-19 16:24:21 +0200884static const struct snd_kcontrol_new via_hp_mixer = {
885 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
886 .name = "Independent HP",
887 .info = via_independent_hp_info,
888 .get = via_independent_hp_get,
889 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800890};
891
Takashi Iwai3d83e572010-04-14 14:36:23 +0200892static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100893{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200894 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100895 struct snd_kcontrol_new *knew;
896 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100897
Takashi Iwaiece8d042011-06-19 16:24:21 +0200898 nid = spec->autocfg.hp_pins[0];
899 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200900 if (knew == NULL)
901 return -ENOMEM;
902
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100903 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100904
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100905 return 0;
906}
907
Lydia Wang1564b282009-10-10 19:07:52 +0800908static void notify_aa_path_ctls(struct hda_codec *codec)
909{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200910 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800911 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800912
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200913 for (i = 0; i < spec->smart51_nums; i++) {
914 struct snd_kcontrol *ctl;
915 struct snd_ctl_elem_id id;
916 memset(&id, 0, sizeof(id));
917 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
918 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800919 ctl = snd_hda_find_mixer_ctl(codec, id.name);
920 if (ctl)
921 snd_ctl_notify(codec->bus->card,
922 SNDRV_CTL_EVENT_MASK_VALUE,
923 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800924 }
925}
926
927static void mute_aa_path(struct hda_codec *codec, int mute)
928{
929 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200930 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800931 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200932
Lydia Wang1564b282009-10-10 19:07:52 +0800933 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200934 for (i = 0; i < spec->smart51_nums; i++) {
935 if (spec->smart51_idxs[i] < 0)
936 continue;
937 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
938 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800939 HDA_AMP_MUTE, val);
940 }
941}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200942
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200943static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
944{
945 struct via_spec *spec = codec->spec;
946 int i;
947
948 for (i = 0; i < spec->smart51_nums; i++)
949 if (spec->smart51_pins[i] == pin)
950 return true;
951 return false;
952}
953
Lydia Wang1564b282009-10-10 19:07:52 +0800954static int via_smart51_get(struct snd_kcontrol *kcontrol,
955 struct snd_ctl_elem_value *ucontrol)
956{
957 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
958 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800959
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200960 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800961 return 0;
962}
963
964static int via_smart51_put(struct snd_kcontrol *kcontrol,
965 struct snd_ctl_elem_value *ucontrol)
966{
967 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
968 struct via_spec *spec = codec->spec;
969 int out_in = *ucontrol->value.integer.value
970 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800971 int i;
972
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200973 for (i = 0; i < spec->smart51_nums; i++) {
974 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200975 unsigned int parm;
976
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200977 parm = snd_hda_codec_read(codec, nid, 0,
978 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
979 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
980 parm |= out_in;
981 snd_hda_codec_write(codec, nid, 0,
982 AC_VERB_SET_PIN_WIDGET_CONTROL,
983 parm);
984 if (out_in == AC_PINCTL_OUT_EN) {
985 mute_aa_path(codec, 1);
986 notify_aa_path_ctls(codec);
987 }
Lydia Wang1564b282009-10-10 19:07:52 +0800988 }
989 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800990 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800991 return 1;
992}
993
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200994static const struct snd_kcontrol_new via_smart51_mixer = {
995 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
996 .name = "Smart 5.1",
997 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200998 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200999 .get = via_smart51_get,
1000 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +08001001};
1002
Takashi Iwaif4a78282011-06-17 18:46:48 +02001003static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001004{
Takashi Iwaif4a78282011-06-17 18:46:48 +02001005 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001006
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001007 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +08001008 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001009 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001010 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001011 return 0;
1012}
1013
Takashi Iwaiada509e2011-06-20 15:40:19 +02001014/* check AA path's mute status */
1015static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001016{
Lydia Wangf5271102009-10-10 19:07:35 +08001017 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001018 const struct hda_amp_list *p;
1019 int i, ch, v;
1020
1021 for (i = 0; i < spec->num_loopbacks; i++) {
1022 p = &spec->loopback_list[i];
1023 for (ch = 0; ch < 2; ch++) {
1024 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
1025 p->idx);
1026 if (!(v & HDA_AMP_MUTE) && v > 0)
1027 return false;
Lydia Wangf5271102009-10-10 19:07:35 +08001028 }
1029 }
Takashi Iwaiada509e2011-06-20 15:40:19 +02001030 return true;
Lydia Wangf5271102009-10-10 19:07:35 +08001031}
1032
1033/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +02001034static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001035{
1036 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001037 bool enable;
1038 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +08001039
Takashi Iwai3b607e32011-07-18 16:54:40 +02001040 enable = is_aa_path_mute(codec) && (spec->opened_streams != 0);
Lydia Wangf5271102009-10-10 19:07:35 +08001041
1042 /* decide low current mode's verb & parameter */
1043 switch (spec->codec_type) {
1044 case VT1708B_8CH:
1045 case VT1708B_4CH:
1046 verb = 0xf70;
1047 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1048 break;
1049 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001050 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001051 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001052 verb = 0xf73;
1053 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1054 break;
1055 case VT1702:
1056 verb = 0xf73;
1057 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1058 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001059 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001060 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +08001061 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +08001062 verb = 0xf93;
1063 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1064 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001065 default:
1066 return; /* other codecs are not supported */
1067 }
1068 /* send verb */
1069 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1070}
1071
Joseph Chanc577b8a2006-11-29 15:29:40 +01001072/*
1073 * generic initialization of ADC, input mixers and output mixers
1074 */
Takashi Iwai096a8852011-06-20 12:09:02 +02001075static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001076 /* power down jack detect function */
1077 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001078 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001079};
1080
Takashi Iwai3b607e32011-07-18 16:54:40 +02001081static void set_stream_open(struct hda_codec *codec, int bit, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001082{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001083 struct via_spec *spec = codec->spec;
1084
1085 if (active)
Takashi Iwai3b607e32011-07-18 16:54:40 +02001086 spec->opened_streams |= bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001087 else
Takashi Iwai3b607e32011-07-18 16:54:40 +02001088 spec->opened_streams &= ~bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001089 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001090}
1091
Takashi Iwaiece8d042011-06-19 16:24:21 +02001092static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001093 struct hda_codec *codec,
1094 struct snd_pcm_substream *substream)
1095{
1096 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001097 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001098 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001099
Takashi Iwai25250502011-06-30 17:24:47 +02001100 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
Takashi Iwai25250502011-06-30 17:24:47 +02001101 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001102 set_stream_open(codec, STREAM_MULTI_OUT, true);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001103 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1104 hinfo);
1105 if (err < 0) {
Takashi Iwai3b607e32011-07-18 16:54:40 +02001106 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001107 return err;
1108 }
1109 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001110}
1111
Takashi Iwaiece8d042011-06-19 16:24:21 +02001112static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001113 struct hda_codec *codec,
1114 struct snd_pcm_substream *substream)
1115{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001116 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001117 return 0;
1118}
1119
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001120static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1121 struct hda_codec *codec,
1122 struct snd_pcm_substream *substream)
1123{
1124 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001125
Takashi Iwaiece8d042011-06-19 16:24:21 +02001126 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001127 return -EINVAL;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001128 set_stream_open(codec, STREAM_INDEP_HP, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001129 return 0;
1130}
1131
1132static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1133 struct hda_codec *codec,
1134 struct snd_pcm_substream *substream)
1135{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001136 set_stream_open(codec, STREAM_INDEP_HP, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001137 return 0;
1138}
1139
1140static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1141 struct hda_codec *codec,
1142 unsigned int stream_tag,
1143 unsigned int format,
1144 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001145{
1146 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001147
Takashi Iwai3b607e32011-07-18 16:54:40 +02001148 mutex_lock(&spec->config_mutex);
1149 setup_playback_multi_pcm(spec);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001150 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1151 format, substream);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001152 /* remember for dynamic DAC switch with indep-HP */
1153 spec->active_streams |= STREAM_MULTI_OUT;
1154 spec->cur_dac_stream_tag = stream_tag;
1155 spec->cur_dac_format = format;
1156 mutex_unlock(&spec->config_mutex);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001157 vt1708_start_hp_work(spec);
1158 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001159}
1160
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001161static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1162 struct hda_codec *codec,
1163 unsigned int stream_tag,
1164 unsigned int format,
1165 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001166{
1167 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001168
Takashi Iwai3b607e32011-07-18 16:54:40 +02001169 mutex_lock(&spec->config_mutex);
1170 if (spec->hp_independent_mode)
1171 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1172 stream_tag, 0, format);
1173 spec->active_streams |= STREAM_INDEP_HP;
1174 spec->cur_hp_stream_tag = stream_tag;
1175 spec->cur_hp_format = format;
1176 mutex_unlock(&spec->config_mutex);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001177 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001178 return 0;
1179}
1180
1181static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1182 struct hda_codec *codec,
1183 struct snd_pcm_substream *substream)
1184{
1185 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001186
Takashi Iwai3b607e32011-07-18 16:54:40 +02001187 mutex_lock(&spec->config_mutex);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001188 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001189 spec->active_streams &= ~STREAM_MULTI_OUT;
1190 mutex_unlock(&spec->config_mutex);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001191 vt1708_stop_hp_work(spec);
1192 return 0;
1193}
1194
1195static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1196 struct hda_codec *codec,
1197 struct snd_pcm_substream *substream)
1198{
1199 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001200
Takashi Iwai3b607e32011-07-18 16:54:40 +02001201 mutex_lock(&spec->config_mutex);
1202 if (spec->hp_independent_mode)
1203 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
1204 spec->active_streams &= ~STREAM_INDEP_HP;
1205 mutex_unlock(&spec->config_mutex);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001206 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001207 return 0;
1208}
1209
Joseph Chanc577b8a2006-11-29 15:29:40 +01001210/*
1211 * Digital out
1212 */
1213static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1214 struct hda_codec *codec,
1215 struct snd_pcm_substream *substream)
1216{
1217 struct via_spec *spec = codec->spec;
1218 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1219}
1220
1221static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1222 struct hda_codec *codec,
1223 struct snd_pcm_substream *substream)
1224{
1225 struct via_spec *spec = codec->spec;
1226 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1227}
1228
Harald Welte5691ec72008-09-15 22:42:26 +08001229static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001230 struct hda_codec *codec,
1231 unsigned int stream_tag,
1232 unsigned int format,
1233 struct snd_pcm_substream *substream)
1234{
1235 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001236 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1237 stream_tag, format, substream);
1238}
Harald Welte5691ec72008-09-15 22:42:26 +08001239
Takashi Iwai9da29272009-05-07 16:31:14 +02001240static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1241 struct hda_codec *codec,
1242 struct snd_pcm_substream *substream)
1243{
1244 struct via_spec *spec = codec->spec;
1245 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001246 return 0;
1247}
1248
Joseph Chanc577b8a2006-11-29 15:29:40 +01001249/*
1250 * Analog capture
1251 */
1252static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1253 struct hda_codec *codec,
1254 unsigned int stream_tag,
1255 unsigned int format,
1256 struct snd_pcm_substream *substream)
1257{
1258 struct via_spec *spec = codec->spec;
1259
1260 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1261 stream_tag, 0, format);
1262 return 0;
1263}
1264
1265static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1266 struct hda_codec *codec,
1267 struct snd_pcm_substream *substream)
1268{
1269 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001270 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001271 return 0;
1272}
1273
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001274/* analog capture with dynamic ADC switching */
1275static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1276 struct hda_codec *codec,
1277 unsigned int stream_tag,
1278 unsigned int format,
1279 struct snd_pcm_substream *substream)
1280{
1281 struct via_spec *spec = codec->spec;
1282 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1283
Takashi Iwai3b607e32011-07-18 16:54:40 +02001284 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001285 spec->cur_adc = spec->adc_nids[adc_idx];
1286 spec->cur_adc_stream_tag = stream_tag;
1287 spec->cur_adc_format = format;
1288 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001289 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001290 return 0;
1291}
1292
1293static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1294 struct hda_codec *codec,
1295 struct snd_pcm_substream *substream)
1296{
1297 struct via_spec *spec = codec->spec;
1298
Takashi Iwai3b607e32011-07-18 16:54:40 +02001299 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001300 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1301 spec->cur_adc = 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001302 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001303 return 0;
1304}
1305
1306/* re-setup the stream if running; called from input-src put */
1307static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1308{
1309 struct via_spec *spec = codec->spec;
1310 int adc_idx = spec->inputs[cur].adc_idx;
1311 hda_nid_t adc = spec->adc_nids[adc_idx];
Takashi Iwai3b607e32011-07-18 16:54:40 +02001312 bool ret = false;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001313
Takashi Iwai3b607e32011-07-18 16:54:40 +02001314 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001315 if (spec->cur_adc && spec->cur_adc != adc) {
1316 /* stream is running, let's swap the current ADC */
1317 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1318 spec->cur_adc = adc;
1319 snd_hda_codec_setup_stream(codec, adc,
1320 spec->cur_adc_stream_tag, 0,
1321 spec->cur_adc_format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001322 ret = true;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001323 }
Takashi Iwai3b607e32011-07-18 16:54:40 +02001324 mutex_unlock(&spec->config_mutex);
1325 return ret;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001326}
1327
Takashi Iwai9af74212011-06-18 16:17:45 +02001328static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001329 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001330 .channels_min = 2,
1331 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001332 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001333 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001334 .open = via_playback_multi_pcm_open,
1335 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001336 .prepare = via_playback_multi_pcm_prepare,
1337 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001338 },
1339};
1340
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001341static const struct hda_pcm_stream via_pcm_hp_playback = {
1342 .substreams = 1,
1343 .channels_min = 2,
1344 .channels_max = 2,
1345 /* NID is set in via_build_pcms */
1346 .ops = {
1347 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001348 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001349 .prepare = via_playback_hp_pcm_prepare,
1350 .cleanup = via_playback_hp_pcm_cleanup
1351 },
1352};
1353
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001354static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001355 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001356 .channels_min = 2,
1357 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001358 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001359 /* We got noisy outputs on the right channel on VT1708 when
1360 * 24bit samples are used. Until any workaround is found,
1361 * disable the 24bit format, so far.
1362 */
1363 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1364 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001365 .open = via_playback_multi_pcm_open,
1366 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001367 .prepare = via_playback_multi_pcm_prepare,
1368 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001369 },
1370};
1371
Takashi Iwai9af74212011-06-18 16:17:45 +02001372static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001373 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001374 .channels_min = 2,
1375 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001376 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001377 .ops = {
1378 .prepare = via_capture_pcm_prepare,
1379 .cleanup = via_capture_pcm_cleanup
1380 },
1381};
1382
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001383static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1384 .substreams = 1,
1385 .channels_min = 2,
1386 .channels_max = 2,
1387 /* NID is set in via_build_pcms */
1388 .ops = {
1389 .prepare = via_dyn_adc_capture_pcm_prepare,
1390 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1391 },
1392};
1393
Takashi Iwai9af74212011-06-18 16:17:45 +02001394static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001395 .substreams = 1,
1396 .channels_min = 2,
1397 .channels_max = 2,
1398 /* NID is set in via_build_pcms */
1399 .ops = {
1400 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001401 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001402 .prepare = via_dig_playback_pcm_prepare,
1403 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001404 },
1405};
1406
Takashi Iwai9af74212011-06-18 16:17:45 +02001407static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001408 .substreams = 1,
1409 .channels_min = 2,
1410 .channels_max = 2,
1411};
1412
Takashi Iwai370bafb2011-06-20 12:47:45 +02001413/*
1414 * slave controls for virtual master
1415 */
1416static const char * const via_slave_vols[] = {
1417 "Front Playback Volume",
1418 "Surround Playback Volume",
1419 "Center Playback Volume",
1420 "LFE Playback Volume",
1421 "Side Playback Volume",
1422 "Headphone Playback Volume",
1423 "Speaker Playback Volume",
1424 NULL,
1425};
1426
1427static const char * const via_slave_sws[] = {
1428 "Front Playback Switch",
1429 "Surround Playback Switch",
1430 "Center Playback Switch",
1431 "LFE Playback Switch",
1432 "Side Playback Switch",
1433 "Headphone Playback Switch",
1434 "Speaker Playback Switch",
1435 NULL,
1436};
1437
Joseph Chanc577b8a2006-11-29 15:29:40 +01001438static int via_build_controls(struct hda_codec *codec)
1439{
1440 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001441 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001442 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001443
Takashi Iwai24088a52011-06-17 16:59:21 +02001444 if (spec->set_widgets_power_state)
1445 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1446 return -ENOMEM;
1447
Joseph Chanc577b8a2006-11-29 15:29:40 +01001448 for (i = 0; i < spec->num_mixers; i++) {
1449 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1450 if (err < 0)
1451 return err;
1452 }
1453
1454 if (spec->multiout.dig_out_nid) {
1455 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001456 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001457 spec->multiout.dig_out_nid);
1458 if (err < 0)
1459 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001460 err = snd_hda_create_spdif_share_sw(codec,
1461 &spec->multiout);
1462 if (err < 0)
1463 return err;
1464 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001465 }
1466 if (spec->dig_in_nid) {
1467 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1468 if (err < 0)
1469 return err;
1470 }
Lydia Wang17314372009-10-10 19:07:37 +08001471
Takashi Iwai370bafb2011-06-20 12:47:45 +02001472 /* if we have no master control, let's create it */
1473 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1474 unsigned int vmaster_tlv[4];
1475 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1476 HDA_OUTPUT, vmaster_tlv);
1477 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1478 vmaster_tlv, via_slave_vols);
1479 if (err < 0)
1480 return err;
1481 }
1482 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1483 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1484 NULL, via_slave_sws);
1485 if (err < 0)
1486 return err;
1487 }
1488
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001489 /* assign Capture Source enums to NID */
1490 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1491 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001492 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001493 if (err < 0)
1494 return err;
1495 }
1496
Lydia Wang17314372009-10-10 19:07:37 +08001497 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001498 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001499 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001500
Takashi Iwai603c4012008-07-30 15:01:44 +02001501 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001502 return 0;
1503}
1504
1505static int via_build_pcms(struct hda_codec *codec)
1506{
1507 struct via_spec *spec = codec->spec;
1508 struct hda_pcm *info = spec->pcm_rec;
1509
Takashi Iwaia5973102011-09-28 16:43:36 +02001510 codec->num_pcms = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001511 codec->pcm_info = info;
1512
Takashi Iwaia5973102011-09-28 16:43:36 +02001513 if (spec->multiout.num_dacs || spec->num_adc_nids) {
1514 snprintf(spec->stream_name_analog,
1515 sizeof(spec->stream_name_analog),
1516 "%s Analog", codec->chip_name);
1517 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001518
Takashi Iwaia5973102011-09-28 16:43:36 +02001519 if (spec->multiout.num_dacs) {
1520 if (!spec->stream_analog_playback)
1521 spec->stream_analog_playback =
1522 &via_pcm_analog_playback;
1523 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1524 *spec->stream_analog_playback;
1525 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1526 spec->multiout.dac_nids[0];
1527 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1528 spec->multiout.max_channels;
1529 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001530
Takashi Iwaia5973102011-09-28 16:43:36 +02001531 if (!spec->stream_analog_capture) {
1532 if (spec->dyn_adc_switch)
1533 spec->stream_analog_capture =
1534 &via_pcm_dyn_adc_analog_capture;
1535 else
1536 spec->stream_analog_capture =
1537 &via_pcm_analog_capture;
1538 }
1539 if (spec->num_adc_nids) {
1540 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1541 *spec->stream_analog_capture;
1542 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1543 spec->adc_nids[0];
1544 if (!spec->dyn_adc_switch)
1545 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1546 spec->num_adc_nids;
1547 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001548 codec->num_pcms++;
1549 info++;
Takashi Iwaia5973102011-09-28 16:43:36 +02001550 }
1551
1552 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
Takashi Iwai82673bc2011-06-17 16:24:21 +02001553 snprintf(spec->stream_name_digital,
1554 sizeof(spec->stream_name_digital),
1555 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001556 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001557 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001558 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001559 if (!spec->stream_digital_playback)
1560 spec->stream_digital_playback =
1561 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001562 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001563 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001564 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1565 spec->multiout.dig_out_nid;
1566 }
1567 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001568 if (!spec->stream_digital_capture)
1569 spec->stream_digital_capture =
1570 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001571 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001572 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001573 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1574 spec->dig_in_nid;
1575 }
Takashi Iwaia5973102011-09-28 16:43:36 +02001576 codec->num_pcms++;
1577 info++;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001578 }
1579
Takashi Iwaiece8d042011-06-19 16:24:21 +02001580 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001581 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1582 "%s HP", codec->chip_name);
1583 info->name = spec->stream_name_hp;
1584 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1585 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001586 spec->hp_dac_nid;
Takashi Iwaia5973102011-09-28 16:43:36 +02001587 codec->num_pcms++;
1588 info++;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001589 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001590 return 0;
1591}
1592
1593static void via_free(struct hda_codec *codec)
1594{
1595 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001596
1597 if (!spec)
1598 return;
1599
Takashi Iwai603c4012008-07-30 15:01:44 +02001600 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001601 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001602 kfree(spec->bind_cap_vol);
1603 kfree(spec->bind_cap_sw);
1604 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001605}
1606
Takashi Iwai64be2852011-06-17 16:51:39 +02001607/* mute/unmute outputs */
1608static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1609 hda_nid_t *pins, bool mute)
1610{
1611 int i;
Takashi Iwai94994732011-07-11 11:36:44 +02001612 for (i = 0; i < num_pins; i++) {
1613 unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
1614 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1615 if (parm & AC_PINCTL_IN_EN)
1616 continue;
1617 if (mute)
1618 parm &= ~AC_PINCTL_OUT_EN;
1619 else
1620 parm |= AC_PINCTL_OUT_EN;
Takashi Iwai64be2852011-06-17 16:51:39 +02001621 snd_hda_codec_write(codec, pins[i], 0,
Takashi Iwai94994732011-07-11 11:36:44 +02001622 AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
1623 }
Takashi Iwai64be2852011-06-17 16:51:39 +02001624}
1625
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001626/* mute internal speaker if line-out is plugged */
1627static void via_line_automute(struct hda_codec *codec, int present)
1628{
1629 struct via_spec *spec = codec->spec;
1630
1631 if (!spec->autocfg.speaker_outs)
1632 return;
1633 if (!present)
1634 present = snd_hda_jack_detect(codec,
1635 spec->autocfg.line_out_pins[0]);
1636 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1637 spec->autocfg.speaker_pins,
1638 present);
1639}
1640
Harald Welte69e52a82008-09-09 15:57:32 +08001641/* mute internal speaker if HP is plugged */
1642static void via_hp_automute(struct hda_codec *codec)
1643{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001644 int present = 0;
Takashi Iwai6e969d92011-07-11 11:28:13 +02001645 int nums;
Harald Welte69e52a82008-09-09 15:57:32 +08001646 struct via_spec *spec = codec->spec;
1647
Takashi Iwai6e969d92011-07-11 11:28:13 +02001648 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0])
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001649 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai6e969d92011-07-11 11:28:13 +02001650
1651 if (spec->smart51_enabled)
1652 nums = spec->autocfg.line_outs + spec->smart51_nums;
1653 else
1654 nums = spec->autocfg.line_outs;
1655 toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
1656
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001657 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001658}
1659
Harald Welte69e52a82008-09-09 15:57:32 +08001660static void via_gpio_control(struct hda_codec *codec)
1661{
1662 unsigned int gpio_data;
1663 unsigned int vol_counter;
1664 unsigned int vol;
1665 unsigned int master_vol;
1666
1667 struct via_spec *spec = codec->spec;
1668
1669 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1670 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1671
1672 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1673 0xF84, 0) & 0x3F0000) >> 16;
1674
1675 vol = vol_counter & 0x1F;
1676 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1677 AC_VERB_GET_AMP_GAIN_MUTE,
1678 AC_AMP_GET_INPUT);
1679
1680 if (gpio_data == 0x02) {
1681 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001682 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1683 AC_VERB_SET_PIN_WIDGET_CONTROL,
1684 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001685 if (vol_counter & 0x20) {
1686 /* decrease volume */
1687 if (vol > master_vol)
1688 vol = master_vol;
1689 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1690 0, HDA_AMP_VOLMASK,
1691 master_vol-vol);
1692 } else {
1693 /* increase volume */
1694 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1695 HDA_AMP_VOLMASK,
1696 ((master_vol+vol) > 0x2A) ? 0x2A :
1697 (master_vol+vol));
1698 }
1699 } else if (!(gpio_data & 0x02)) {
1700 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001701 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1702 AC_VERB_SET_PIN_WIDGET_CONTROL,
1703 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001704 }
1705}
1706
1707/* unsolicited event for jack sensing */
1708static void via_unsol_event(struct hda_codec *codec,
1709 unsigned int res)
1710{
1711 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001712
Lydia Wanga34df192009-10-10 19:08:01 +08001713 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001714 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001715
1716 res &= ~VIA_JACK_EVENT;
1717
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001718 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001719 via_hp_automute(codec);
1720 else if (res == VIA_GPIO_EVENT)
1721 via_gpio_control(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001722}
1723
Takashi Iwai2a439522011-07-26 09:52:50 +02001724#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001725static int via_suspend(struct hda_codec *codec, pm_message_t state)
1726{
1727 struct via_spec *spec = codec->spec;
1728 vt1708_stop_hp_work(spec);
1729 return 0;
1730}
1731#endif
1732
Takashi Iwaicb53c622007-08-10 17:21:45 +02001733#ifdef CONFIG_SND_HDA_POWER_SAVE
1734static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1735{
1736 struct via_spec *spec = codec->spec;
1737 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1738}
1739#endif
1740
Joseph Chanc577b8a2006-11-29 15:29:40 +01001741/*
1742 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001743
1744static int via_init(struct hda_codec *codec);
1745
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001746static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001747 .build_controls = via_build_controls,
1748 .build_pcms = via_build_pcms,
1749 .init = via_init,
1750 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001751 .unsol_event = via_unsol_event,
Takashi Iwai2a439522011-07-26 09:52:50 +02001752#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001753 .suspend = via_suspend,
1754#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001755#ifdef CONFIG_SND_HDA_POWER_SAVE
1756 .check_power_status = via_check_power_status,
1757#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001758};
1759
Takashi Iwai4a796162011-06-17 17:53:38 +02001760static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001761{
Takashi Iwai4a796162011-06-17 17:53:38 +02001762 struct via_spec *spec = codec->spec;
1763 int i;
1764
1765 for (i = 0; i < spec->multiout.num_dacs; i++) {
1766 if (spec->multiout.dac_nids[i] == dac)
1767 return false;
1768 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001769 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001770 return false;
1771 return true;
1772}
1773
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001774static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001775 hda_nid_t target_dac, int with_aa_mix,
1776 struct nid_path *path, int depth)
Takashi Iwai4a796162011-06-17 17:53:38 +02001777{
Takashi Iwai3214b962011-07-18 12:49:25 +02001778 struct via_spec *spec = codec->spec;
Takashi Iwai4a796162011-06-17 17:53:38 +02001779 hda_nid_t conn[8];
1780 int i, nums;
1781
Takashi Iwai3214b962011-07-18 12:49:25 +02001782 if (nid == spec->aa_mix_nid) {
1783 if (!with_aa_mix)
1784 return false;
1785 with_aa_mix = 2; /* mark aa-mix is included */
1786 }
1787
Takashi Iwai4a796162011-06-17 17:53:38 +02001788 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1789 for (i = 0; i < nums; i++) {
1790 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1791 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001792 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1793 /* aa-mix is requested but not included? */
1794 if (!(spec->aa_mix_nid && with_aa_mix == 1))
1795 goto found;
1796 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001797 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001798 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001799 return false;
1800 for (i = 0; i < nums; i++) {
1801 unsigned int type;
1802 type = get_wcaps_type(get_wcaps(codec, conn[i]));
Takashi Iwai3214b962011-07-18 12:49:25 +02001803 if (type == AC_WID_AUD_OUT)
Takashi Iwai4a796162011-06-17 17:53:38 +02001804 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001805 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai3214b962011-07-18 12:49:25 +02001806 with_aa_mix, path, depth + 1))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001807 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001808 }
1809 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001810
1811 found:
1812 path->path[path->depth] = conn[i];
1813 path->idx[path->depth] = i;
1814 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1815 path->multi[path->depth] = 1;
1816 path->depth++;
1817 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001818}
1819
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001820static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001821 hda_nid_t target_dac, int with_aa_mix,
1822 struct nid_path *path)
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001823{
Takashi Iwai3214b962011-07-18 12:49:25 +02001824 if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001825 path->path[path->depth] = nid;
1826 path->depth++;
Takashi Iwai3214b962011-07-18 12:49:25 +02001827 snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
1828 path->depth, path->path[0], path->path[1],
1829 path->path[2], path->path[3], path->path[4]);
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001830 return true;
1831 }
1832 return false;
1833}
1834
Takashi Iwai4a796162011-06-17 17:53:38 +02001835static int via_auto_fill_dac_nids(struct hda_codec *codec)
1836{
1837 struct via_spec *spec = codec->spec;
1838 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001839 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001840 hda_nid_t nid;
1841
Joseph Chanc577b8a2006-11-29 15:29:40 +01001842 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001843 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001844 for (i = 0; i < cfg->line_outs; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001845 hda_nid_t dac = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001846 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001847 if (!nid)
1848 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001849 if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
1850 dac = spec->out_path[i].path[0];
1851 if (!i && parse_output_path(codec, nid, dac, 1,
1852 &spec->out_mix_path))
1853 dac = spec->out_mix_path.path[0];
1854 if (dac) {
1855 spec->private_dac_nids[i] = dac;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001856 dac_num++;
1857 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001858 }
Takashi Iwai3214b962011-07-18 12:49:25 +02001859 if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
1860 spec->out_path[0] = spec->out_mix_path;
1861 spec->out_mix_path.depth = 0;
1862 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001863 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001864 return 0;
1865}
1866
Takashi Iwai4a796162011-06-17 17:53:38 +02001867static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001868 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001869{
Takashi Iwai4a796162011-06-17 17:53:38 +02001870 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001871 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001872 hda_nid_t dac, pin, sel, nid;
1873 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001874
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001875 dac = check_dac ? path->path[0] : 0;
1876 pin = path->path[path->depth - 1];
1877 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001878
Takashi Iwai8df2a312011-06-21 11:48:29 +02001879 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001880 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001881 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001882 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001883 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1884 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001885 else
1886 nid = 0;
1887 if (nid) {
1888 sprintf(name, "%s Playback Volume", pfx);
1889 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001890 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001891 if (err < 0)
1892 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001893 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001894 }
1895
Takashi Iwai8df2a312011-06-21 11:48:29 +02001896 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001897 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001898 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001899 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001900 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1901 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001902 else
1903 nid = 0;
1904 if (nid) {
1905 sprintf(name, "%s Playback Switch", pfx);
1906 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1907 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1908 if (err < 0)
1909 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001910 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001911 }
1912 return 0;
1913}
1914
Takashi Iwaif4a78282011-06-17 18:46:48 +02001915static void mangle_smart51(struct hda_codec *codec)
1916{
1917 struct via_spec *spec = codec->spec;
1918 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001919 struct auto_pin_cfg_item *ins = cfg->inputs;
1920 int i, j, nums, attr;
1921 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001922
Takashi Iwai0f98c242011-06-21 12:51:33 +02001923 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1924 nums = 0;
1925 for (i = 0; i < cfg->num_inputs; i++) {
1926 unsigned int def;
1927 if (ins[i].type > AUTO_PIN_LINE_IN)
1928 continue;
1929 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1930 if (snd_hda_get_input_pin_attr(def) != attr)
1931 continue;
1932 for (j = 0; j < nums; j++)
1933 if (ins[pins[j]].type < ins[i].type) {
1934 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001935 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001936 break;
1937 }
1938 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001939 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001940 }
1941 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001942 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001943 for (i = 0; i < nums; i++) {
1944 hda_nid_t pin = ins[pins[i]].pin;
1945 spec->smart51_pins[spec->smart51_nums++] = pin;
1946 cfg->line_out_pins[cfg->line_outs++] = pin;
1947 if (cfg->line_outs == 3)
1948 break;
1949 }
1950 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001951 }
1952}
1953
Takashi Iwai020066d2011-07-21 13:45:56 +02001954static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src)
1955{
1956 dst->vol_ctl = src->vol_ctl;
1957 dst->mute_ctl = src->mute_ctl;
1958}
1959
Takashi Iwai4a796162011-06-17 17:53:38 +02001960/* add playback controls from the parsed DAC table */
1961static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1962{
1963 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001964 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai3214b962011-07-18 12:49:25 +02001965 struct nid_path *path;
Takashi Iwaiea734962011-01-17 11:29:34 +01001966 static const char * const chname[4] = {
1967 "Front", "Surround", "C/LFE", "Side"
1968 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001969 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001970 int old_line_outs;
1971
1972 /* check smart51 */
1973 old_line_outs = cfg->line_outs;
1974 if (cfg->line_outs == 1)
1975 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001976
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001977 err = via_auto_fill_dac_nids(codec);
1978 if (err < 0)
1979 return err;
1980
Lydia Wang5c9a5612011-07-08 14:03:43 +08001981 if (spec->multiout.num_dacs < 3) {
1982 spec->smart51_nums = 0;
1983 cfg->line_outs = old_line_outs;
1984 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001985 for (i = 0; i < cfg->line_outs; i++) {
1986 hda_nid_t pin, dac;
1987 pin = cfg->line_out_pins[i];
1988 dac = spec->multiout.dac_nids[i];
1989 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001990 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001991 path = spec->out_path + i;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001992 if (i == HDA_CLFE) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001993 err = create_ch_ctls(codec, "Center", 1, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001994 if (err < 0)
1995 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02001996 err = create_ch_ctls(codec, "LFE", 2, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001997 if (err < 0)
1998 return err;
1999 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02002000 const char *pfx = chname[i];
2001 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
2002 cfg->line_outs == 1)
2003 pfx = "Speaker";
Takashi Iwai3214b962011-07-18 12:49:25 +02002004 err = create_ch_ctls(codec, pfx, 3, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002005 if (err < 0)
2006 return err;
2007 }
Takashi Iwai020066d2011-07-21 13:45:56 +02002008 if (path != spec->out_path + i)
2009 copy_path_mixer_ctls(&spec->out_path[i], path);
2010 if (path == spec->out_path && spec->out_mix_path.depth)
2011 copy_path_mixer_ctls(&spec->out_mix_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002012 }
2013
Takashi Iwai4a796162011-06-17 17:53:38 +02002014 idx = get_connection_index(codec, spec->aa_mix_nid,
2015 spec->multiout.dac_nids[0]);
2016 if (idx >= 0) {
2017 /* add control to mixer */
Takashi Iwai3214b962011-07-18 12:49:25 +02002018 const char *name;
2019 name = spec->out_mix_path.depth ?
2020 "PCM Loopback Playback Volume" : "PCM Playback Volume";
2021 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002022 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2023 idx, HDA_INPUT));
2024 if (err < 0)
2025 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002026 name = spec->out_mix_path.depth ?
2027 "PCM Loopback Playback Switch" : "PCM Playback Switch";
2028 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002029 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2030 idx, HDA_INPUT));
2031 if (err < 0)
2032 return err;
2033 }
2034
Takashi Iwaif4a78282011-06-17 18:46:48 +02002035 cfg->line_outs = old_line_outs;
2036
Joseph Chanc577b8a2006-11-29 15:29:40 +01002037 return 0;
2038}
2039
Takashi Iwai4a796162011-06-17 17:53:38 +02002040static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002041{
Takashi Iwai4a796162011-06-17 17:53:38 +02002042 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002043 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002044 bool check_dac;
Takashi Iwai3214b962011-07-18 12:49:25 +02002045 int i, err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002046
2047 if (!pin)
2048 return 0;
2049
Takashi Iwai3214b962011-07-18 12:49:25 +02002050 if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
2051 for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
2052 if (i < spec->multiout.num_dacs &&
2053 parse_output_path(codec, pin,
2054 spec->multiout.dac_nids[i], 0,
2055 &spec->hp_indep_path)) {
2056 spec->hp_indep_shared = i;
2057 break;
2058 }
2059 }
Takashi Iwai25250502011-06-30 17:24:47 +02002060 }
Takashi Iwai3214b962011-07-18 12:49:25 +02002061 if (spec->hp_indep_path.depth) {
2062 spec->hp_dac_nid = spec->hp_indep_path.path[0];
2063 if (!spec->hp_indep_shared)
2064 spec->hp_path = spec->hp_indep_path;
2065 }
2066 /* optionally check front-path w/o AA-mix */
2067 if (!spec->hp_path.depth)
2068 parse_output_path(codec, pin,
2069 spec->multiout.dac_nids[HDA_FRONT], 0,
2070 &spec->hp_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002071
Takashi Iwaiece8d042011-06-19 16:24:21 +02002072 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai3214b962011-07-18 12:49:25 +02002073 1, &spec->hp_mix_path) && !spec->hp_path.depth)
Takashi Iwaiece8d042011-06-19 16:24:21 +02002074 return 0;
2075
Takashi Iwai3214b962011-07-18 12:49:25 +02002076 if (spec->hp_path.depth) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002077 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002078 check_dac = true;
2079 } else {
Takashi Iwai3214b962011-07-18 12:49:25 +02002080 path = &spec->hp_mix_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002081 check_dac = false;
2082 }
2083 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002084 if (err < 0)
2085 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002086 if (check_dac)
2087 copy_path_mixer_ctls(&spec->hp_mix_path, path);
2088 else
2089 copy_path_mixer_ctls(&spec->hp_path, path);
2090 if (spec->hp_indep_path.depth)
2091 copy_path_mixer_ctls(&spec->hp_indep_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002092 return 0;
2093}
2094
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002095static int via_auto_create_speaker_ctls(struct hda_codec *codec)
2096{
2097 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +02002098 struct nid_path *path;
2099 bool check_dac;
Wang Shaoyan81c0a782011-08-05 18:51:29 +08002100 hda_nid_t pin, dac = 0;
Takashi Iwai3214b962011-07-18 12:49:25 +02002101 int err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002102
2103 pin = spec->autocfg.speaker_pins[0];
2104 if (!spec->autocfg.speaker_outs || !pin)
2105 return 0;
2106
Takashi Iwai3214b962011-07-18 12:49:25 +02002107 if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02002108 dac = spec->speaker_path.path[0];
Takashi Iwai3214b962011-07-18 12:49:25 +02002109 if (!dac)
2110 parse_output_path(codec, pin,
2111 spec->multiout.dac_nids[HDA_FRONT], 0,
2112 &spec->speaker_path);
2113 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
2114 1, &spec->speaker_mix_path) && !dac)
2115 return 0;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002116
Takashi Iwai3214b962011-07-18 12:49:25 +02002117 /* no AA-path for front? */
2118 if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
2119 dac = 0;
2120
2121 spec->speaker_dac_nid = dac;
2122 spec->multiout.extra_out_nid[0] = dac;
2123 if (dac) {
2124 path = &spec->speaker_path;
2125 check_dac = true;
2126 } else {
2127 path = &spec->speaker_mix_path;
2128 check_dac = false;
2129 }
2130 err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
2131 if (err < 0)
2132 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002133 if (check_dac)
2134 copy_path_mixer_ctls(&spec->speaker_mix_path, path);
2135 else
2136 copy_path_mixer_ctls(&spec->speaker_path, path);
Takashi Iwai3214b962011-07-18 12:49:25 +02002137 return 0;
2138}
2139
2140#define via_aamix_ctl_info via_pin_power_ctl_info
2141
2142static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol,
2143 struct snd_ctl_elem_value *ucontrol)
2144{
2145 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2146 struct via_spec *spec = codec->spec;
2147 ucontrol->value.enumerated.item[0] = spec->aamix_mode;
2148 return 0;
2149}
2150
2151static void update_aamix_paths(struct hda_codec *codec, int do_mix,
2152 struct nid_path *nomix, struct nid_path *mix)
2153{
2154 if (do_mix) {
2155 activate_output_path(codec, nomix, false, false);
2156 activate_output_path(codec, mix, true, false);
2157 } else {
2158 activate_output_path(codec, mix, false, false);
2159 activate_output_path(codec, nomix, true, false);
2160 }
2161}
2162
2163static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol,
2164 struct snd_ctl_elem_value *ucontrol)
2165{
2166 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2167 struct via_spec *spec = codec->spec;
2168 unsigned int val = ucontrol->value.enumerated.item[0];
2169
2170 if (val == spec->aamix_mode)
2171 return 0;
2172 spec->aamix_mode = val;
2173 /* update front path */
2174 update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
2175 /* update HP path */
2176 if (!spec->hp_independent_mode) {
2177 update_aamix_paths(codec, val, &spec->hp_path,
2178 &spec->hp_mix_path);
2179 }
2180 /* update speaker path */
2181 update_aamix_paths(codec, val, &spec->speaker_path,
2182 &spec->speaker_mix_path);
2183 return 1;
2184}
2185
2186static const struct snd_kcontrol_new via_aamix_ctl_enum = {
2187 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2188 .name = "Loopback Mixing",
2189 .info = via_aamix_ctl_info,
2190 .get = via_aamix_ctl_get,
2191 .put = via_aamix_ctl_put,
2192};
2193
2194static int via_auto_create_loopback_switch(struct hda_codec *codec)
2195{
2196 struct via_spec *spec = codec->spec;
2197
2198 if (!spec->aa_mix_nid || !spec->out_mix_path.depth)
2199 return 0; /* no loopback switching available */
2200 if (!via_clone_control(spec, &via_aamix_ctl_enum))
2201 return -ENOMEM;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002202 return 0;
2203}
2204
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002205/* look for ADCs */
2206static int via_fill_adcs(struct hda_codec *codec)
2207{
2208 struct via_spec *spec = codec->spec;
2209 hda_nid_t nid = codec->start_nid;
2210 int i;
2211
2212 for (i = 0; i < codec->num_nodes; i++, nid++) {
2213 unsigned int wcaps = get_wcaps(codec, nid);
2214 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2215 continue;
2216 if (wcaps & AC_WCAP_DIGITAL)
2217 continue;
2218 if (!(wcaps & AC_WCAP_CONN_LIST))
2219 continue;
2220 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2221 return -ENOMEM;
2222 spec->adc_nids[spec->num_adc_nids++] = nid;
2223 }
2224 return 0;
2225}
2226
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002227/* input-src control */
2228static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2229 struct snd_ctl_elem_info *uinfo)
2230{
2231 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2232 struct via_spec *spec = codec->spec;
2233
2234 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2235 uinfo->count = 1;
2236 uinfo->value.enumerated.items = spec->num_inputs;
2237 if (uinfo->value.enumerated.item >= spec->num_inputs)
2238 uinfo->value.enumerated.item = spec->num_inputs - 1;
2239 strcpy(uinfo->value.enumerated.name,
2240 spec->inputs[uinfo->value.enumerated.item].label);
2241 return 0;
2242}
2243
2244static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2245 struct snd_ctl_elem_value *ucontrol)
2246{
2247 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2248 struct via_spec *spec = codec->spec;
2249 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2250
2251 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2252 return 0;
2253}
2254
2255static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2256 struct snd_ctl_elem_value *ucontrol)
2257{
2258 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2259 struct via_spec *spec = codec->spec;
2260 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2261 hda_nid_t mux;
2262 int cur;
2263
2264 cur = ucontrol->value.enumerated.item[0];
2265 if (cur < 0 || cur >= spec->num_inputs)
2266 return -EINVAL;
2267 if (spec->cur_mux[idx] == cur)
2268 return 0;
2269 spec->cur_mux[idx] = cur;
2270 if (spec->dyn_adc_switch) {
2271 int adc_idx = spec->inputs[cur].adc_idx;
2272 mux = spec->mux_nids[adc_idx];
2273 via_dyn_adc_pcm_resetup(codec, cur);
2274 } else {
2275 mux = spec->mux_nids[idx];
2276 if (snd_BUG_ON(!mux))
2277 return -EINVAL;
2278 }
2279
2280 if (mux) {
2281 /* switch to D0 beofre change index */
2282 if (snd_hda_codec_read(codec, mux, 0,
2283 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2284 snd_hda_codec_write(codec, mux, 0,
2285 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2286 snd_hda_codec_write(codec, mux, 0,
2287 AC_VERB_SET_CONNECT_SEL,
2288 spec->inputs[cur].mux_idx);
2289 }
2290
2291 /* update jack power state */
2292 set_widgets_power_state(codec);
2293 return 0;
2294}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002295
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002296static const struct snd_kcontrol_new via_input_src_ctl = {
2297 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2298 /* The multiple "Capture Source" controls confuse alsamixer
2299 * So call somewhat different..
2300 */
2301 /* .name = "Capture Source", */
2302 .name = "Input Source",
2303 .info = via_mux_enum_info,
2304 .get = via_mux_enum_get,
2305 .put = via_mux_enum_put,
2306};
2307
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002308static int create_input_src_ctls(struct hda_codec *codec, int count)
2309{
2310 struct via_spec *spec = codec->spec;
2311 struct snd_kcontrol_new *knew;
2312
2313 if (spec->num_inputs <= 1 || !count)
2314 return 0; /* no need for single src */
2315
2316 knew = via_clone_control(spec, &via_input_src_ctl);
2317 if (!knew)
2318 return -ENOMEM;
2319 knew->count = count;
2320 return 0;
2321}
2322
2323/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002324static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2325{
2326 struct hda_amp_list *list;
2327
2328 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2329 return;
2330 list = spec->loopback_list + spec->num_loopbacks;
2331 list->nid = mix;
2332 list->dir = HDA_INPUT;
2333 list->idx = idx;
2334 spec->num_loopbacks++;
2335 spec->loopback.amplist = spec->loopback_list;
2336}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002337
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002338static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002339 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002340{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002341 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002342}
2343
2344/* add the input-route to the given pin */
2345static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002346{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002347 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002348 int c, idx;
2349
2350 spec->inputs[spec->num_inputs].adc_idx = -1;
2351 spec->inputs[spec->num_inputs].pin = pin;
2352 for (c = 0; c < spec->num_adc_nids; c++) {
2353 if (spec->mux_nids[c]) {
2354 idx = get_connection_index(codec, spec->mux_nids[c],
2355 pin);
2356 if (idx < 0)
2357 continue;
2358 spec->inputs[spec->num_inputs].mux_idx = idx;
2359 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002360 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002361 continue;
2362 }
2363 spec->inputs[spec->num_inputs].adc_idx = c;
2364 /* Can primary ADC satisfy all inputs? */
2365 if (!spec->dyn_adc_switch &&
2366 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2367 snd_printd(KERN_INFO
2368 "via: dynamic ADC switching enabled\n");
2369 spec->dyn_adc_switch = 1;
2370 }
2371 return true;
2372 }
2373 return false;
2374}
2375
2376static int get_mux_nids(struct hda_codec *codec);
2377
2378/* parse input-routes; fill ADCs, MUXs and input-src entries */
2379static int parse_analog_inputs(struct hda_codec *codec)
2380{
2381 struct via_spec *spec = codec->spec;
2382 const struct auto_pin_cfg *cfg = &spec->autocfg;
2383 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002384
2385 err = via_fill_adcs(codec);
2386 if (err < 0)
2387 return err;
2388 err = get_mux_nids(codec);
2389 if (err < 0)
2390 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002391
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002392 /* fill all input-routes */
2393 for (i = 0; i < cfg->num_inputs; i++) {
2394 if (add_input_route(codec, cfg->inputs[i].pin))
2395 spec->inputs[spec->num_inputs++].label =
2396 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002397 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002398
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002399 /* check for internal loopback recording */
2400 if (spec->aa_mix_nid &&
2401 add_input_route(codec, spec->aa_mix_nid))
2402 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2403
2404 return 0;
2405}
2406
2407/* create analog-loopback volume/switch controls */
2408static int create_loopback_ctls(struct hda_codec *codec)
2409{
2410 struct via_spec *spec = codec->spec;
2411 const struct auto_pin_cfg *cfg = &spec->autocfg;
2412 const char *prev_label = NULL;
2413 int type_idx = 0;
2414 int i, j, err, idx;
2415
2416 if (!spec->aa_mix_nid)
2417 return 0;
2418
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002419 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002420 hda_nid_t pin = cfg->inputs[i].pin;
2421 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2422
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002423 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002424 type_idx++;
2425 else
2426 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002427 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002428 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2429 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002430 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002431 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002432 if (err < 0)
2433 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002434 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002435 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002436
2437 /* remember the label for smart51 control */
2438 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002439 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002440 spec->smart51_idxs[j] = idx;
2441 spec->smart51_labels[j] = label;
2442 break;
2443 }
2444 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002445 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002446 return 0;
2447}
2448
2449/* create mic-boost controls (if present) */
2450static int create_mic_boost_ctls(struct hda_codec *codec)
2451{
2452 struct via_spec *spec = codec->spec;
2453 const struct auto_pin_cfg *cfg = &spec->autocfg;
2454 int i, err;
2455
2456 for (i = 0; i < cfg->num_inputs; i++) {
2457 hda_nid_t pin = cfg->inputs[i].pin;
2458 unsigned int caps;
2459 const char *label;
2460 char name[32];
2461
2462 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2463 continue;
2464 caps = query_amp_caps(codec, pin, HDA_INPUT);
2465 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2466 continue;
2467 label = hda_get_autocfg_input_label(codec, cfg, i);
2468 snprintf(name, sizeof(name), "%s Boost Volume", label);
2469 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2470 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2471 if (err < 0)
2472 return err;
2473 }
2474 return 0;
2475}
2476
2477/* create capture and input-src controls for multiple streams */
2478static int create_multi_adc_ctls(struct hda_codec *codec)
2479{
2480 struct via_spec *spec = codec->spec;
2481 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002482
2483 /* create capture mixer elements */
2484 for (i = 0; i < spec->num_adc_nids; i++) {
2485 hda_nid_t adc = spec->adc_nids[i];
2486 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2487 "Capture Volume", i,
2488 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2489 HDA_INPUT));
2490 if (err < 0)
2491 return err;
2492 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2493 "Capture Switch", i,
2494 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2495 HDA_INPUT));
2496 if (err < 0)
2497 return err;
2498 }
2499
2500 /* input-source control */
2501 for (i = 0; i < spec->num_adc_nids; i++)
2502 if (!spec->mux_nids[i])
2503 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002504 err = create_input_src_ctls(codec, i);
2505 if (err < 0)
2506 return err;
2507 return 0;
2508}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002509
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002510/* bind capture volume/switch */
2511static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2512 HDA_BIND_VOL("Capture Volume", 0);
2513static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2514 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002515
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002516static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2517 struct hda_ctl_ops *ops)
2518{
2519 struct hda_bind_ctls *ctl;
2520 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002521
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002522 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2523 if (!ctl)
2524 return -ENOMEM;
2525 ctl->ops = ops;
2526 for (i = 0; i < spec->num_adc_nids; i++)
2527 ctl->values[i] =
2528 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2529 *ctl_ret = ctl;
2530 return 0;
2531}
2532
2533/* create capture and input-src controls for dynamic ADC-switch case */
2534static int create_dyn_adc_ctls(struct hda_codec *codec)
2535{
2536 struct via_spec *spec = codec->spec;
2537 struct snd_kcontrol_new *knew;
2538 int err;
2539
2540 /* set up the bind capture ctls */
2541 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2542 if (err < 0)
2543 return err;
2544 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2545 if (err < 0)
2546 return err;
2547
2548 /* create capture mixer elements */
2549 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2550 if (!knew)
2551 return -ENOMEM;
2552 knew->private_value = (long)spec->bind_cap_vol;
2553
2554 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2555 if (!knew)
2556 return -ENOMEM;
2557 knew->private_value = (long)spec->bind_cap_sw;
2558
2559 /* input-source control */
2560 err = create_input_src_ctls(codec, 1);
2561 if (err < 0)
2562 return err;
2563 return 0;
2564}
2565
2566/* parse and create capture-related stuff */
2567static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2568{
2569 struct via_spec *spec = codec->spec;
2570 int err;
2571
2572 err = parse_analog_inputs(codec);
2573 if (err < 0)
2574 return err;
2575 if (spec->dyn_adc_switch)
2576 err = create_dyn_adc_ctls(codec);
2577 else
2578 err = create_multi_adc_ctls(codec);
2579 if (err < 0)
2580 return err;
2581 err = create_loopback_ctls(codec);
2582 if (err < 0)
2583 return err;
2584 err = create_mic_boost_ctls(codec);
2585 if (err < 0)
2586 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002587 return 0;
2588}
2589
Harald Welte76d9b0d2008-09-09 15:50:37 +08002590static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2591{
2592 unsigned int def_conf;
2593 unsigned char seqassoc;
2594
Takashi Iwai2f334f92009-02-20 14:37:42 +01002595 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002596 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2597 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002598 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2599 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2600 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2601 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002602 }
2603
2604 return;
2605}
2606
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002607static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002608 struct snd_ctl_elem_value *ucontrol)
2609{
2610 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2611 struct via_spec *spec = codec->spec;
2612
2613 if (spec->codec_type != VT1708)
2614 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002615 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002616 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002617 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002618 return 0;
2619}
2620
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002621static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002622 struct snd_ctl_elem_value *ucontrol)
2623{
2624 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2625 struct via_spec *spec = codec->spec;
2626 int change;
2627
2628 if (spec->codec_type != VT1708)
2629 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002630 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002631 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002632 == !spec->vt1708_jack_detect;
2633 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002634 mute_aa_path(codec, 1);
2635 notify_aa_path_ctls(codec);
2636 }
2637 return change;
2638}
2639
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002640static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2641 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2642 .name = "Jack Detect",
2643 .count = 1,
2644 .info = snd_ctl_boolean_mono_info,
2645 .get = vt1708_jack_detect_get,
2646 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002647};
2648
Takashi Iwai12daef62011-06-18 17:45:49 +02002649static void fill_dig_outs(struct hda_codec *codec);
2650static void fill_dig_in(struct hda_codec *codec);
2651
2652static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002653{
2654 struct via_spec *spec = codec->spec;
2655 int err;
2656
2657 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2658 if (err < 0)
2659 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002660 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002661 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002662
Takashi Iwai4a796162011-06-17 17:53:38 +02002663 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002664 if (err < 0)
2665 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002666 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002667 if (err < 0)
2668 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002669 err = via_auto_create_speaker_ctls(codec);
2670 if (err < 0)
2671 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002672 err = via_auto_create_loopback_switch(codec);
2673 if (err < 0)
2674 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002675 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002676 if (err < 0)
2677 return err;
2678
2679 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2680
Takashi Iwai12daef62011-06-18 17:45:49 +02002681 fill_dig_outs(codec);
2682 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002683
Takashi Iwai603c4012008-07-30 15:01:44 +02002684 if (spec->kctls.list)
2685 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002686
Joseph Chanc577b8a2006-11-29 15:29:40 +01002687
Takashi Iwai3214b962011-07-18 12:49:25 +02002688 if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002689 err = via_hp_build(codec);
2690 if (err < 0)
2691 return err;
2692 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002693
Takashi Iwaif4a78282011-06-17 18:46:48 +02002694 err = via_smart51_build(codec);
2695 if (err < 0)
2696 return err;
2697
Takashi Iwai5d417622011-06-20 11:32:27 +02002698 /* assign slave outs */
2699 if (spec->slave_dig_outs[0])
2700 codec->slave_dig_outs = spec->slave_dig_outs;
2701
Joseph Chanc577b8a2006-11-29 15:29:40 +01002702 return 1;
2703}
2704
Takashi Iwai5d417622011-06-20 11:32:27 +02002705static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002706{
Lydia Wang25eaba22009-10-10 19:08:43 +08002707 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002708 if (spec->multiout.dig_out_nid)
2709 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2710 if (spec->slave_dig_outs[0])
2711 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2712}
Lydia Wang25eaba22009-10-10 19:08:43 +08002713
Takashi Iwai5d417622011-06-20 11:32:27 +02002714static void via_auto_init_dig_in(struct hda_codec *codec)
2715{
2716 struct via_spec *spec = codec->spec;
2717 if (!spec->dig_in_nid)
2718 return;
2719 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2720 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2721}
2722
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002723/* initialize the unsolicited events */
2724static void via_auto_init_unsol_event(struct hda_codec *codec)
2725{
2726 struct via_spec *spec = codec->spec;
2727 struct auto_pin_cfg *cfg = &spec->autocfg;
2728 unsigned int ev;
2729 int i;
2730
2731 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2732 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2733 AC_VERB_SET_UNSOLICITED_ENABLE,
2734 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2735
2736 if (cfg->speaker_pins[0])
2737 ev = VIA_LINE_EVENT;
2738 else
2739 ev = 0;
2740 for (i = 0; i < cfg->line_outs; i++) {
2741 if (cfg->line_out_pins[i] &&
2742 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002743 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002744 AC_VERB_SET_UNSOLICITED_ENABLE,
2745 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2746 }
2747
2748 for (i = 0; i < cfg->num_inputs; i++) {
2749 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2750 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2751 AC_VERB_SET_UNSOLICITED_ENABLE,
2752 AC_USRSP_EN | VIA_JACK_EVENT);
2753 }
2754}
2755
Takashi Iwai5d417622011-06-20 11:32:27 +02002756static int via_init(struct hda_codec *codec)
2757{
2758 struct via_spec *spec = codec->spec;
2759 int i;
2760
2761 for (i = 0; i < spec->num_iverbs; i++)
2762 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2763
Joseph Chanc577b8a2006-11-29 15:29:40 +01002764 via_auto_init_multi_out(codec);
2765 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002766 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002767 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002768 via_auto_init_dig_outs(codec);
2769 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002770
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002771 via_auto_init_unsol_event(codec);
2772
2773 via_hp_automute(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002774
Joseph Chanc577b8a2006-11-29 15:29:40 +01002775 return 0;
2776}
2777
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002778static void vt1708_update_hp_jack_state(struct work_struct *work)
2779{
2780 struct via_spec *spec = container_of(work, struct via_spec,
2781 vt1708_hp_work.work);
2782 if (spec->codec_type != VT1708)
2783 return;
2784 /* if jack state toggled */
2785 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002786 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002787 spec->vt1708_hp_present ^= 1;
2788 via_hp_automute(spec->codec);
2789 }
2790 vt1708_start_hp_work(spec);
2791}
2792
Takashi Iwai337b9d02009-07-07 18:18:59 +02002793static int get_mux_nids(struct hda_codec *codec)
2794{
2795 struct via_spec *spec = codec->spec;
2796 hda_nid_t nid, conn[8];
2797 unsigned int type;
2798 int i, n;
2799
2800 for (i = 0; i < spec->num_adc_nids; i++) {
2801 nid = spec->adc_nids[i];
2802 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002803 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002804 if (type == AC_WID_PIN)
2805 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002806 n = snd_hda_get_connections(codec, nid, conn,
2807 ARRAY_SIZE(conn));
2808 if (n <= 0)
2809 break;
2810 if (n > 1) {
2811 spec->mux_nids[i] = nid;
2812 break;
2813 }
2814 nid = conn[0];
2815 }
2816 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002817 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002818}
2819
Joseph Chanc577b8a2006-11-29 15:29:40 +01002820static int patch_vt1708(struct hda_codec *codec)
2821{
2822 struct via_spec *spec;
2823 int err;
2824
2825 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002826 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002827 if (spec == NULL)
2828 return -ENOMEM;
2829
Takashi Iwai620e2b22011-06-17 17:19:19 +02002830 spec->aa_mix_nid = 0x17;
2831
Takashi Iwai12daef62011-06-18 17:45:49 +02002832 /* Add HP and CD pin config connect bit re-config action */
2833 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2834 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2835
Joseph Chanc577b8a2006-11-29 15:29:40 +01002836 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002837 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002838 if (err < 0) {
2839 via_free(codec);
2840 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002841 }
2842
Takashi Iwai12daef62011-06-18 17:45:49 +02002843 /* add jack detect on/off control */
2844 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2845 return -ENOMEM;
2846
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002847 /* disable 32bit format on VT1708 */
2848 if (codec->vendor_id == 0x11061708)
2849 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002850
Lydia Wange322a362011-06-29 13:52:02 +08002851 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2852
Joseph Chanc577b8a2006-11-29 15:29:40 +01002853 codec->patch_ops = via_patch_ops;
2854
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002855 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002856 return 0;
2857}
2858
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002859static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002860{
2861 struct via_spec *spec;
2862 int err;
2863
2864 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002865 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002866 if (spec == NULL)
2867 return -ENOMEM;
2868
Takashi Iwai620e2b22011-06-17 17:19:19 +02002869 spec->aa_mix_nid = 0x18;
2870
Takashi Iwai12daef62011-06-18 17:45:49 +02002871 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002872 if (err < 0) {
2873 via_free(codec);
2874 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002875 }
2876
Joseph Chanc577b8a2006-11-29 15:29:40 +01002877 codec->patch_ops = via_patch_ops;
2878
Josepch Chanf7278fd2007-12-13 16:40:40 +01002879 return 0;
2880}
2881
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002882static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2883{
2884 struct via_spec *spec = codec->spec;
2885 int imux_is_smixer;
2886 unsigned int parm;
2887 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002888 if ((spec->codec_type != VT1708B_4CH) &&
2889 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002890 is_8ch = 1;
2891
2892 /* SW0 (17h) = stereo mixer */
2893 imux_is_smixer =
2894 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2895 == ((spec->codec_type == VT1708S) ? 5 : 0));
2896 /* inputs */
2897 /* PW 1/2/5 (1ah/1bh/1eh) */
2898 parm = AC_PWRST_D3;
2899 set_pin_power_state(codec, 0x1a, &parm);
2900 set_pin_power_state(codec, 0x1b, &parm);
2901 set_pin_power_state(codec, 0x1e, &parm);
2902 if (imux_is_smixer)
2903 parm = AC_PWRST_D0;
2904 /* SW0 (17h), AIW 0/1 (13h/14h) */
2905 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2906 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2907 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2908
2909 /* outputs */
2910 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2911 parm = AC_PWRST_D3;
2912 set_pin_power_state(codec, 0x19, &parm);
2913 if (spec->smart51_enabled)
2914 set_pin_power_state(codec, 0x1b, &parm);
2915 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2916 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2917
2918 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2919 if (is_8ch) {
2920 parm = AC_PWRST_D3;
2921 set_pin_power_state(codec, 0x22, &parm);
2922 if (spec->smart51_enabled)
2923 set_pin_power_state(codec, 0x1a, &parm);
2924 snd_hda_codec_write(codec, 0x26, 0,
2925 AC_VERB_SET_POWER_STATE, parm);
2926 snd_hda_codec_write(codec, 0x24, 0,
2927 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002928 } else if (codec->vendor_id == 0x11064397) {
2929 /* PW7(23h), SW2(27h), AOW2(25h) */
2930 parm = AC_PWRST_D3;
2931 set_pin_power_state(codec, 0x23, &parm);
2932 if (spec->smart51_enabled)
2933 set_pin_power_state(codec, 0x1a, &parm);
2934 snd_hda_codec_write(codec, 0x27, 0,
2935 AC_VERB_SET_POWER_STATE, parm);
2936 snd_hda_codec_write(codec, 0x25, 0,
2937 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002938 }
2939
2940 /* PW 3/4/7 (1ch/1dh/23h) */
2941 parm = AC_PWRST_D3;
2942 /* force to D0 for internal Speaker */
2943 set_pin_power_state(codec, 0x1c, &parm);
2944 set_pin_power_state(codec, 0x1d, &parm);
2945 if (is_8ch)
2946 set_pin_power_state(codec, 0x23, &parm);
2947
2948 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2949 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2950 imux_is_smixer ? AC_PWRST_D0 : parm);
2951 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2952 if (is_8ch) {
2953 snd_hda_codec_write(codec, 0x25, 0,
2954 AC_VERB_SET_POWER_STATE, parm);
2955 snd_hda_codec_write(codec, 0x27, 0,
2956 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002957 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2958 snd_hda_codec_write(codec, 0x25, 0,
2959 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002960}
2961
Lydia Wang518bf3b2009-10-10 19:07:29 +08002962static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002963static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002964{
2965 struct via_spec *spec;
2966 int err;
2967
Lydia Wang518bf3b2009-10-10 19:07:29 +08002968 if (get_codec_type(codec) == VT1708BCE)
2969 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002970
Josepch Chanf7278fd2007-12-13 16:40:40 +01002971 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002972 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002973 if (spec == NULL)
2974 return -ENOMEM;
2975
Takashi Iwai620e2b22011-06-17 17:19:19 +02002976 spec->aa_mix_nid = 0x16;
2977
Josepch Chanf7278fd2007-12-13 16:40:40 +01002978 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002979 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002980 if (err < 0) {
2981 via_free(codec);
2982 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002983 }
2984
Josepch Chanf7278fd2007-12-13 16:40:40 +01002985 codec->patch_ops = via_patch_ops;
2986
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002987 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2988
Josepch Chanf7278fd2007-12-13 16:40:40 +01002989 return 0;
2990}
2991
Harald Welted949cac2008-09-09 15:56:01 +08002992/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002993static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002994 /* Enable Mic Boost Volume backdoor */
2995 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002996 /* don't bybass mixer */
2997 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002998 { }
2999};
3000
Takashi Iwai9da29272009-05-07 16:31:14 +02003001/* fill out digital output widgets; one for master and one for slave outputs */
3002static void fill_dig_outs(struct hda_codec *codec)
3003{
3004 struct via_spec *spec = codec->spec;
3005 int i;
3006
3007 for (i = 0; i < spec->autocfg.dig_outs; i++) {
3008 hda_nid_t nid;
3009 int conn;
3010
3011 nid = spec->autocfg.dig_out_pins[i];
3012 if (!nid)
3013 continue;
3014 conn = snd_hda_get_connections(codec, nid, &nid, 1);
3015 if (conn < 1)
3016 continue;
3017 if (!spec->multiout.dig_out_nid)
3018 spec->multiout.dig_out_nid = nid;
3019 else {
3020 spec->slave_dig_outs[0] = nid;
3021 break; /* at most two dig outs */
3022 }
3023 }
3024}
3025
Takashi Iwai12daef62011-06-18 17:45:49 +02003026static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08003027{
3028 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02003029 hda_nid_t dig_nid;
3030 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08003031
Takashi Iwai12daef62011-06-18 17:45:49 +02003032 if (!spec->autocfg.dig_in_pin)
3033 return;
Harald Welted949cac2008-09-09 15:56:01 +08003034
Takashi Iwai12daef62011-06-18 17:45:49 +02003035 dig_nid = codec->start_nid;
3036 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
3037 unsigned int wcaps = get_wcaps(codec, dig_nid);
3038 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
3039 continue;
3040 if (!(wcaps & AC_WCAP_DIGITAL))
3041 continue;
3042 if (!(wcaps & AC_WCAP_CONN_LIST))
3043 continue;
3044 err = get_connection_index(codec, dig_nid,
3045 spec->autocfg.dig_in_pin);
3046 if (err >= 0) {
3047 spec->dig_in_nid = dig_nid;
3048 break;
3049 }
3050 }
Harald Welted949cac2008-09-09 15:56:01 +08003051}
3052
Lydia Wang6369bcf2009-10-10 19:08:31 +08003053static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
3054 int offset, int num_steps, int step_size)
3055{
3056 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
3057 (offset << AC_AMPCAP_OFFSET_SHIFT) |
3058 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
3059 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
3060 (0 << AC_AMPCAP_MUTE_SHIFT));
3061}
3062
Harald Welted949cac2008-09-09 15:56:01 +08003063static int patch_vt1708S(struct hda_codec *codec)
3064{
3065 struct via_spec *spec;
3066 int err;
3067
3068 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003069 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003070 if (spec == NULL)
3071 return -ENOMEM;
3072
Takashi Iwai620e2b22011-06-17 17:19:19 +02003073 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003074 override_mic_boost(codec, 0x1a, 0, 3, 40);
3075 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003076
Harald Welted949cac2008-09-09 15:56:01 +08003077 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003078 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003079 if (err < 0) {
3080 via_free(codec);
3081 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003082 }
3083
Takashi Iwai096a8852011-06-20 12:09:02 +02003084 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003085
Harald Welted949cac2008-09-09 15:56:01 +08003086 codec->patch_ops = via_patch_ops;
3087
Lydia Wang518bf3b2009-10-10 19:07:29 +08003088 /* correct names for VT1708BCE */
3089 if (get_codec_type(codec) == VT1708BCE) {
3090 kfree(codec->chip_name);
3091 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3092 snprintf(codec->bus->card->mixername,
3093 sizeof(codec->bus->card->mixername),
3094 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08003095 }
Lydia Wangbc92df72011-03-23 17:56:05 +08003096 /* correct names for VT1705 */
3097 if (codec->vendor_id == 0x11064397) {
3098 kfree(codec->chip_name);
3099 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3100 snprintf(codec->bus->card->mixername,
3101 sizeof(codec->bus->card->mixername),
3102 "%s %s", codec->vendor_name, codec->chip_name);
3103 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003104 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08003105 return 0;
3106}
3107
3108/* Patch for VT1702 */
3109
Takashi Iwai096a8852011-06-20 12:09:02 +02003110static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003111 /* mixer enable */
3112 {0x1, 0xF88, 0x3},
3113 /* GPIO 0~2 */
3114 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003115 { }
3116};
3117
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003118static void set_widgets_power_state_vt1702(struct hda_codec *codec)
3119{
3120 int imux_is_smixer =
3121 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3122 unsigned int parm;
3123 /* inputs */
3124 /* PW 1/2/5 (14h/15h/18h) */
3125 parm = AC_PWRST_D3;
3126 set_pin_power_state(codec, 0x14, &parm);
3127 set_pin_power_state(codec, 0x15, &parm);
3128 set_pin_power_state(codec, 0x18, &parm);
3129 if (imux_is_smixer)
3130 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
3131 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
3132 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3133 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
3134 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3135 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
3136
3137 /* outputs */
3138 /* PW 3/4 (16h/17h) */
3139 parm = AC_PWRST_D3;
3140 set_pin_power_state(codec, 0x17, &parm);
3141 set_pin_power_state(codec, 0x16, &parm);
3142 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
3143 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
3144 imux_is_smixer ? AC_PWRST_D0 : parm);
3145 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3146 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3147}
3148
Harald Welted949cac2008-09-09 15:56:01 +08003149static int patch_vt1702(struct hda_codec *codec)
3150{
3151 struct via_spec *spec;
3152 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003153
3154 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003155 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003156 if (spec == NULL)
3157 return -ENOMEM;
3158
Takashi Iwai620e2b22011-06-17 17:19:19 +02003159 spec->aa_mix_nid = 0x1a;
3160
Takashi Iwai12daef62011-06-18 17:45:49 +02003161 /* limit AA path volume to 0 dB */
3162 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3163 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3164 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3165 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3166 (1 << AC_AMPCAP_MUTE_SHIFT));
3167
Harald Welted949cac2008-09-09 15:56:01 +08003168 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003169 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003170 if (err < 0) {
3171 via_free(codec);
3172 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003173 }
3174
Takashi Iwai096a8852011-06-20 12:09:02 +02003175 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003176
Harald Welted949cac2008-09-09 15:56:01 +08003177 codec->patch_ops = via_patch_ops;
3178
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003179 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08003180 return 0;
3181}
3182
Lydia Wangeb7188c2009-10-10 19:08:34 +08003183/* Patch for VT1718S */
3184
Takashi Iwai096a8852011-06-20 12:09:02 +02003185static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08003186 /* Enable MW0 adjust Gain 5 */
3187 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003188 /* Enable Boost Volume backdoor */
3189 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02003190
Lydia Wangeb7188c2009-10-10 19:08:34 +08003191 { }
3192};
3193
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003194static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
3195{
3196 struct via_spec *spec = codec->spec;
3197 int imux_is_smixer;
3198 unsigned int parm;
3199 /* MUX6 (1eh) = stereo mixer */
3200 imux_is_smixer =
3201 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3202 /* inputs */
3203 /* PW 5/6/7 (29h/2ah/2bh) */
3204 parm = AC_PWRST_D3;
3205 set_pin_power_state(codec, 0x29, &parm);
3206 set_pin_power_state(codec, 0x2a, &parm);
3207 set_pin_power_state(codec, 0x2b, &parm);
3208 if (imux_is_smixer)
3209 parm = AC_PWRST_D0;
3210 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
3211 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3212 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3213 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3214 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3215
3216 /* outputs */
3217 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
3218 parm = AC_PWRST_D3;
3219 set_pin_power_state(codec, 0x27, &parm);
3220 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
3221 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
3222
3223 /* PW2 (26h), AOW2 (ah) */
3224 parm = AC_PWRST_D3;
3225 set_pin_power_state(codec, 0x26, &parm);
3226 if (spec->smart51_enabled)
3227 set_pin_power_state(codec, 0x2b, &parm);
3228 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
3229
3230 /* PW0 (24h), AOW0 (8h) */
3231 parm = AC_PWRST_D3;
3232 set_pin_power_state(codec, 0x24, &parm);
3233 if (!spec->hp_independent_mode) /* check for redirected HP */
3234 set_pin_power_state(codec, 0x28, &parm);
3235 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3236 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
3237 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
3238 imux_is_smixer ? AC_PWRST_D0 : parm);
3239
3240 /* PW1 (25h), AOW1 (9h) */
3241 parm = AC_PWRST_D3;
3242 set_pin_power_state(codec, 0x25, &parm);
3243 if (spec->smart51_enabled)
3244 set_pin_power_state(codec, 0x2a, &parm);
3245 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
3246
3247 if (spec->hp_independent_mode) {
3248 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3249 parm = AC_PWRST_D3;
3250 set_pin_power_state(codec, 0x28, &parm);
3251 snd_hda_codec_write(codec, 0x1b, 0,
3252 AC_VERB_SET_POWER_STATE, parm);
3253 snd_hda_codec_write(codec, 0x34, 0,
3254 AC_VERB_SET_POWER_STATE, parm);
3255 snd_hda_codec_write(codec, 0xc, 0,
3256 AC_VERB_SET_POWER_STATE, parm);
3257 }
3258}
3259
Takashi Iwai30b45032011-07-11 17:05:04 +02003260/* Add a connection to the primary DAC from AA-mixer for some codecs
3261 * This isn't listed from the raw info, but the chip has a secret connection.
3262 */
3263static int add_secret_dac_path(struct hda_codec *codec)
3264{
3265 struct via_spec *spec = codec->spec;
3266 int i, nums;
3267 hda_nid_t conn[8];
3268 hda_nid_t nid;
3269
3270 if (!spec->aa_mix_nid)
3271 return 0;
3272 nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
3273 ARRAY_SIZE(conn) - 1);
3274 for (i = 0; i < nums; i++) {
3275 if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
3276 return 0;
3277 }
3278
3279 /* find the primary DAC and add to the connection list */
3280 nid = codec->start_nid;
3281 for (i = 0; i < codec->num_nodes; i++, nid++) {
3282 unsigned int caps = get_wcaps(codec, nid);
3283 if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
3284 !(caps & AC_WCAP_DIGITAL)) {
3285 conn[nums++] = nid;
3286 return snd_hda_override_conn_list(codec,
3287 spec->aa_mix_nid,
3288 nums, conn);
3289 }
3290 }
3291 return 0;
3292}
3293
3294
Lydia Wangeb7188c2009-10-10 19:08:34 +08003295static int patch_vt1718S(struct hda_codec *codec)
3296{
3297 struct via_spec *spec;
3298 int err;
3299
3300 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003301 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003302 if (spec == NULL)
3303 return -ENOMEM;
3304
Takashi Iwai620e2b22011-06-17 17:19:19 +02003305 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003306 override_mic_boost(codec, 0x2b, 0, 3, 40);
3307 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003308 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003309
Lydia Wangeb7188c2009-10-10 19:08:34 +08003310 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003311 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003312 if (err < 0) {
3313 via_free(codec);
3314 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003315 }
3316
Takashi Iwai096a8852011-06-20 12:09:02 +02003317 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003318
Lydia Wangeb7188c2009-10-10 19:08:34 +08003319 codec->patch_ops = via_patch_ops;
3320
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003321 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3322
Lydia Wangeb7188c2009-10-10 19:08:34 +08003323 return 0;
3324}
Lydia Wangf3db4232009-10-10 19:08:41 +08003325
3326/* Patch for VT1716S */
3327
3328static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3329 struct snd_ctl_elem_info *uinfo)
3330{
3331 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3332 uinfo->count = 1;
3333 uinfo->value.integer.min = 0;
3334 uinfo->value.integer.max = 1;
3335 return 0;
3336}
3337
3338static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3339 struct snd_ctl_elem_value *ucontrol)
3340{
3341 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3342 int index = 0;
3343
3344 index = snd_hda_codec_read(codec, 0x26, 0,
3345 AC_VERB_GET_CONNECT_SEL, 0);
3346 if (index != -1)
3347 *ucontrol->value.integer.value = index;
3348
3349 return 0;
3350}
3351
3352static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3353 struct snd_ctl_elem_value *ucontrol)
3354{
3355 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3356 struct via_spec *spec = codec->spec;
3357 int index = *ucontrol->value.integer.value;
3358
3359 snd_hda_codec_write(codec, 0x26, 0,
3360 AC_VERB_SET_CONNECT_SEL, index);
3361 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003362 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003363 return 1;
3364}
3365
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003366static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003367 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3368 {
3369 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3370 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003371 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003372 .count = 1,
3373 .info = vt1716s_dmic_info,
3374 .get = vt1716s_dmic_get,
3375 .put = vt1716s_dmic_put,
3376 },
3377 {} /* end */
3378};
3379
3380
3381/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003382static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003383 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3384 { } /* end */
3385};
3386
Takashi Iwai096a8852011-06-20 12:09:02 +02003387static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003388 /* Enable Boost Volume backdoor */
3389 {0x1, 0xf8a, 0x80},
3390 /* don't bybass mixer */
3391 {0x1, 0xf88, 0xc0},
3392 /* Enable mono output */
3393 {0x1, 0xf90, 0x08},
3394 { }
3395};
3396
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003397static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3398{
3399 struct via_spec *spec = codec->spec;
3400 int imux_is_smixer;
3401 unsigned int parm;
3402 unsigned int mono_out, present;
3403 /* SW0 (17h) = stereo mixer */
3404 imux_is_smixer =
3405 (snd_hda_codec_read(codec, 0x17, 0,
3406 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3407 /* inputs */
3408 /* PW 1/2/5 (1ah/1bh/1eh) */
3409 parm = AC_PWRST_D3;
3410 set_pin_power_state(codec, 0x1a, &parm);
3411 set_pin_power_state(codec, 0x1b, &parm);
3412 set_pin_power_state(codec, 0x1e, &parm);
3413 if (imux_is_smixer)
3414 parm = AC_PWRST_D0;
3415 /* SW0 (17h), AIW0(13h) */
3416 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3417 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3418
3419 parm = AC_PWRST_D3;
3420 set_pin_power_state(codec, 0x1e, &parm);
3421 /* PW11 (22h) */
3422 if (spec->dmic_enabled)
3423 set_pin_power_state(codec, 0x22, &parm);
3424 else
3425 snd_hda_codec_write(codec, 0x22, 0,
3426 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3427
3428 /* SW2(26h), AIW1(14h) */
3429 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3430 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3431
3432 /* outputs */
3433 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3434 parm = AC_PWRST_D3;
3435 set_pin_power_state(codec, 0x19, &parm);
3436 /* Smart 5.1 PW2(1bh) */
3437 if (spec->smart51_enabled)
3438 set_pin_power_state(codec, 0x1b, &parm);
3439 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3440 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3441
3442 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3443 parm = AC_PWRST_D3;
3444 set_pin_power_state(codec, 0x23, &parm);
3445 /* Smart 5.1 PW1(1ah) */
3446 if (spec->smart51_enabled)
3447 set_pin_power_state(codec, 0x1a, &parm);
3448 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3449
3450 /* Smart 5.1 PW5(1eh) */
3451 if (spec->smart51_enabled)
3452 set_pin_power_state(codec, 0x1e, &parm);
3453 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3454
3455 /* Mono out */
3456 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3457 present = snd_hda_jack_detect(codec, 0x1c);
3458
3459 if (present)
3460 mono_out = 0;
3461 else {
3462 present = snd_hda_jack_detect(codec, 0x1d);
3463 if (!spec->hp_independent_mode && present)
3464 mono_out = 0;
3465 else
3466 mono_out = 1;
3467 }
3468 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3469 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3470 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3471 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3472
3473 /* PW 3/4 (1ch/1dh) */
3474 parm = AC_PWRST_D3;
3475 set_pin_power_state(codec, 0x1c, &parm);
3476 set_pin_power_state(codec, 0x1d, &parm);
3477 /* HP Independent Mode, power on AOW3 */
3478 if (spec->hp_independent_mode)
3479 snd_hda_codec_write(codec, 0x25, 0,
3480 AC_VERB_SET_POWER_STATE, parm);
3481
3482 /* force to D0 for internal Speaker */
3483 /* MW0 (16h), AOW0 (10h) */
3484 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3485 imux_is_smixer ? AC_PWRST_D0 : parm);
3486 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3487 mono_out ? AC_PWRST_D0 : parm);
3488}
3489
Lydia Wangf3db4232009-10-10 19:08:41 +08003490static int patch_vt1716S(struct hda_codec *codec)
3491{
3492 struct via_spec *spec;
3493 int err;
3494
3495 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003496 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003497 if (spec == NULL)
3498 return -ENOMEM;
3499
Takashi Iwai620e2b22011-06-17 17:19:19 +02003500 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003501 override_mic_boost(codec, 0x1a, 0, 3, 40);
3502 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003503
Lydia Wangf3db4232009-10-10 19:08:41 +08003504 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003505 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003506 if (err < 0) {
3507 via_free(codec);
3508 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003509 }
3510
Takashi Iwai096a8852011-06-20 12:09:02 +02003511 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003512
Lydia Wangf3db4232009-10-10 19:08:41 +08003513 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3514 spec->num_mixers++;
3515
3516 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3517
3518 codec->patch_ops = via_patch_ops;
3519
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003520 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003521 return 0;
3522}
Lydia Wang25eaba22009-10-10 19:08:43 +08003523
3524/* for vt2002P */
3525
Takashi Iwai096a8852011-06-20 12:09:02 +02003526static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003527 /* Class-D speaker related verbs */
3528 {0x1, 0xfe0, 0x4},
3529 {0x1, 0xfe9, 0x80},
3530 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003531 /* Enable Boost Volume backdoor */
3532 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003533 /* Enable AOW0 to MW9 */
3534 {0x1, 0xfb8, 0x88},
3535 { }
3536};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003537
Takashi Iwai096a8852011-06-20 12:09:02 +02003538static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003539 /* Enable Boost Volume backdoor */
3540 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003541 /* Enable AOW0 to MW9 */
3542 {0x1, 0xfb8, 0x88},
3543 { }
3544};
Lydia Wang25eaba22009-10-10 19:08:43 +08003545
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003546static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3547{
3548 struct via_spec *spec = codec->spec;
3549 int imux_is_smixer;
3550 unsigned int parm;
3551 unsigned int present;
3552 /* MUX9 (1eh) = stereo mixer */
3553 imux_is_smixer =
3554 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3555 /* inputs */
3556 /* PW 5/6/7 (29h/2ah/2bh) */
3557 parm = AC_PWRST_D3;
3558 set_pin_power_state(codec, 0x29, &parm);
3559 set_pin_power_state(codec, 0x2a, &parm);
3560 set_pin_power_state(codec, 0x2b, &parm);
3561 parm = AC_PWRST_D0;
3562 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3563 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3564 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3565 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3566 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3567
3568 /* outputs */
3569 /* AOW0 (8h)*/
3570 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3571
Lydia Wang118909562011-03-23 17:57:34 +08003572 if (spec->codec_type == VT1802) {
3573 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3574 parm = AC_PWRST_D3;
3575 set_pin_power_state(codec, 0x28, &parm);
3576 snd_hda_codec_write(codec, 0x18, 0,
3577 AC_VERB_SET_POWER_STATE, parm);
3578 snd_hda_codec_write(codec, 0x38, 0,
3579 AC_VERB_SET_POWER_STATE, parm);
3580 } else {
3581 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3582 parm = AC_PWRST_D3;
3583 set_pin_power_state(codec, 0x26, &parm);
3584 snd_hda_codec_write(codec, 0x1c, 0,
3585 AC_VERB_SET_POWER_STATE, parm);
3586 snd_hda_codec_write(codec, 0x37, 0,
3587 AC_VERB_SET_POWER_STATE, parm);
3588 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003589
Lydia Wang118909562011-03-23 17:57:34 +08003590 if (spec->codec_type == VT1802) {
3591 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3592 parm = AC_PWRST_D3;
3593 set_pin_power_state(codec, 0x25, &parm);
3594 snd_hda_codec_write(codec, 0x15, 0,
3595 AC_VERB_SET_POWER_STATE, parm);
3596 snd_hda_codec_write(codec, 0x35, 0,
3597 AC_VERB_SET_POWER_STATE, parm);
3598 } else {
3599 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3600 parm = AC_PWRST_D3;
3601 set_pin_power_state(codec, 0x25, &parm);
3602 snd_hda_codec_write(codec, 0x19, 0,
3603 AC_VERB_SET_POWER_STATE, parm);
3604 snd_hda_codec_write(codec, 0x35, 0,
3605 AC_VERB_SET_POWER_STATE, parm);
3606 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003607
3608 if (spec->hp_independent_mode)
3609 snd_hda_codec_write(codec, 0x9, 0,
3610 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3611
3612 /* Class-D */
3613 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3614 present = snd_hda_jack_detect(codec, 0x25);
3615
3616 parm = AC_PWRST_D3;
3617 set_pin_power_state(codec, 0x24, &parm);
3618 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003619 if (spec->codec_type == VT1802)
3620 snd_hda_codec_write(codec, 0x14, 0,
3621 AC_VERB_SET_POWER_STATE, parm);
3622 else
3623 snd_hda_codec_write(codec, 0x18, 0,
3624 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003625 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3626
3627 /* Mono Out */
3628 present = snd_hda_jack_detect(codec, 0x26);
3629
3630 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003631 if (spec->codec_type == VT1802) {
3632 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3633 snd_hda_codec_write(codec, 0x33, 0,
3634 AC_VERB_SET_POWER_STATE, parm);
3635 snd_hda_codec_write(codec, 0x1c, 0,
3636 AC_VERB_SET_POWER_STATE, parm);
3637 snd_hda_codec_write(codec, 0x3c, 0,
3638 AC_VERB_SET_POWER_STATE, parm);
3639 } else {
3640 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3641 snd_hda_codec_write(codec, 0x31, 0,
3642 AC_VERB_SET_POWER_STATE, parm);
3643 snd_hda_codec_write(codec, 0x17, 0,
3644 AC_VERB_SET_POWER_STATE, parm);
3645 snd_hda_codec_write(codec, 0x3b, 0,
3646 AC_VERB_SET_POWER_STATE, parm);
3647 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003648 /* MW9 (21h) */
3649 if (imux_is_smixer || !is_aa_path_mute(codec))
3650 snd_hda_codec_write(codec, 0x21, 0,
3651 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3652 else
3653 snd_hda_codec_write(codec, 0x21, 0,
3654 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3655}
Lydia Wang25eaba22009-10-10 19:08:43 +08003656
3657/* patch for vt2002P */
3658static int patch_vt2002P(struct hda_codec *codec)
3659{
3660 struct via_spec *spec;
3661 int err;
3662
3663 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003664 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003665 if (spec == NULL)
3666 return -ENOMEM;
3667
Takashi Iwai620e2b22011-06-17 17:19:19 +02003668 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003669 override_mic_boost(codec, 0x2b, 0, 3, 40);
3670 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003671 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003672
Lydia Wang25eaba22009-10-10 19:08:43 +08003673 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003674 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003675 if (err < 0) {
3676 via_free(codec);
3677 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003678 }
3679
Lydia Wang118909562011-03-23 17:57:34 +08003680 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003681 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003682 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003683 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003684
Lydia Wang25eaba22009-10-10 19:08:43 +08003685 codec->patch_ops = via_patch_ops;
3686
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003687 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003688 return 0;
3689}
Lydia Wangab6734e2009-10-10 19:08:46 +08003690
3691/* for vt1812 */
3692
Takashi Iwai096a8852011-06-20 12:09:02 +02003693static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003694 /* Enable Boost Volume backdoor */
3695 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003696 /* Enable AOW0 to MW9 */
3697 {0x1, 0xfb8, 0xa8},
3698 { }
3699};
3700
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003701static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3702{
3703 struct via_spec *spec = codec->spec;
3704 int imux_is_smixer =
3705 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3706 unsigned int parm;
3707 unsigned int present;
3708 /* MUX10 (1eh) = stereo mixer */
3709 imux_is_smixer =
3710 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3711 /* inputs */
3712 /* PW 5/6/7 (29h/2ah/2bh) */
3713 parm = AC_PWRST_D3;
3714 set_pin_power_state(codec, 0x29, &parm);
3715 set_pin_power_state(codec, 0x2a, &parm);
3716 set_pin_power_state(codec, 0x2b, &parm);
3717 parm = AC_PWRST_D0;
3718 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3719 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3720 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3721 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3722 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3723
3724 /* outputs */
3725 /* AOW0 (8h)*/
3726 snd_hda_codec_write(codec, 0x8, 0,
3727 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3728
3729 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3730 parm = AC_PWRST_D3;
3731 set_pin_power_state(codec, 0x28, &parm);
3732 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3733 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3734
3735 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3736 parm = AC_PWRST_D3;
3737 set_pin_power_state(codec, 0x25, &parm);
3738 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3739 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3740 if (spec->hp_independent_mode)
3741 snd_hda_codec_write(codec, 0x9, 0,
3742 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3743
3744 /* Internal Speaker */
3745 /* PW0 (24h), MW0(14h), MUX0(34h) */
3746 present = snd_hda_jack_detect(codec, 0x25);
3747
3748 parm = AC_PWRST_D3;
3749 set_pin_power_state(codec, 0x24, &parm);
3750 if (present) {
3751 snd_hda_codec_write(codec, 0x14, 0,
3752 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3753 snd_hda_codec_write(codec, 0x34, 0,
3754 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3755 } else {
3756 snd_hda_codec_write(codec, 0x14, 0,
3757 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3758 snd_hda_codec_write(codec, 0x34, 0,
3759 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3760 }
3761
3762
3763 /* Mono Out */
3764 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3765 present = snd_hda_jack_detect(codec, 0x28);
3766
3767 parm = AC_PWRST_D3;
3768 set_pin_power_state(codec, 0x31, &parm);
3769 if (present) {
3770 snd_hda_codec_write(codec, 0x1c, 0,
3771 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3772 snd_hda_codec_write(codec, 0x3c, 0,
3773 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3774 snd_hda_codec_write(codec, 0x3e, 0,
3775 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3776 } else {
3777 snd_hda_codec_write(codec, 0x1c, 0,
3778 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3779 snd_hda_codec_write(codec, 0x3c, 0,
3780 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3781 snd_hda_codec_write(codec, 0x3e, 0,
3782 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3783 }
3784
3785 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3786 parm = AC_PWRST_D3;
3787 set_pin_power_state(codec, 0x33, &parm);
3788 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3789 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3790
3791}
Lydia Wangab6734e2009-10-10 19:08:46 +08003792
3793/* patch for vt1812 */
3794static int patch_vt1812(struct hda_codec *codec)
3795{
3796 struct via_spec *spec;
3797 int err;
3798
3799 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003800 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003801 if (spec == NULL)
3802 return -ENOMEM;
3803
Takashi Iwai620e2b22011-06-17 17:19:19 +02003804 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003805 override_mic_boost(codec, 0x2b, 0, 3, 40);
3806 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003807 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003808
Lydia Wangab6734e2009-10-10 19:08:46 +08003809 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003810 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003811 if (err < 0) {
3812 via_free(codec);
3813 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003814 }
3815
Takashi Iwai096a8852011-06-20 12:09:02 +02003816 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003817
Lydia Wangab6734e2009-10-10 19:08:46 +08003818 codec->patch_ops = via_patch_ops;
3819
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003820 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003821 return 0;
3822}
3823
Joseph Chanc577b8a2006-11-29 15:29:40 +01003824/*
3825 * patch entries
3826 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003827static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003828 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3829 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3830 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3831 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3832 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003833 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003834 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003835 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003836 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003837 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003838 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003839 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003840 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003841 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003842 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003843 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003844 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003845 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003846 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003847 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003848 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003849 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003850 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003851 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003852 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003853 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003854 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003855 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003856 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003857 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003858 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003859 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003860 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003861 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003862 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003863 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003864 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003865 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003866 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003867 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003868 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003869 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003870 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003871 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003872 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003873 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003874 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003875 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003876 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003877 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003878 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003879 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003880 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003881 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003882 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003883 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003884 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003885 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003886 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003887 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003888 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003889 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003890 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003891 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003892 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003893 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003894 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003895 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003896 { .id = 0x11060428, .name = "VT1718S",
3897 .patch = patch_vt1718S},
3898 { .id = 0x11064428, .name = "VT1718S",
3899 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003900 { .id = 0x11060441, .name = "VT2020",
3901 .patch = patch_vt1718S},
3902 { .id = 0x11064441, .name = "VT1828S",
3903 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003904 { .id = 0x11060433, .name = "VT1716S",
3905 .patch = patch_vt1716S},
3906 { .id = 0x1106a721, .name = "VT1716S",
3907 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003908 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3909 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003910 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003911 { .id = 0x11060440, .name = "VT1818S",
3912 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003913 { .id = 0x11060446, .name = "VT1802",
3914 .patch = patch_vt2002P},
3915 { .id = 0x11068446, .name = "VT1802",
3916 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003917 {} /* terminator */
3918};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003919
3920MODULE_ALIAS("snd-hda-codec-id:1106*");
3921
3922static struct hda_codec_preset_list via_list = {
3923 .preset = snd_hda_preset_via,
3924 .owner = THIS_MODULE,
3925};
3926
3927MODULE_LICENSE("GPL");
3928MODULE_DESCRIPTION("VIA HD-audio codec");
3929
3930static int __init patch_via_init(void)
3931{
3932 return snd_hda_add_codec_preset(&via_list);
3933}
3934
3935static void __exit patch_via_exit(void)
3936{
3937 snd_hda_delete_codec_preset(&via_list);
3938}
3939
3940module_init(patch_via_init)
3941module_exit(patch_via_exit)