blob: 417d62ad3b96c89bd5f05f23e7c41e3edddb59c0 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010052#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080053#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010054#include "hda_codec.h"
55#include "hda_local.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010056
Joseph Chanc577b8a2006-11-29 15:29:40 +010057/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080058#define VT1708_HP_PIN_NID 0x20
59#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010060
Harald Welted7426322008-09-15 22:43:23 +080061enum VIA_HDA_CODEC {
62 UNKNOWN = -1,
63 VT1708,
64 VT1709_10CH,
65 VT1709_6CH,
66 VT1708B_8CH,
67 VT1708B_4CH,
68 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080069 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080070 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080071 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080072 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080073 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080074 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080075 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080076 CODEC_TYPES,
77};
78
Lydia Wang118909562011-03-23 17:57:34 +080079#define VT2002P_COMPATIBLE(spec) \
80 ((spec)->codec_type == VT2002P ||\
81 (spec)->codec_type == VT1812 ||\
82 (spec)->codec_type == VT1802)
83
Takashi Iwai8e3679d2011-06-21 09:01:36 +020084#define MAX_NID_PATH_DEPTH 5
85
Takashi Iwai09a9ad692011-06-21 15:57:44 +020086/* output-path: DAC -> ... -> pin
87 * idx[] contains the source index number of the next widget;
88 * e.g. idx[0] is the index of the DAC selected by path[1] widget
89 * multi[] indicates whether it's a selector widget with multi-connectors
90 * (i.e. the connection selection is mandatory)
91 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
92 */
Takashi Iwai4a796162011-06-17 17:53:38 +020093struct nid_path {
94 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020095 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad692011-06-21 15:57:44 +020096 unsigned char idx[MAX_NID_PATH_DEPTH];
97 unsigned char multi[MAX_NID_PATH_DEPTH];
98 unsigned int vol_ctl;
99 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200100};
101
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200102/* input-path */
103struct via_input {
104 hda_nid_t pin; /* input-pin or aa-mix */
105 int adc_idx; /* ADC index to be used */
106 int mux_idx; /* MUX index (if any) */
107 const char *label; /* input-source label */
108};
109
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200110#define VIA_MAX_ADCS 3
111
Takashi Iwai3b607e32011-07-18 16:54:40 +0200112enum {
113 STREAM_MULTI_OUT = (1 << 0),
114 STREAM_INDEP_HP = (1 << 1),
115};
116
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800117struct via_spec {
118 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200119 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800120 unsigned int num_mixers;
121
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200122 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800123 unsigned int num_iverbs;
124
Takashi Iwai82673bc2011-06-17 16:24:21 +0200125 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200126 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200127 const struct hda_pcm_stream *stream_analog_playback;
128 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800129
Takashi Iwai82673bc2011-06-17 16:24:21 +0200130 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200131 const struct hda_pcm_stream *stream_digital_playback;
132 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800133
134 /* playback */
135 struct hda_multi_out multiout;
136 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200137 hda_nid_t hp_dac_nid;
Takashi Iwai3214b962011-07-18 12:49:25 +0200138 hda_nid_t speaker_dac_nid;
139 int hp_indep_shared; /* indep HP-DAC is shared with side ch */
Takashi Iwai3b607e32011-07-18 16:54:40 +0200140 int opened_streams; /* STREAM_* bits */
141 int active_streams; /* STREAM_* bits */
Takashi Iwai3214b962011-07-18 12:49:25 +0200142 int aamix_mode; /* loopback is enabled for output-path? */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800143
Takashi Iwai3214b962011-07-18 12:49:25 +0200144 /* Output-paths:
145 * There are different output-paths depending on the setup.
146 * out_path, hp_path and speaker_path are primary paths. If both
147 * direct DAC and aa-loopback routes are available, these contain
148 * the former paths. Meanwhile *_mix_path contain the paths with
149 * loopback mixer. (Since the loopback is only for front channel,
150 * no out_mix_path for surround channels.)
151 * The HP output has another path, hp_indep_path, which is used in
152 * the independent-HP mode.
153 */
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200154 struct nid_path out_path[HDA_SIDE + 1];
Takashi Iwai3214b962011-07-18 12:49:25 +0200155 struct nid_path out_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200156 struct nid_path hp_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200157 struct nid_path hp_mix_path;
158 struct nid_path hp_indep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200159 struct nid_path speaker_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200160 struct nid_path speaker_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200161
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800162 /* capture */
163 unsigned int num_adc_nids;
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200164 hda_nid_t adc_nids[VIA_MAX_ADCS];
165 hda_nid_t mux_nids[VIA_MAX_ADCS];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200166 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800167 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800168
169 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200170 bool dyn_adc_switch;
171 int num_inputs;
172 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200173 unsigned int cur_mux[VIA_MAX_ADCS];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800174
Takashi Iwai3b607e32011-07-18 16:54:40 +0200175 /* dynamic DAC switching */
176 unsigned int cur_dac_stream_tag;
177 unsigned int cur_dac_format;
178 unsigned int cur_hp_stream_tag;
179 unsigned int cur_hp_format;
180
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200181 /* dynamic ADC switching */
182 hda_nid_t cur_adc;
183 unsigned int cur_adc_stream_tag;
184 unsigned int cur_adc_format;
185
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800186 /* PCM information */
187 struct hda_pcm pcm_rec[3];
188
189 /* dynamic controls, init_verbs and input_mux */
190 struct auto_pin_cfg autocfg;
191 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800192 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
193
194 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800195 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800196 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200197 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800198 enum VIA_HDA_CODEC codec_type;
199
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200200 /* smart51 setup */
201 unsigned int smart51_nums;
202 hda_nid_t smart51_pins[2];
203 int smart51_idxs[2];
204 const char *smart51_labels[2];
205 unsigned int smart51_enabled;
206
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800207 /* work to check hp jack state */
208 struct hda_codec *codec;
209 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200210 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800211 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800212
213 void (*set_widgets_power_state)(struct hda_codec *codec);
214
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800215 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200216 int num_loopbacks;
217 struct hda_amp_list loopback_list[8];
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200218
219 /* bind capture-volume */
220 struct hda_bind_ctls *bind_cap_vol;
221 struct hda_bind_ctls *bind_cap_sw;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200222
223 struct mutex config_mutex;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800224};
225
Lydia Wang0341ccd2011-03-22 16:25:03 +0800226static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100227static struct via_spec * via_new_spec(struct hda_codec *codec)
228{
229 struct via_spec *spec;
230
231 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
232 if (spec == NULL)
233 return NULL;
234
Takashi Iwai3b607e32011-07-18 16:54:40 +0200235 mutex_init(&spec->config_mutex);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100236 codec->spec = spec;
237 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800238 spec->codec_type = get_codec_type(codec);
239 /* VT1708BCE & VT1708S are almost same */
240 if (spec->codec_type == VT1708BCE)
241 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100242 return spec;
243}
244
Lydia Wang744ff5f2009-10-10 19:07:26 +0800245static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800246{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800247 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800248 u16 ven_id = vendor_id >> 16;
249 u16 dev_id = vendor_id & 0xffff;
250 enum VIA_HDA_CODEC codec_type;
251
252 /* get codec type */
253 if (ven_id != 0x1106)
254 codec_type = UNKNOWN;
255 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
256 codec_type = VT1708;
257 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
258 codec_type = VT1709_10CH;
259 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
260 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800261 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800262 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800263 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
264 codec_type = VT1708BCE;
265 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800266 codec_type = VT1708B_4CH;
267 else if ((dev_id & 0xfff) == 0x397
268 && (dev_id >> 12) < 8)
269 codec_type = VT1708S;
270 else if ((dev_id & 0xfff) == 0x398
271 && (dev_id >> 12) < 8)
272 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800273 else if ((dev_id & 0xfff) == 0x428
274 && (dev_id >> 12) < 8)
275 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800276 else if (dev_id == 0x0433 || dev_id == 0xa721)
277 codec_type = VT1716S;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +0800278 else if (dev_id == 0x0441 || dev_id == 0x4441)
279 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800280 else if (dev_id == 0x0438 || dev_id == 0x4438)
281 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800282 else if (dev_id == 0x0448)
283 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800284 else if (dev_id == 0x0440)
285 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800286 else if ((dev_id & 0xfff) == 0x446)
287 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800288 else
289 codec_type = UNKNOWN;
290 return codec_type;
291};
292
Lydia Wangec7e7e42011-03-24 12:43:44 +0800293#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800294#define VIA_HP_EVENT 0x01
295#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200296#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800297
Joseph Chanc577b8a2006-11-29 15:29:40 +0100298enum {
299 VIA_CTL_WIDGET_VOL,
300 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800301 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100302};
303
Takashi Iwaiada509e2011-06-20 15:40:19 +0200304static void analog_low_current_mode(struct hda_codec *codec);
305static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800306
307static void vt1708_start_hp_work(struct via_spec *spec)
308{
309 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
310 return;
311 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200312 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800313 if (!delayed_work_pending(&spec->vt1708_hp_work))
314 schedule_delayed_work(&spec->vt1708_hp_work,
315 msecs_to_jiffies(100));
316}
317
318static void vt1708_stop_hp_work(struct via_spec *spec)
319{
320 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
321 return;
322 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
323 && !is_aa_path_mute(spec->codec))
324 return;
325 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200326 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100327 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800328}
Lydia Wangf5271102009-10-10 19:07:35 +0800329
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800330static void set_widgets_power_state(struct hda_codec *codec)
331{
332 struct via_spec *spec = codec->spec;
333 if (spec->set_widgets_power_state)
334 spec->set_widgets_power_state(codec);
335}
Lydia Wang25eaba22009-10-10 19:08:43 +0800336
Lydia Wangf5271102009-10-10 19:07:35 +0800337static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
338 struct snd_ctl_elem_value *ucontrol)
339{
340 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
341 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
342
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800343 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200344 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800345 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
346 if (is_aa_path_mute(codec))
347 vt1708_start_hp_work(codec->spec);
348 else
349 vt1708_stop_hp_work(codec->spec);
350 }
Lydia Wangf5271102009-10-10 19:07:35 +0800351 return change;
352}
353
354/* modify .put = snd_hda_mixer_amp_switch_put */
355#define ANALOG_INPUT_MUTE \
356 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
357 .name = NULL, \
358 .index = 0, \
359 .info = snd_hda_mixer_amp_switch_info, \
360 .get = snd_hda_mixer_amp_switch_get, \
361 .put = analog_input_switch_put, \
362 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
363
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200364static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100365 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
366 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800367 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100368};
369
Lydia Wangab6734e2009-10-10 19:08:46 +0800370
Joseph Chanc577b8a2006-11-29 15:29:40 +0100371/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200372static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
373 const struct snd_kcontrol_new *tmpl,
374 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100375{
376 struct snd_kcontrol_new *knew;
377
Takashi Iwai603c4012008-07-30 15:01:44 +0200378 snd_array_init(&spec->kctls, sizeof(*knew), 32);
379 knew = snd_array_new(&spec->kctls);
380 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200381 return NULL;
382 *knew = *tmpl;
383 if (!name)
384 name = tmpl->name;
385 if (name) {
386 knew->name = kstrdup(name, GFP_KERNEL);
387 if (!knew->name)
388 return NULL;
389 }
390 return knew;
391}
392
393static int __via_add_control(struct via_spec *spec, int type, const char *name,
394 int idx, unsigned long val)
395{
396 struct snd_kcontrol_new *knew;
397
398 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
399 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100400 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200401 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100402 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100403 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100404 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100405 return 0;
406}
407
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200408#define via_add_control(spec, type, name, val) \
409 __via_add_control(spec, type, name, 0, val)
410
Takashi Iwai291c9e32011-06-17 16:15:26 +0200411#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100412
Takashi Iwai603c4012008-07-30 15:01:44 +0200413static void via_free_kctls(struct hda_codec *codec)
414{
415 struct via_spec *spec = codec->spec;
416
417 if (spec->kctls.list) {
418 struct snd_kcontrol_new *kctl = spec->kctls.list;
419 int i;
420 for (i = 0; i < spec->kctls.used; i++)
421 kfree(kctl[i].name);
422 }
423 snd_array_free(&spec->kctls);
424}
425
Joseph Chanc577b8a2006-11-29 15:29:40 +0100426/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800427static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200428 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100429{
430 char name[32];
431 int err;
432
433 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200434 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100435 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
436 if (err < 0)
437 return err;
438 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200439 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100440 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
441 if (err < 0)
442 return err;
443 return 0;
444}
445
Takashi Iwai5d417622011-06-20 11:32:27 +0200446#define get_connection_index(codec, mux, nid) \
Takashi Iwai8d087c72011-06-28 12:45:47 +0200447 snd_hda_get_conn_index(codec, mux, nid, 0)
Takashi Iwai5d417622011-06-20 11:32:27 +0200448
Takashi Iwai8df2a312011-06-21 11:48:29 +0200449static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
450 unsigned int mask)
451{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200452 unsigned int caps;
453 if (!nid)
454 return false;
455 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200456 if (dir == HDA_INPUT)
457 caps &= AC_WCAP_IN_AMP;
458 else
459 caps &= AC_WCAP_OUT_AMP;
460 if (!caps)
461 return false;
462 if (query_amp_caps(codec, nid, dir) & mask)
463 return true;
464 return false;
465}
466
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200467#define have_mute(codec, nid, dir) \
468 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200469
Lydia Wangd69607b2011-07-08 14:02:52 +0800470/* enable/disable the output-route mixers */
471static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
Takashi Iwai3214b962011-07-18 12:49:25 +0200472 hda_nid_t mix_nid, int idx, bool enable)
Lydia Wangd69607b2011-07-08 14:02:52 +0800473{
474 int i, num, val;
Lydia Wangd69607b2011-07-08 14:02:52 +0800475
476 if (!path)
477 return;
478 num = snd_hda_get_conn_list(codec, mix_nid, NULL);
Lydia Wangd69607b2011-07-08 14:02:52 +0800479 for (i = 0; i < num; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +0200480 if (i == idx)
481 val = AMP_IN_UNMUTE(i);
482 else
483 val = AMP_IN_MUTE(i);
Lydia Wangd69607b2011-07-08 14:02:52 +0800484 snd_hda_codec_write(codec, mix_nid, 0,
485 AC_VERB_SET_AMP_GAIN_MUTE, val);
486 }
487}
488
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200489/* enable/disable the output-route */
490static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
491 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200492{
Lydia Wangd69607b2011-07-08 14:02:52 +0800493 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200494 int i;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200495 for (i = 0; i < path->depth; i++) {
496 hda_nid_t src, dst;
497 int idx = path->idx[i];
498 src = path->path[i];
499 if (i < path->depth - 1)
500 dst = path->path[i + 1];
501 else
502 dst = 0;
503 if (enable && path->multi[i])
504 snd_hda_codec_write(codec, dst, 0,
505 AC_VERB_SET_CONNECT_SEL, idx);
Takashi Iwai3214b962011-07-18 12:49:25 +0200506 if (!force && (dst == spec->aa_mix_nid))
Lydia Wange5e14682011-07-01 10:55:07 +0800507 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +0200508 if (have_mute(codec, dst, HDA_INPUT))
509 activate_output_mix(codec, path, dst, idx, enable);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200510 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
511 continue;
512 if (have_mute(codec, src, HDA_OUTPUT)) {
513 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
514 snd_hda_codec_write(codec, src, 0,
515 AC_VERB_SET_AMP_GAIN_MUTE, val);
516 }
517 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200518}
519
520/* set the given pin as output */
521static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
522 int pin_type)
523{
524 if (!pin)
525 return;
526 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
527 pin_type);
528 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
529 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200530 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100531}
532
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200533static void via_auto_init_output(struct hda_codec *codec,
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200534 struct nid_path *path, int pin_type)
Takashi Iwai5d417622011-06-20 11:32:27 +0200535{
Takashi Iwai5d417622011-06-20 11:32:27 +0200536 unsigned int caps;
Lydia Wangd69607b2011-07-08 14:02:52 +0800537 hda_nid_t pin;
Takashi Iwai5d417622011-06-20 11:32:27 +0200538
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200539 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200540 return;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200541 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200542
543 init_output_pin(codec, pin, pin_type);
544 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
545 if (caps & AC_AMPCAP_MUTE) {
546 unsigned int val;
547 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
548 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
549 AMP_OUT_MUTE | val);
550 }
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200551 activate_output_path(codec, path, true, true); /* force on */
Takashi Iwai5d417622011-06-20 11:32:27 +0200552}
553
Joseph Chanc577b8a2006-11-29 15:29:40 +0100554static void via_auto_init_multi_out(struct hda_codec *codec)
555{
556 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200557 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100558 int i;
559
Takashi Iwai3214b962011-07-18 12:49:25 +0200560 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) {
561 path = &spec->out_path[i];
562 if (!i && spec->aamix_mode && spec->out_mix_path.depth)
563 path = &spec->out_mix_path;
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200564 via_auto_init_output(codec, path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200565 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100566}
567
Takashi Iwai020066d2011-07-21 13:45:56 +0200568/* deactivate the inactive headphone-paths */
569static void deactivate_hp_paths(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100570{
571 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200572 int shared = spec->hp_indep_shared;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100573
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200574 if (spec->hp_independent_mode) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200575 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200576 activate_output_path(codec, &spec->hp_mix_path, false, false);
577 if (shared)
578 activate_output_path(codec, &spec->out_path[shared],
579 false, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200580 } else if (spec->aamix_mode || !spec->hp_path.depth) {
581 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200582 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200583 } else {
Takashi Iwai020066d2011-07-21 13:45:56 +0200584 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200585 activate_output_path(codec, &spec->hp_mix_path, false, false);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200586 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100587}
588
Takashi Iwai020066d2011-07-21 13:45:56 +0200589static void via_auto_init_hp_out(struct hda_codec *codec)
590{
591 struct via_spec *spec = codec->spec;
592
593 if (!spec->hp_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200594 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200595 return;
596 }
597 deactivate_hp_paths(codec);
598 if (spec->hp_independent_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200599 via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200600 else if (spec->aamix_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200601 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200602 else
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200603 via_auto_init_output(codec, &spec->hp_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200604}
605
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200606static void via_auto_init_speaker_out(struct hda_codec *codec)
607{
608 struct via_spec *spec = codec->spec;
609
Takashi Iwai3214b962011-07-18 12:49:25 +0200610 if (!spec->autocfg.speaker_outs)
611 return;
612 if (!spec->speaker_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200613 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200614 return;
615 }
616 if (!spec->aamix_mode) {
617 activate_output_path(codec, &spec->speaker_mix_path,
618 false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200619 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200620 } else {
621 activate_output_path(codec, &spec->speaker_path, false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200622 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200623 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200624}
625
Takashi Iwaif4a78282011-06-17 18:46:48 +0200626static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200627static void via_hp_automute(struct hda_codec *codec);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200628
Joseph Chanc577b8a2006-11-29 15:29:40 +0100629static void via_auto_init_analog_input(struct hda_codec *codec)
630{
631 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200632 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200633 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200634 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200635 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100636
Takashi Iwai096a8852011-06-20 12:09:02 +0200637 /* init ADCs */
638 for (i = 0; i < spec->num_adc_nids; i++) {
639 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
640 AC_VERB_SET_AMP_GAIN_MUTE,
641 AMP_IN_UNMUTE(0));
642 }
643
644 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200645 for (i = 0; i < cfg->num_inputs; i++) {
646 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200647 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200648 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100649 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200650 ctl = PIN_VREF50;
651 else
652 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100653 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200654 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100655 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200656
657 /* init input-src */
658 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200659 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
660 if (spec->mux_nids[adc_idx]) {
661 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
662 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
663 AC_VERB_SET_CONNECT_SEL,
664 mux_idx);
665 }
666 if (spec->dyn_adc_switch)
667 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200668 }
669
670 /* init aa-mixer */
671 if (!spec->aa_mix_nid)
672 return;
673 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
674 ARRAY_SIZE(conn));
675 for (i = 0; i < num_conns; i++) {
676 unsigned int caps = get_wcaps(codec, conn[i]);
677 if (get_wcaps_type(caps) == AC_WID_PIN)
678 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
679 AC_VERB_SET_AMP_GAIN_MUTE,
680 AMP_IN_MUTE(i));
681 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100682}
Lydia Wangf5271102009-10-10 19:07:35 +0800683
684static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
685 unsigned int *affected_parm)
686{
687 unsigned parm;
688 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
689 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
690 >> AC_DEFCFG_MISC_SHIFT
691 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800692 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200693 unsigned present = 0;
694
695 no_presence |= spec->no_pin_power_ctl;
696 if (!no_presence)
697 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200698 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800699 || ((no_presence || present)
700 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800701 *affected_parm = AC_PWRST_D0; /* if it's connected */
702 parm = AC_PWRST_D0;
703 } else
704 parm = AC_PWRST_D3;
705
706 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
707}
708
Takashi Iwai24088a52011-06-17 16:59:21 +0200709static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
710 struct snd_ctl_elem_info *uinfo)
711{
712 static const char * const texts[] = {
713 "Disabled", "Enabled"
714 };
715
716 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
717 uinfo->count = 1;
718 uinfo->value.enumerated.items = 2;
719 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
720 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
721 strcpy(uinfo->value.enumerated.name,
722 texts[uinfo->value.enumerated.item]);
723 return 0;
724}
725
726static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
727 struct snd_ctl_elem_value *ucontrol)
728{
729 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
730 struct via_spec *spec = codec->spec;
731 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
732 return 0;
733}
734
735static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
736 struct snd_ctl_elem_value *ucontrol)
737{
738 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
739 struct via_spec *spec = codec->spec;
740 unsigned int val = !ucontrol->value.enumerated.item[0];
741
742 if (val == spec->no_pin_power_ctl)
743 return 0;
744 spec->no_pin_power_ctl = val;
745 set_widgets_power_state(codec);
746 return 1;
747}
748
749static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
750 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
751 .name = "Dynamic Power-Control",
752 .info = via_pin_power_ctl_info,
753 .get = via_pin_power_ctl_get,
754 .put = via_pin_power_ctl_put,
755};
756
757
Harald Welte0aa62ae2008-09-09 15:58:27 +0800758static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
759 struct snd_ctl_elem_info *uinfo)
760{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200761 static const char * const texts[] = { "OFF", "ON" };
762
763 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
764 uinfo->count = 1;
765 uinfo->value.enumerated.items = 2;
766 if (uinfo->value.enumerated.item >= 2)
767 uinfo->value.enumerated.item = 1;
768 strcpy(uinfo->value.enumerated.name,
769 texts[uinfo->value.enumerated.item]);
770 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800771}
772
773static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
774 struct snd_ctl_elem_value *ucontrol)
775{
776 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800777 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800778
Takashi Iwaiece8d042011-06-19 16:24:21 +0200779 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800780 return 0;
781}
782
Takashi Iwai3b607e32011-07-18 16:54:40 +0200783/* adjust spec->multiout setup according to the current flags */
784static void setup_playback_multi_pcm(struct via_spec *spec)
785{
786 const struct auto_pin_cfg *cfg = &spec->autocfg;
787 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
788 spec->multiout.hp_nid = 0;
789 if (!spec->hp_independent_mode) {
790 if (!spec->hp_indep_shared)
791 spec->multiout.hp_nid = spec->hp_dac_nid;
792 } else {
793 if (spec->hp_indep_shared)
794 spec->multiout.num_dacs = cfg->line_outs - 1;
795 }
796}
797
798/* update DAC setups according to indep-HP switch;
799 * this function is called only when indep-HP is modified
800 */
801static void switch_indep_hp_dacs(struct hda_codec *codec)
802{
803 struct via_spec *spec = codec->spec;
804 int shared = spec->hp_indep_shared;
805 hda_nid_t shared_dac, hp_dac;
806
807 if (!spec->opened_streams)
808 return;
809
810 shared_dac = shared ? spec->multiout.dac_nids[shared] : 0;
811 hp_dac = spec->hp_dac_nid;
812 if (spec->hp_independent_mode) {
813 /* switch to indep-HP mode */
814 if (spec->active_streams & STREAM_MULTI_OUT) {
815 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
816 __snd_hda_codec_cleanup_stream(codec, shared_dac, 1);
817 }
818 if (spec->active_streams & STREAM_INDEP_HP)
819 snd_hda_codec_setup_stream(codec, hp_dac,
820 spec->cur_hp_stream_tag, 0,
821 spec->cur_hp_format);
822 } else {
823 /* back to HP or shared-DAC */
824 if (spec->active_streams & STREAM_INDEP_HP)
825 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
826 if (spec->active_streams & STREAM_MULTI_OUT) {
827 hda_nid_t dac;
828 int ch;
829 if (shared_dac) { /* reset mutli-ch DAC */
830 dac = shared_dac;
831 ch = shared * 2;
832 } else { /* reset HP DAC */
833 dac = hp_dac;
834 ch = 0;
835 }
836 snd_hda_codec_setup_stream(codec, dac,
837 spec->cur_dac_stream_tag, ch,
838 spec->cur_dac_format);
839 }
840 }
841 setup_playback_multi_pcm(spec);
842}
843
Harald Welte0aa62ae2008-09-09 15:58:27 +0800844static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
845 struct snd_ctl_elem_value *ucontrol)
846{
847 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
848 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200849 int cur, shared;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200850
Takashi Iwai3b607e32011-07-18 16:54:40 +0200851 mutex_lock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200852 cur = !!ucontrol->value.enumerated.item[0];
Takashi Iwai3b607e32011-07-18 16:54:40 +0200853 if (spec->hp_independent_mode == cur) {
854 mutex_unlock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200855 return 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200856 }
Takashi Iwai25250502011-06-30 17:24:47 +0200857 spec->hp_independent_mode = cur;
Takashi Iwai3214b962011-07-18 12:49:25 +0200858 shared = spec->hp_indep_shared;
Takashi Iwai020066d2011-07-21 13:45:56 +0200859 deactivate_hp_paths(codec);
860 if (cur)
861 activate_output_path(codec, &spec->hp_indep_path, true, false);
862 else {
Takashi Iwai3214b962011-07-18 12:49:25 +0200863 if (shared)
864 activate_output_path(codec, &spec->out_path[shared],
Takashi Iwai25250502011-06-30 17:24:47 +0200865 true, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200866 if (spec->aamix_mode || !spec->hp_path.depth)
867 activate_output_path(codec, &spec->hp_mix_path,
868 true, false);
869 else
870 activate_output_path(codec, &spec->hp_path,
871 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200872 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800873
Takashi Iwai3b607e32011-07-18 16:54:40 +0200874 switch_indep_hp_dacs(codec);
875 mutex_unlock(&spec->config_mutex);
876
Lydia Wangce0e5a92011-03-22 16:22:37 +0800877 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800878 set_widgets_power_state(codec);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200879 via_hp_automute(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200880 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800881}
882
Takashi Iwaiece8d042011-06-19 16:24:21 +0200883static const struct snd_kcontrol_new via_hp_mixer = {
884 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
885 .name = "Independent HP",
886 .info = via_independent_hp_info,
887 .get = via_independent_hp_get,
888 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800889};
890
Takashi Iwai3d83e572010-04-14 14:36:23 +0200891static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100892{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200893 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100894 struct snd_kcontrol_new *knew;
895 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100896
Takashi Iwaiece8d042011-06-19 16:24:21 +0200897 nid = spec->autocfg.hp_pins[0];
898 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200899 if (knew == NULL)
900 return -ENOMEM;
901
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100902 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100903
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100904 return 0;
905}
906
Lydia Wang1564b282009-10-10 19:07:52 +0800907static void notify_aa_path_ctls(struct hda_codec *codec)
908{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200909 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800910 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800911
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200912 for (i = 0; i < spec->smart51_nums; i++) {
913 struct snd_kcontrol *ctl;
914 struct snd_ctl_elem_id id;
915 memset(&id, 0, sizeof(id));
916 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
917 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800918 ctl = snd_hda_find_mixer_ctl(codec, id.name);
919 if (ctl)
920 snd_ctl_notify(codec->bus->card,
921 SNDRV_CTL_EVENT_MASK_VALUE,
922 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800923 }
924}
925
926static void mute_aa_path(struct hda_codec *codec, int mute)
927{
928 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200929 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800930 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200931
Lydia Wang1564b282009-10-10 19:07:52 +0800932 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200933 for (i = 0; i < spec->smart51_nums; i++) {
934 if (spec->smart51_idxs[i] < 0)
935 continue;
936 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
937 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800938 HDA_AMP_MUTE, val);
939 }
940}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200941
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200942static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
943{
944 struct via_spec *spec = codec->spec;
945 int i;
946
947 for (i = 0; i < spec->smart51_nums; i++)
948 if (spec->smart51_pins[i] == pin)
949 return true;
950 return false;
951}
952
Lydia Wang1564b282009-10-10 19:07:52 +0800953static int via_smart51_get(struct snd_kcontrol *kcontrol,
954 struct snd_ctl_elem_value *ucontrol)
955{
956 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
957 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800958
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200959 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800960 return 0;
961}
962
963static int via_smart51_put(struct snd_kcontrol *kcontrol,
964 struct snd_ctl_elem_value *ucontrol)
965{
966 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
967 struct via_spec *spec = codec->spec;
968 int out_in = *ucontrol->value.integer.value
969 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800970 int i;
971
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200972 for (i = 0; i < spec->smart51_nums; i++) {
973 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200974 unsigned int parm;
975
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200976 parm = snd_hda_codec_read(codec, nid, 0,
977 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
978 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
979 parm |= out_in;
980 snd_hda_codec_write(codec, nid, 0,
981 AC_VERB_SET_PIN_WIDGET_CONTROL,
982 parm);
983 if (out_in == AC_PINCTL_OUT_EN) {
984 mute_aa_path(codec, 1);
985 notify_aa_path_ctls(codec);
986 }
Lydia Wang1564b282009-10-10 19:07:52 +0800987 }
988 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800989 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800990 return 1;
991}
992
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200993static const struct snd_kcontrol_new via_smart51_mixer = {
994 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
995 .name = "Smart 5.1",
996 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200997 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200998 .get = via_smart51_get,
999 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +08001000};
1001
Takashi Iwaif4a78282011-06-17 18:46:48 +02001002static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001003{
Takashi Iwaif4a78282011-06-17 18:46:48 +02001004 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001005
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001006 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +08001007 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001008 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001009 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001010 return 0;
1011}
1012
Takashi Iwaiada509e2011-06-20 15:40:19 +02001013/* check AA path's mute status */
1014static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001015{
Lydia Wangf5271102009-10-10 19:07:35 +08001016 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001017 const struct hda_amp_list *p;
1018 int i, ch, v;
1019
1020 for (i = 0; i < spec->num_loopbacks; i++) {
1021 p = &spec->loopback_list[i];
1022 for (ch = 0; ch < 2; ch++) {
1023 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
1024 p->idx);
1025 if (!(v & HDA_AMP_MUTE) && v > 0)
1026 return false;
Lydia Wangf5271102009-10-10 19:07:35 +08001027 }
1028 }
Takashi Iwaiada509e2011-06-20 15:40:19 +02001029 return true;
Lydia Wangf5271102009-10-10 19:07:35 +08001030}
1031
1032/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +02001033static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001034{
1035 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001036 bool enable;
1037 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +08001038
Takashi Iwai3b607e32011-07-18 16:54:40 +02001039 enable = is_aa_path_mute(codec) && (spec->opened_streams != 0);
Lydia Wangf5271102009-10-10 19:07:35 +08001040
1041 /* decide low current mode's verb & parameter */
1042 switch (spec->codec_type) {
1043 case VT1708B_8CH:
1044 case VT1708B_4CH:
1045 verb = 0xf70;
1046 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1047 break;
1048 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001049 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001050 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001051 verb = 0xf73;
1052 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1053 break;
1054 case VT1702:
1055 verb = 0xf73;
1056 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1057 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001058 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001059 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +08001060 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +08001061 verb = 0xf93;
1062 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1063 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001064 default:
1065 return; /* other codecs are not supported */
1066 }
1067 /* send verb */
1068 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1069}
1070
Joseph Chanc577b8a2006-11-29 15:29:40 +01001071/*
1072 * generic initialization of ADC, input mixers and output mixers
1073 */
Takashi Iwai096a8852011-06-20 12:09:02 +02001074static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001075 /* power down jack detect function */
1076 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001077 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001078};
1079
Takashi Iwai3b607e32011-07-18 16:54:40 +02001080static void set_stream_open(struct hda_codec *codec, int bit, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001081{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001082 struct via_spec *spec = codec->spec;
1083
1084 if (active)
Takashi Iwai3b607e32011-07-18 16:54:40 +02001085 spec->opened_streams |= bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001086 else
Takashi Iwai3b607e32011-07-18 16:54:40 +02001087 spec->opened_streams &= ~bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001088 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001089}
1090
Takashi Iwaiece8d042011-06-19 16:24:21 +02001091static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001092 struct hda_codec *codec,
1093 struct snd_pcm_substream *substream)
1094{
1095 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001096 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001097 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001098
Takashi Iwai25250502011-06-30 17:24:47 +02001099 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
Takashi Iwai25250502011-06-30 17:24:47 +02001100 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001101 set_stream_open(codec, STREAM_MULTI_OUT, true);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001102 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1103 hinfo);
1104 if (err < 0) {
Takashi Iwai3b607e32011-07-18 16:54:40 +02001105 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001106 return err;
1107 }
1108 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001109}
1110
Takashi Iwaiece8d042011-06-19 16:24:21 +02001111static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001112 struct hda_codec *codec,
1113 struct snd_pcm_substream *substream)
1114{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001115 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001116 return 0;
1117}
1118
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001119static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1120 struct hda_codec *codec,
1121 struct snd_pcm_substream *substream)
1122{
1123 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001124
Takashi Iwaiece8d042011-06-19 16:24:21 +02001125 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001126 return -EINVAL;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001127 set_stream_open(codec, STREAM_INDEP_HP, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001128 return 0;
1129}
1130
1131static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1132 struct hda_codec *codec,
1133 struct snd_pcm_substream *substream)
1134{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001135 set_stream_open(codec, STREAM_INDEP_HP, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001136 return 0;
1137}
1138
1139static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1140 struct hda_codec *codec,
1141 unsigned int stream_tag,
1142 unsigned int format,
1143 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001144{
1145 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001146
Takashi Iwai3b607e32011-07-18 16:54:40 +02001147 mutex_lock(&spec->config_mutex);
1148 setup_playback_multi_pcm(spec);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001149 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1150 format, substream);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001151 /* remember for dynamic DAC switch with indep-HP */
1152 spec->active_streams |= STREAM_MULTI_OUT;
1153 spec->cur_dac_stream_tag = stream_tag;
1154 spec->cur_dac_format = format;
1155 mutex_unlock(&spec->config_mutex);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001156 vt1708_start_hp_work(spec);
1157 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001158}
1159
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001160static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1161 struct hda_codec *codec,
1162 unsigned int stream_tag,
1163 unsigned int format,
1164 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001165{
1166 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001167
Takashi Iwai3b607e32011-07-18 16:54:40 +02001168 mutex_lock(&spec->config_mutex);
1169 if (spec->hp_independent_mode)
1170 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1171 stream_tag, 0, format);
1172 spec->active_streams |= STREAM_INDEP_HP;
1173 spec->cur_hp_stream_tag = stream_tag;
1174 spec->cur_hp_format = format;
1175 mutex_unlock(&spec->config_mutex);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001176 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001177 return 0;
1178}
1179
1180static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1181 struct hda_codec *codec,
1182 struct snd_pcm_substream *substream)
1183{
1184 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001185
Takashi Iwai3b607e32011-07-18 16:54:40 +02001186 mutex_lock(&spec->config_mutex);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001187 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001188 spec->active_streams &= ~STREAM_MULTI_OUT;
1189 mutex_unlock(&spec->config_mutex);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001190 vt1708_stop_hp_work(spec);
1191 return 0;
1192}
1193
1194static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1195 struct hda_codec *codec,
1196 struct snd_pcm_substream *substream)
1197{
1198 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001199
Takashi Iwai3b607e32011-07-18 16:54:40 +02001200 mutex_lock(&spec->config_mutex);
1201 if (spec->hp_independent_mode)
1202 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
1203 spec->active_streams &= ~STREAM_INDEP_HP;
1204 mutex_unlock(&spec->config_mutex);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001205 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001206 return 0;
1207}
1208
Joseph Chanc577b8a2006-11-29 15:29:40 +01001209/*
1210 * Digital out
1211 */
1212static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1213 struct hda_codec *codec,
1214 struct snd_pcm_substream *substream)
1215{
1216 struct via_spec *spec = codec->spec;
1217 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1218}
1219
1220static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1221 struct hda_codec *codec,
1222 struct snd_pcm_substream *substream)
1223{
1224 struct via_spec *spec = codec->spec;
1225 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1226}
1227
Harald Welte5691ec72008-09-15 22:42:26 +08001228static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001229 struct hda_codec *codec,
1230 unsigned int stream_tag,
1231 unsigned int format,
1232 struct snd_pcm_substream *substream)
1233{
1234 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001235 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1236 stream_tag, format, substream);
1237}
Harald Welte5691ec72008-09-15 22:42:26 +08001238
Takashi Iwai9da29272009-05-07 16:31:14 +02001239static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1240 struct hda_codec *codec,
1241 struct snd_pcm_substream *substream)
1242{
1243 struct via_spec *spec = codec->spec;
1244 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001245 return 0;
1246}
1247
Joseph Chanc577b8a2006-11-29 15:29:40 +01001248/*
1249 * Analog capture
1250 */
1251static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1252 struct hda_codec *codec,
1253 unsigned int stream_tag,
1254 unsigned int format,
1255 struct snd_pcm_substream *substream)
1256{
1257 struct via_spec *spec = codec->spec;
1258
1259 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1260 stream_tag, 0, format);
1261 return 0;
1262}
1263
1264static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1265 struct hda_codec *codec,
1266 struct snd_pcm_substream *substream)
1267{
1268 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001269 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001270 return 0;
1271}
1272
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001273/* analog capture with dynamic ADC switching */
1274static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1275 struct hda_codec *codec,
1276 unsigned int stream_tag,
1277 unsigned int format,
1278 struct snd_pcm_substream *substream)
1279{
1280 struct via_spec *spec = codec->spec;
1281 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1282
Takashi Iwai3b607e32011-07-18 16:54:40 +02001283 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001284 spec->cur_adc = spec->adc_nids[adc_idx];
1285 spec->cur_adc_stream_tag = stream_tag;
1286 spec->cur_adc_format = format;
1287 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001288 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001289 return 0;
1290}
1291
1292static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1293 struct hda_codec *codec,
1294 struct snd_pcm_substream *substream)
1295{
1296 struct via_spec *spec = codec->spec;
1297
Takashi Iwai3b607e32011-07-18 16:54:40 +02001298 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001299 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1300 spec->cur_adc = 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001301 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001302 return 0;
1303}
1304
1305/* re-setup the stream if running; called from input-src put */
1306static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1307{
1308 struct via_spec *spec = codec->spec;
1309 int adc_idx = spec->inputs[cur].adc_idx;
1310 hda_nid_t adc = spec->adc_nids[adc_idx];
Takashi Iwai3b607e32011-07-18 16:54:40 +02001311 bool ret = false;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001312
Takashi Iwai3b607e32011-07-18 16:54:40 +02001313 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001314 if (spec->cur_adc && spec->cur_adc != adc) {
1315 /* stream is running, let's swap the current ADC */
1316 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1317 spec->cur_adc = adc;
1318 snd_hda_codec_setup_stream(codec, adc,
1319 spec->cur_adc_stream_tag, 0,
1320 spec->cur_adc_format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001321 ret = true;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001322 }
Takashi Iwai3b607e32011-07-18 16:54:40 +02001323 mutex_unlock(&spec->config_mutex);
1324 return ret;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001325}
1326
Takashi Iwai9af74212011-06-18 16:17:45 +02001327static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001328 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001329 .channels_min = 2,
1330 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001331 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001332 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001333 .open = via_playback_multi_pcm_open,
1334 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001335 .prepare = via_playback_multi_pcm_prepare,
1336 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001337 },
1338};
1339
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001340static const struct hda_pcm_stream via_pcm_hp_playback = {
1341 .substreams = 1,
1342 .channels_min = 2,
1343 .channels_max = 2,
1344 /* NID is set in via_build_pcms */
1345 .ops = {
1346 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001347 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001348 .prepare = via_playback_hp_pcm_prepare,
1349 .cleanup = via_playback_hp_pcm_cleanup
1350 },
1351};
1352
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001353static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001354 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001355 .channels_min = 2,
1356 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001357 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001358 /* We got noisy outputs on the right channel on VT1708 when
1359 * 24bit samples are used. Until any workaround is found,
1360 * disable the 24bit format, so far.
1361 */
1362 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1363 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001364 .open = via_playback_multi_pcm_open,
1365 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001366 .prepare = via_playback_multi_pcm_prepare,
1367 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001368 },
1369};
1370
Takashi Iwai9af74212011-06-18 16:17:45 +02001371static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001372 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001373 .channels_min = 2,
1374 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001375 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001376 .ops = {
1377 .prepare = via_capture_pcm_prepare,
1378 .cleanup = via_capture_pcm_cleanup
1379 },
1380};
1381
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001382static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1383 .substreams = 1,
1384 .channels_min = 2,
1385 .channels_max = 2,
1386 /* NID is set in via_build_pcms */
1387 .ops = {
1388 .prepare = via_dyn_adc_capture_pcm_prepare,
1389 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1390 },
1391};
1392
Takashi Iwai9af74212011-06-18 16:17:45 +02001393static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001394 .substreams = 1,
1395 .channels_min = 2,
1396 .channels_max = 2,
1397 /* NID is set in via_build_pcms */
1398 .ops = {
1399 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001400 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001401 .prepare = via_dig_playback_pcm_prepare,
1402 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001403 },
1404};
1405
Takashi Iwai9af74212011-06-18 16:17:45 +02001406static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001407 .substreams = 1,
1408 .channels_min = 2,
1409 .channels_max = 2,
1410};
1411
Takashi Iwai370bafb2011-06-20 12:47:45 +02001412/*
1413 * slave controls for virtual master
1414 */
1415static const char * const via_slave_vols[] = {
1416 "Front Playback Volume",
1417 "Surround Playback Volume",
1418 "Center Playback Volume",
1419 "LFE Playback Volume",
1420 "Side Playback Volume",
1421 "Headphone Playback Volume",
1422 "Speaker Playback Volume",
1423 NULL,
1424};
1425
1426static const char * const via_slave_sws[] = {
1427 "Front Playback Switch",
1428 "Surround Playback Switch",
1429 "Center Playback Switch",
1430 "LFE Playback Switch",
1431 "Side Playback Switch",
1432 "Headphone Playback Switch",
1433 "Speaker Playback Switch",
1434 NULL,
1435};
1436
Joseph Chanc577b8a2006-11-29 15:29:40 +01001437static int via_build_controls(struct hda_codec *codec)
1438{
1439 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001440 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001441 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001442
Takashi Iwai24088a52011-06-17 16:59:21 +02001443 if (spec->set_widgets_power_state)
1444 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1445 return -ENOMEM;
1446
Joseph Chanc577b8a2006-11-29 15:29:40 +01001447 for (i = 0; i < spec->num_mixers; i++) {
1448 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1449 if (err < 0)
1450 return err;
1451 }
1452
1453 if (spec->multiout.dig_out_nid) {
1454 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001455 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001456 spec->multiout.dig_out_nid);
1457 if (err < 0)
1458 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001459 err = snd_hda_create_spdif_share_sw(codec,
1460 &spec->multiout);
1461 if (err < 0)
1462 return err;
1463 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001464 }
1465 if (spec->dig_in_nid) {
1466 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1467 if (err < 0)
1468 return err;
1469 }
Lydia Wang17314372009-10-10 19:07:37 +08001470
Takashi Iwai370bafb2011-06-20 12:47:45 +02001471 /* if we have no master control, let's create it */
1472 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1473 unsigned int vmaster_tlv[4];
1474 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1475 HDA_OUTPUT, vmaster_tlv);
1476 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1477 vmaster_tlv, via_slave_vols);
1478 if (err < 0)
1479 return err;
1480 }
1481 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1482 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1483 NULL, via_slave_sws);
1484 if (err < 0)
1485 return err;
1486 }
1487
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001488 /* assign Capture Source enums to NID */
1489 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1490 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001491 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001492 if (err < 0)
1493 return err;
1494 }
1495
Lydia Wang17314372009-10-10 19:07:37 +08001496 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001497 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001498 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001499
Takashi Iwai603c4012008-07-30 15:01:44 +02001500 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001501 return 0;
1502}
1503
1504static int via_build_pcms(struct hda_codec *codec)
1505{
1506 struct via_spec *spec = codec->spec;
1507 struct hda_pcm *info = spec->pcm_rec;
1508
Takashi Iwaia5973102011-09-28 16:43:36 +02001509 codec->num_pcms = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001510 codec->pcm_info = info;
1511
Takashi Iwaia5973102011-09-28 16:43:36 +02001512 if (spec->multiout.num_dacs || spec->num_adc_nids) {
1513 snprintf(spec->stream_name_analog,
1514 sizeof(spec->stream_name_analog),
1515 "%s Analog", codec->chip_name);
1516 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001517
Takashi Iwaia5973102011-09-28 16:43:36 +02001518 if (spec->multiout.num_dacs) {
1519 if (!spec->stream_analog_playback)
1520 spec->stream_analog_playback =
1521 &via_pcm_analog_playback;
1522 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1523 *spec->stream_analog_playback;
1524 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1525 spec->multiout.dac_nids[0];
1526 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1527 spec->multiout.max_channels;
1528 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001529
Takashi Iwaia5973102011-09-28 16:43:36 +02001530 if (!spec->stream_analog_capture) {
1531 if (spec->dyn_adc_switch)
1532 spec->stream_analog_capture =
1533 &via_pcm_dyn_adc_analog_capture;
1534 else
1535 spec->stream_analog_capture =
1536 &via_pcm_analog_capture;
1537 }
1538 if (spec->num_adc_nids) {
1539 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1540 *spec->stream_analog_capture;
1541 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1542 spec->adc_nids[0];
1543 if (!spec->dyn_adc_switch)
1544 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1545 spec->num_adc_nids;
1546 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001547 codec->num_pcms++;
1548 info++;
Takashi Iwaia5973102011-09-28 16:43:36 +02001549 }
1550
1551 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
Takashi Iwai82673bc2011-06-17 16:24:21 +02001552 snprintf(spec->stream_name_digital,
1553 sizeof(spec->stream_name_digital),
1554 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001555 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001556 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001557 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001558 if (!spec->stream_digital_playback)
1559 spec->stream_digital_playback =
1560 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001561 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001562 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001563 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1564 spec->multiout.dig_out_nid;
1565 }
1566 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001567 if (!spec->stream_digital_capture)
1568 spec->stream_digital_capture =
1569 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001570 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001571 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001572 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1573 spec->dig_in_nid;
1574 }
Takashi Iwaia5973102011-09-28 16:43:36 +02001575 codec->num_pcms++;
1576 info++;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001577 }
1578
Takashi Iwaiece8d042011-06-19 16:24:21 +02001579 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001580 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1581 "%s HP", codec->chip_name);
1582 info->name = spec->stream_name_hp;
1583 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1584 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001585 spec->hp_dac_nid;
Takashi Iwaia5973102011-09-28 16:43:36 +02001586 codec->num_pcms++;
1587 info++;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001588 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001589 return 0;
1590}
1591
1592static void via_free(struct hda_codec *codec)
1593{
1594 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001595
1596 if (!spec)
1597 return;
1598
Takashi Iwai603c4012008-07-30 15:01:44 +02001599 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001600 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001601 kfree(spec->bind_cap_vol);
1602 kfree(spec->bind_cap_sw);
1603 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001604}
1605
Takashi Iwai64be2852011-06-17 16:51:39 +02001606/* mute/unmute outputs */
1607static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1608 hda_nid_t *pins, bool mute)
1609{
1610 int i;
Takashi Iwai94994732011-07-11 11:36:44 +02001611 for (i = 0; i < num_pins; i++) {
1612 unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
1613 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1614 if (parm & AC_PINCTL_IN_EN)
1615 continue;
1616 if (mute)
1617 parm &= ~AC_PINCTL_OUT_EN;
1618 else
1619 parm |= AC_PINCTL_OUT_EN;
Takashi Iwai64be2852011-06-17 16:51:39 +02001620 snd_hda_codec_write(codec, pins[i], 0,
Takashi Iwai94994732011-07-11 11:36:44 +02001621 AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
1622 }
Takashi Iwai64be2852011-06-17 16:51:39 +02001623}
1624
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001625/* mute internal speaker if line-out is plugged */
1626static void via_line_automute(struct hda_codec *codec, int present)
1627{
1628 struct via_spec *spec = codec->spec;
1629
1630 if (!spec->autocfg.speaker_outs)
1631 return;
1632 if (!present)
1633 present = snd_hda_jack_detect(codec,
1634 spec->autocfg.line_out_pins[0]);
1635 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1636 spec->autocfg.speaker_pins,
1637 present);
1638}
1639
Harald Welte69e52a82008-09-09 15:57:32 +08001640/* mute internal speaker if HP is plugged */
1641static void via_hp_automute(struct hda_codec *codec)
1642{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001643 int present = 0;
Takashi Iwai6e969d92011-07-11 11:28:13 +02001644 int nums;
Harald Welte69e52a82008-09-09 15:57:32 +08001645 struct via_spec *spec = codec->spec;
1646
Takashi Iwai6e969d92011-07-11 11:28:13 +02001647 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0])
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001648 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai6e969d92011-07-11 11:28:13 +02001649
1650 if (spec->smart51_enabled)
1651 nums = spec->autocfg.line_outs + spec->smart51_nums;
1652 else
1653 nums = spec->autocfg.line_outs;
1654 toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
1655
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001656 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001657}
1658
Harald Welte69e52a82008-09-09 15:57:32 +08001659static void via_gpio_control(struct hda_codec *codec)
1660{
1661 unsigned int gpio_data;
1662 unsigned int vol_counter;
1663 unsigned int vol;
1664 unsigned int master_vol;
1665
1666 struct via_spec *spec = codec->spec;
1667
1668 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1669 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1670
1671 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1672 0xF84, 0) & 0x3F0000) >> 16;
1673
1674 vol = vol_counter & 0x1F;
1675 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1676 AC_VERB_GET_AMP_GAIN_MUTE,
1677 AC_AMP_GET_INPUT);
1678
1679 if (gpio_data == 0x02) {
1680 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001681 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1682 AC_VERB_SET_PIN_WIDGET_CONTROL,
1683 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001684 if (vol_counter & 0x20) {
1685 /* decrease volume */
1686 if (vol > master_vol)
1687 vol = master_vol;
1688 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1689 0, HDA_AMP_VOLMASK,
1690 master_vol-vol);
1691 } else {
1692 /* increase volume */
1693 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1694 HDA_AMP_VOLMASK,
1695 ((master_vol+vol) > 0x2A) ? 0x2A :
1696 (master_vol+vol));
1697 }
1698 } else if (!(gpio_data & 0x02)) {
1699 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001700 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1701 AC_VERB_SET_PIN_WIDGET_CONTROL,
1702 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001703 }
1704}
1705
1706/* unsolicited event for jack sensing */
1707static void via_unsol_event(struct hda_codec *codec,
1708 unsigned int res)
1709{
1710 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001711
Lydia Wanga34df192009-10-10 19:08:01 +08001712 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001713 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001714
1715 res &= ~VIA_JACK_EVENT;
1716
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001717 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001718 via_hp_automute(codec);
1719 else if (res == VIA_GPIO_EVENT)
1720 via_gpio_control(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001721}
1722
Takashi Iwai2a439522011-07-26 09:52:50 +02001723#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001724static int via_suspend(struct hda_codec *codec, pm_message_t state)
1725{
1726 struct via_spec *spec = codec->spec;
1727 vt1708_stop_hp_work(spec);
1728 return 0;
1729}
1730#endif
1731
Takashi Iwaicb53c622007-08-10 17:21:45 +02001732#ifdef CONFIG_SND_HDA_POWER_SAVE
1733static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1734{
1735 struct via_spec *spec = codec->spec;
1736 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1737}
1738#endif
1739
Joseph Chanc577b8a2006-11-29 15:29:40 +01001740/*
1741 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001742
1743static int via_init(struct hda_codec *codec);
1744
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001745static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001746 .build_controls = via_build_controls,
1747 .build_pcms = via_build_pcms,
1748 .init = via_init,
1749 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001750 .unsol_event = via_unsol_event,
Takashi Iwai2a439522011-07-26 09:52:50 +02001751#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001752 .suspend = via_suspend,
1753#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001754#ifdef CONFIG_SND_HDA_POWER_SAVE
1755 .check_power_status = via_check_power_status,
1756#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001757};
1758
Takashi Iwai4a796162011-06-17 17:53:38 +02001759static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001760{
Takashi Iwai4a796162011-06-17 17:53:38 +02001761 struct via_spec *spec = codec->spec;
1762 int i;
1763
1764 for (i = 0; i < spec->multiout.num_dacs; i++) {
1765 if (spec->multiout.dac_nids[i] == dac)
1766 return false;
1767 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001768 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001769 return false;
1770 return true;
1771}
1772
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001773static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001774 hda_nid_t target_dac, int with_aa_mix,
1775 struct nid_path *path, int depth)
Takashi Iwai4a796162011-06-17 17:53:38 +02001776{
Takashi Iwai3214b962011-07-18 12:49:25 +02001777 struct via_spec *spec = codec->spec;
Takashi Iwai4a796162011-06-17 17:53:38 +02001778 hda_nid_t conn[8];
1779 int i, nums;
1780
Takashi Iwai3214b962011-07-18 12:49:25 +02001781 if (nid == spec->aa_mix_nid) {
1782 if (!with_aa_mix)
1783 return false;
1784 with_aa_mix = 2; /* mark aa-mix is included */
1785 }
1786
Takashi Iwai4a796162011-06-17 17:53:38 +02001787 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1788 for (i = 0; i < nums; i++) {
1789 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1790 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001791 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1792 /* aa-mix is requested but not included? */
1793 if (!(spec->aa_mix_nid && with_aa_mix == 1))
1794 goto found;
1795 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001796 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001797 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001798 return false;
1799 for (i = 0; i < nums; i++) {
1800 unsigned int type;
1801 type = get_wcaps_type(get_wcaps(codec, conn[i]));
Takashi Iwai3214b962011-07-18 12:49:25 +02001802 if (type == AC_WID_AUD_OUT)
Takashi Iwai4a796162011-06-17 17:53:38 +02001803 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001804 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai3214b962011-07-18 12:49:25 +02001805 with_aa_mix, path, depth + 1))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001806 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001807 }
1808 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001809
1810 found:
1811 path->path[path->depth] = conn[i];
1812 path->idx[path->depth] = i;
1813 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1814 path->multi[path->depth] = 1;
1815 path->depth++;
1816 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001817}
1818
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001819static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001820 hda_nid_t target_dac, int with_aa_mix,
1821 struct nid_path *path)
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001822{
Takashi Iwai3214b962011-07-18 12:49:25 +02001823 if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001824 path->path[path->depth] = nid;
1825 path->depth++;
Takashi Iwai3214b962011-07-18 12:49:25 +02001826 snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
1827 path->depth, path->path[0], path->path[1],
1828 path->path[2], path->path[3], path->path[4]);
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001829 return true;
1830 }
1831 return false;
1832}
1833
Takashi Iwai4a796162011-06-17 17:53:38 +02001834static int via_auto_fill_dac_nids(struct hda_codec *codec)
1835{
1836 struct via_spec *spec = codec->spec;
1837 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001838 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001839 hda_nid_t nid;
1840
Joseph Chanc577b8a2006-11-29 15:29:40 +01001841 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001842 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001843 for (i = 0; i < cfg->line_outs; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001844 hda_nid_t dac = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001845 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001846 if (!nid)
1847 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001848 if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
1849 dac = spec->out_path[i].path[0];
1850 if (!i && parse_output_path(codec, nid, dac, 1,
1851 &spec->out_mix_path))
1852 dac = spec->out_mix_path.path[0];
1853 if (dac) {
1854 spec->private_dac_nids[i] = dac;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001855 dac_num++;
1856 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001857 }
Takashi Iwai3214b962011-07-18 12:49:25 +02001858 if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
1859 spec->out_path[0] = spec->out_mix_path;
1860 spec->out_mix_path.depth = 0;
1861 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001862 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001863 return 0;
1864}
1865
Takashi Iwai4a796162011-06-17 17:53:38 +02001866static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001867 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001868{
Takashi Iwai4a796162011-06-17 17:53:38 +02001869 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001870 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001871 hda_nid_t dac, pin, sel, nid;
1872 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001873
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001874 dac = check_dac ? path->path[0] : 0;
1875 pin = path->path[path->depth - 1];
1876 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001877
Takashi Iwai8df2a312011-06-21 11:48:29 +02001878 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001879 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001880 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001881 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001882 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1883 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001884 else
1885 nid = 0;
1886 if (nid) {
1887 sprintf(name, "%s Playback Volume", pfx);
1888 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001889 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001890 if (err < 0)
1891 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001892 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001893 }
1894
Takashi Iwai8df2a312011-06-21 11:48:29 +02001895 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001896 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001897 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001898 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001899 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1900 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001901 else
1902 nid = 0;
1903 if (nid) {
1904 sprintf(name, "%s Playback Switch", pfx);
1905 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1906 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1907 if (err < 0)
1908 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001909 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001910 }
1911 return 0;
1912}
1913
Takashi Iwaif4a78282011-06-17 18:46:48 +02001914static void mangle_smart51(struct hda_codec *codec)
1915{
1916 struct via_spec *spec = codec->spec;
1917 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001918 struct auto_pin_cfg_item *ins = cfg->inputs;
1919 int i, j, nums, attr;
1920 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001921
Takashi Iwai0f98c242011-06-21 12:51:33 +02001922 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1923 nums = 0;
1924 for (i = 0; i < cfg->num_inputs; i++) {
1925 unsigned int def;
1926 if (ins[i].type > AUTO_PIN_LINE_IN)
1927 continue;
1928 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1929 if (snd_hda_get_input_pin_attr(def) != attr)
1930 continue;
1931 for (j = 0; j < nums; j++)
1932 if (ins[pins[j]].type < ins[i].type) {
1933 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001934 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001935 break;
1936 }
1937 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001938 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001939 }
1940 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001941 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001942 for (i = 0; i < nums; i++) {
1943 hda_nid_t pin = ins[pins[i]].pin;
1944 spec->smart51_pins[spec->smart51_nums++] = pin;
1945 cfg->line_out_pins[cfg->line_outs++] = pin;
1946 if (cfg->line_outs == 3)
1947 break;
1948 }
1949 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001950 }
1951}
1952
Takashi Iwai020066d2011-07-21 13:45:56 +02001953static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src)
1954{
1955 dst->vol_ctl = src->vol_ctl;
1956 dst->mute_ctl = src->mute_ctl;
1957}
1958
Takashi Iwai4a796162011-06-17 17:53:38 +02001959/* add playback controls from the parsed DAC table */
1960static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1961{
1962 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001963 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai3214b962011-07-18 12:49:25 +02001964 struct nid_path *path;
Takashi Iwaiea734962011-01-17 11:29:34 +01001965 static const char * const chname[4] = {
1966 "Front", "Surround", "C/LFE", "Side"
1967 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001968 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001969 int old_line_outs;
1970
1971 /* check smart51 */
1972 old_line_outs = cfg->line_outs;
1973 if (cfg->line_outs == 1)
1974 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001975
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001976 err = via_auto_fill_dac_nids(codec);
1977 if (err < 0)
1978 return err;
1979
Lydia Wang5c9a5612011-07-08 14:03:43 +08001980 if (spec->multiout.num_dacs < 3) {
1981 spec->smart51_nums = 0;
1982 cfg->line_outs = old_line_outs;
1983 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001984 for (i = 0; i < cfg->line_outs; i++) {
1985 hda_nid_t pin, dac;
1986 pin = cfg->line_out_pins[i];
1987 dac = spec->multiout.dac_nids[i];
1988 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001989 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001990 path = spec->out_path + i;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001991 if (i == HDA_CLFE) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001992 err = create_ch_ctls(codec, "Center", 1, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001993 if (err < 0)
1994 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02001995 err = create_ch_ctls(codec, "LFE", 2, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001996 if (err < 0)
1997 return err;
1998 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001999 const char *pfx = chname[i];
2000 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
2001 cfg->line_outs == 1)
2002 pfx = "Speaker";
Takashi Iwai3214b962011-07-18 12:49:25 +02002003 err = create_ch_ctls(codec, pfx, 3, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002004 if (err < 0)
2005 return err;
2006 }
Takashi Iwai020066d2011-07-21 13:45:56 +02002007 if (path != spec->out_path + i)
2008 copy_path_mixer_ctls(&spec->out_path[i], path);
2009 if (path == spec->out_path && spec->out_mix_path.depth)
2010 copy_path_mixer_ctls(&spec->out_mix_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002011 }
2012
Takashi Iwai4a796162011-06-17 17:53:38 +02002013 idx = get_connection_index(codec, spec->aa_mix_nid,
2014 spec->multiout.dac_nids[0]);
2015 if (idx >= 0) {
2016 /* add control to mixer */
Takashi Iwai3214b962011-07-18 12:49:25 +02002017 const char *name;
2018 name = spec->out_mix_path.depth ?
2019 "PCM Loopback Playback Volume" : "PCM Playback Volume";
2020 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002021 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2022 idx, HDA_INPUT));
2023 if (err < 0)
2024 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002025 name = spec->out_mix_path.depth ?
2026 "PCM Loopback Playback Switch" : "PCM Playback Switch";
2027 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002028 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2029 idx, HDA_INPUT));
2030 if (err < 0)
2031 return err;
2032 }
2033
Takashi Iwaif4a78282011-06-17 18:46:48 +02002034 cfg->line_outs = old_line_outs;
2035
Joseph Chanc577b8a2006-11-29 15:29:40 +01002036 return 0;
2037}
2038
Takashi Iwai4a796162011-06-17 17:53:38 +02002039static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002040{
Takashi Iwai4a796162011-06-17 17:53:38 +02002041 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002042 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002043 bool check_dac;
Takashi Iwai3214b962011-07-18 12:49:25 +02002044 int i, err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002045
2046 if (!pin)
2047 return 0;
2048
Takashi Iwai3214b962011-07-18 12:49:25 +02002049 if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
2050 for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
2051 if (i < spec->multiout.num_dacs &&
2052 parse_output_path(codec, pin,
2053 spec->multiout.dac_nids[i], 0,
2054 &spec->hp_indep_path)) {
2055 spec->hp_indep_shared = i;
2056 break;
2057 }
2058 }
Takashi Iwai25250502011-06-30 17:24:47 +02002059 }
Takashi Iwai3214b962011-07-18 12:49:25 +02002060 if (spec->hp_indep_path.depth) {
2061 spec->hp_dac_nid = spec->hp_indep_path.path[0];
2062 if (!spec->hp_indep_shared)
2063 spec->hp_path = spec->hp_indep_path;
2064 }
2065 /* optionally check front-path w/o AA-mix */
2066 if (!spec->hp_path.depth)
2067 parse_output_path(codec, pin,
2068 spec->multiout.dac_nids[HDA_FRONT], 0,
2069 &spec->hp_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002070
Takashi Iwaiece8d042011-06-19 16:24:21 +02002071 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai3214b962011-07-18 12:49:25 +02002072 1, &spec->hp_mix_path) && !spec->hp_path.depth)
Takashi Iwaiece8d042011-06-19 16:24:21 +02002073 return 0;
2074
Takashi Iwai3214b962011-07-18 12:49:25 +02002075 if (spec->hp_path.depth) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002076 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002077 check_dac = true;
2078 } else {
Takashi Iwai3214b962011-07-18 12:49:25 +02002079 path = &spec->hp_mix_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002080 check_dac = false;
2081 }
2082 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002083 if (err < 0)
2084 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002085 if (check_dac)
2086 copy_path_mixer_ctls(&spec->hp_mix_path, path);
2087 else
2088 copy_path_mixer_ctls(&spec->hp_path, path);
2089 if (spec->hp_indep_path.depth)
2090 copy_path_mixer_ctls(&spec->hp_indep_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002091 return 0;
2092}
2093
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002094static int via_auto_create_speaker_ctls(struct hda_codec *codec)
2095{
2096 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +02002097 struct nid_path *path;
2098 bool check_dac;
Wang Shaoyan81c0a782011-08-05 18:51:29 +08002099 hda_nid_t pin, dac = 0;
Takashi Iwai3214b962011-07-18 12:49:25 +02002100 int err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002101
2102 pin = spec->autocfg.speaker_pins[0];
2103 if (!spec->autocfg.speaker_outs || !pin)
2104 return 0;
2105
Takashi Iwai3214b962011-07-18 12:49:25 +02002106 if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02002107 dac = spec->speaker_path.path[0];
Takashi Iwai3214b962011-07-18 12:49:25 +02002108 if (!dac)
2109 parse_output_path(codec, pin,
2110 spec->multiout.dac_nids[HDA_FRONT], 0,
2111 &spec->speaker_path);
2112 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
2113 1, &spec->speaker_mix_path) && !dac)
2114 return 0;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002115
Takashi Iwai3214b962011-07-18 12:49:25 +02002116 /* no AA-path for front? */
2117 if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
2118 dac = 0;
2119
2120 spec->speaker_dac_nid = dac;
2121 spec->multiout.extra_out_nid[0] = dac;
2122 if (dac) {
2123 path = &spec->speaker_path;
2124 check_dac = true;
2125 } else {
2126 path = &spec->speaker_mix_path;
2127 check_dac = false;
2128 }
2129 err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
2130 if (err < 0)
2131 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002132 if (check_dac)
2133 copy_path_mixer_ctls(&spec->speaker_mix_path, path);
2134 else
2135 copy_path_mixer_ctls(&spec->speaker_path, path);
Takashi Iwai3214b962011-07-18 12:49:25 +02002136 return 0;
2137}
2138
2139#define via_aamix_ctl_info via_pin_power_ctl_info
2140
2141static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol,
2142 struct snd_ctl_elem_value *ucontrol)
2143{
2144 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2145 struct via_spec *spec = codec->spec;
2146 ucontrol->value.enumerated.item[0] = spec->aamix_mode;
2147 return 0;
2148}
2149
2150static void update_aamix_paths(struct hda_codec *codec, int do_mix,
2151 struct nid_path *nomix, struct nid_path *mix)
2152{
2153 if (do_mix) {
2154 activate_output_path(codec, nomix, false, false);
2155 activate_output_path(codec, mix, true, false);
2156 } else {
2157 activate_output_path(codec, mix, false, false);
2158 activate_output_path(codec, nomix, true, false);
2159 }
2160}
2161
2162static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol,
2163 struct snd_ctl_elem_value *ucontrol)
2164{
2165 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2166 struct via_spec *spec = codec->spec;
2167 unsigned int val = ucontrol->value.enumerated.item[0];
2168
2169 if (val == spec->aamix_mode)
2170 return 0;
2171 spec->aamix_mode = val;
2172 /* update front path */
2173 update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
2174 /* update HP path */
2175 if (!spec->hp_independent_mode) {
2176 update_aamix_paths(codec, val, &spec->hp_path,
2177 &spec->hp_mix_path);
2178 }
2179 /* update speaker path */
2180 update_aamix_paths(codec, val, &spec->speaker_path,
2181 &spec->speaker_mix_path);
2182 return 1;
2183}
2184
2185static const struct snd_kcontrol_new via_aamix_ctl_enum = {
2186 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2187 .name = "Loopback Mixing",
2188 .info = via_aamix_ctl_info,
2189 .get = via_aamix_ctl_get,
2190 .put = via_aamix_ctl_put,
2191};
2192
2193static int via_auto_create_loopback_switch(struct hda_codec *codec)
2194{
2195 struct via_spec *spec = codec->spec;
2196
2197 if (!spec->aa_mix_nid || !spec->out_mix_path.depth)
2198 return 0; /* no loopback switching available */
2199 if (!via_clone_control(spec, &via_aamix_ctl_enum))
2200 return -ENOMEM;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002201 return 0;
2202}
2203
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002204/* look for ADCs */
2205static int via_fill_adcs(struct hda_codec *codec)
2206{
2207 struct via_spec *spec = codec->spec;
2208 hda_nid_t nid = codec->start_nid;
2209 int i;
2210
2211 for (i = 0; i < codec->num_nodes; i++, nid++) {
2212 unsigned int wcaps = get_wcaps(codec, nid);
2213 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2214 continue;
2215 if (wcaps & AC_WCAP_DIGITAL)
2216 continue;
2217 if (!(wcaps & AC_WCAP_CONN_LIST))
2218 continue;
2219 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2220 return -ENOMEM;
2221 spec->adc_nids[spec->num_adc_nids++] = nid;
2222 }
2223 return 0;
2224}
2225
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002226/* input-src control */
2227static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2228 struct snd_ctl_elem_info *uinfo)
2229{
2230 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2231 struct via_spec *spec = codec->spec;
2232
2233 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2234 uinfo->count = 1;
2235 uinfo->value.enumerated.items = spec->num_inputs;
2236 if (uinfo->value.enumerated.item >= spec->num_inputs)
2237 uinfo->value.enumerated.item = spec->num_inputs - 1;
2238 strcpy(uinfo->value.enumerated.name,
2239 spec->inputs[uinfo->value.enumerated.item].label);
2240 return 0;
2241}
2242
2243static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2244 struct snd_ctl_elem_value *ucontrol)
2245{
2246 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2247 struct via_spec *spec = codec->spec;
2248 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2249
2250 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2251 return 0;
2252}
2253
2254static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2255 struct snd_ctl_elem_value *ucontrol)
2256{
2257 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2258 struct via_spec *spec = codec->spec;
2259 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2260 hda_nid_t mux;
2261 int cur;
2262
2263 cur = ucontrol->value.enumerated.item[0];
2264 if (cur < 0 || cur >= spec->num_inputs)
2265 return -EINVAL;
2266 if (spec->cur_mux[idx] == cur)
2267 return 0;
2268 spec->cur_mux[idx] = cur;
2269 if (spec->dyn_adc_switch) {
2270 int adc_idx = spec->inputs[cur].adc_idx;
2271 mux = spec->mux_nids[adc_idx];
2272 via_dyn_adc_pcm_resetup(codec, cur);
2273 } else {
2274 mux = spec->mux_nids[idx];
2275 if (snd_BUG_ON(!mux))
2276 return -EINVAL;
2277 }
2278
2279 if (mux) {
2280 /* switch to D0 beofre change index */
2281 if (snd_hda_codec_read(codec, mux, 0,
2282 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2283 snd_hda_codec_write(codec, mux, 0,
2284 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2285 snd_hda_codec_write(codec, mux, 0,
2286 AC_VERB_SET_CONNECT_SEL,
2287 spec->inputs[cur].mux_idx);
2288 }
2289
2290 /* update jack power state */
2291 set_widgets_power_state(codec);
2292 return 0;
2293}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002294
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002295static const struct snd_kcontrol_new via_input_src_ctl = {
2296 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2297 /* The multiple "Capture Source" controls confuse alsamixer
2298 * So call somewhat different..
2299 */
2300 /* .name = "Capture Source", */
2301 .name = "Input Source",
2302 .info = via_mux_enum_info,
2303 .get = via_mux_enum_get,
2304 .put = via_mux_enum_put,
2305};
2306
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002307static int create_input_src_ctls(struct hda_codec *codec, int count)
2308{
2309 struct via_spec *spec = codec->spec;
2310 struct snd_kcontrol_new *knew;
2311
2312 if (spec->num_inputs <= 1 || !count)
2313 return 0; /* no need for single src */
2314
2315 knew = via_clone_control(spec, &via_input_src_ctl);
2316 if (!knew)
2317 return -ENOMEM;
2318 knew->count = count;
2319 return 0;
2320}
2321
2322/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002323static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2324{
2325 struct hda_amp_list *list;
2326
2327 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2328 return;
2329 list = spec->loopback_list + spec->num_loopbacks;
2330 list->nid = mix;
2331 list->dir = HDA_INPUT;
2332 list->idx = idx;
2333 spec->num_loopbacks++;
2334 spec->loopback.amplist = spec->loopback_list;
2335}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002336
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002337static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002338 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002339{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002340 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002341}
2342
2343/* add the input-route to the given pin */
2344static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002345{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002346 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002347 int c, idx;
2348
2349 spec->inputs[spec->num_inputs].adc_idx = -1;
2350 spec->inputs[spec->num_inputs].pin = pin;
2351 for (c = 0; c < spec->num_adc_nids; c++) {
2352 if (spec->mux_nids[c]) {
2353 idx = get_connection_index(codec, spec->mux_nids[c],
2354 pin);
2355 if (idx < 0)
2356 continue;
2357 spec->inputs[spec->num_inputs].mux_idx = idx;
2358 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002359 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002360 continue;
2361 }
2362 spec->inputs[spec->num_inputs].adc_idx = c;
2363 /* Can primary ADC satisfy all inputs? */
2364 if (!spec->dyn_adc_switch &&
2365 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2366 snd_printd(KERN_INFO
2367 "via: dynamic ADC switching enabled\n");
2368 spec->dyn_adc_switch = 1;
2369 }
2370 return true;
2371 }
2372 return false;
2373}
2374
2375static int get_mux_nids(struct hda_codec *codec);
2376
2377/* parse input-routes; fill ADCs, MUXs and input-src entries */
2378static int parse_analog_inputs(struct hda_codec *codec)
2379{
2380 struct via_spec *spec = codec->spec;
2381 const struct auto_pin_cfg *cfg = &spec->autocfg;
2382 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002383
2384 err = via_fill_adcs(codec);
2385 if (err < 0)
2386 return err;
2387 err = get_mux_nids(codec);
2388 if (err < 0)
2389 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002390
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002391 /* fill all input-routes */
2392 for (i = 0; i < cfg->num_inputs; i++) {
2393 if (add_input_route(codec, cfg->inputs[i].pin))
2394 spec->inputs[spec->num_inputs++].label =
2395 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002396 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002397
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002398 /* check for internal loopback recording */
2399 if (spec->aa_mix_nid &&
2400 add_input_route(codec, spec->aa_mix_nid))
2401 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2402
2403 return 0;
2404}
2405
2406/* create analog-loopback volume/switch controls */
2407static int create_loopback_ctls(struct hda_codec *codec)
2408{
2409 struct via_spec *spec = codec->spec;
2410 const struct auto_pin_cfg *cfg = &spec->autocfg;
2411 const char *prev_label = NULL;
2412 int type_idx = 0;
2413 int i, j, err, idx;
2414
2415 if (!spec->aa_mix_nid)
2416 return 0;
2417
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002418 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002419 hda_nid_t pin = cfg->inputs[i].pin;
2420 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2421
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002422 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002423 type_idx++;
2424 else
2425 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002426 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002427 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2428 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002429 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002430 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002431 if (err < 0)
2432 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002433 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002434 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002435
2436 /* remember the label for smart51 control */
2437 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002438 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002439 spec->smart51_idxs[j] = idx;
2440 spec->smart51_labels[j] = label;
2441 break;
2442 }
2443 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002444 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002445 return 0;
2446}
2447
2448/* create mic-boost controls (if present) */
2449static int create_mic_boost_ctls(struct hda_codec *codec)
2450{
2451 struct via_spec *spec = codec->spec;
2452 const struct auto_pin_cfg *cfg = &spec->autocfg;
2453 int i, err;
2454
2455 for (i = 0; i < cfg->num_inputs; i++) {
2456 hda_nid_t pin = cfg->inputs[i].pin;
2457 unsigned int caps;
2458 const char *label;
2459 char name[32];
2460
2461 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2462 continue;
2463 caps = query_amp_caps(codec, pin, HDA_INPUT);
2464 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2465 continue;
2466 label = hda_get_autocfg_input_label(codec, cfg, i);
2467 snprintf(name, sizeof(name), "%s Boost Volume", label);
2468 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2469 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2470 if (err < 0)
2471 return err;
2472 }
2473 return 0;
2474}
2475
2476/* create capture and input-src controls for multiple streams */
2477static int create_multi_adc_ctls(struct hda_codec *codec)
2478{
2479 struct via_spec *spec = codec->spec;
2480 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002481
2482 /* create capture mixer elements */
2483 for (i = 0; i < spec->num_adc_nids; i++) {
2484 hda_nid_t adc = spec->adc_nids[i];
2485 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2486 "Capture Volume", i,
2487 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2488 HDA_INPUT));
2489 if (err < 0)
2490 return err;
2491 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2492 "Capture Switch", i,
2493 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2494 HDA_INPUT));
2495 if (err < 0)
2496 return err;
2497 }
2498
2499 /* input-source control */
2500 for (i = 0; i < spec->num_adc_nids; i++)
2501 if (!spec->mux_nids[i])
2502 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002503 err = create_input_src_ctls(codec, i);
2504 if (err < 0)
2505 return err;
2506 return 0;
2507}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002508
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002509/* bind capture volume/switch */
2510static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2511 HDA_BIND_VOL("Capture Volume", 0);
2512static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2513 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002514
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002515static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2516 struct hda_ctl_ops *ops)
2517{
2518 struct hda_bind_ctls *ctl;
2519 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002520
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002521 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2522 if (!ctl)
2523 return -ENOMEM;
2524 ctl->ops = ops;
2525 for (i = 0; i < spec->num_adc_nids; i++)
2526 ctl->values[i] =
2527 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2528 *ctl_ret = ctl;
2529 return 0;
2530}
2531
2532/* create capture and input-src controls for dynamic ADC-switch case */
2533static int create_dyn_adc_ctls(struct hda_codec *codec)
2534{
2535 struct via_spec *spec = codec->spec;
2536 struct snd_kcontrol_new *knew;
2537 int err;
2538
2539 /* set up the bind capture ctls */
2540 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2541 if (err < 0)
2542 return err;
2543 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2544 if (err < 0)
2545 return err;
2546
2547 /* create capture mixer elements */
2548 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2549 if (!knew)
2550 return -ENOMEM;
2551 knew->private_value = (long)spec->bind_cap_vol;
2552
2553 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2554 if (!knew)
2555 return -ENOMEM;
2556 knew->private_value = (long)spec->bind_cap_sw;
2557
2558 /* input-source control */
2559 err = create_input_src_ctls(codec, 1);
2560 if (err < 0)
2561 return err;
2562 return 0;
2563}
2564
2565/* parse and create capture-related stuff */
2566static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2567{
2568 struct via_spec *spec = codec->spec;
2569 int err;
2570
2571 err = parse_analog_inputs(codec);
2572 if (err < 0)
2573 return err;
2574 if (spec->dyn_adc_switch)
2575 err = create_dyn_adc_ctls(codec);
2576 else
2577 err = create_multi_adc_ctls(codec);
2578 if (err < 0)
2579 return err;
2580 err = create_loopback_ctls(codec);
2581 if (err < 0)
2582 return err;
2583 err = create_mic_boost_ctls(codec);
2584 if (err < 0)
2585 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002586 return 0;
2587}
2588
Harald Welte76d9b0d2008-09-09 15:50:37 +08002589static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2590{
2591 unsigned int def_conf;
2592 unsigned char seqassoc;
2593
Takashi Iwai2f334f92009-02-20 14:37:42 +01002594 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002595 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2596 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002597 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2598 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2599 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2600 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002601 }
2602
2603 return;
2604}
2605
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002606static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002607 struct snd_ctl_elem_value *ucontrol)
2608{
2609 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2610 struct via_spec *spec = codec->spec;
2611
2612 if (spec->codec_type != VT1708)
2613 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002614 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002615 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002616 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002617 return 0;
2618}
2619
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002620static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002621 struct snd_ctl_elem_value *ucontrol)
2622{
2623 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2624 struct via_spec *spec = codec->spec;
2625 int change;
2626
2627 if (spec->codec_type != VT1708)
2628 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002629 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002630 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002631 == !spec->vt1708_jack_detect;
2632 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002633 mute_aa_path(codec, 1);
2634 notify_aa_path_ctls(codec);
2635 }
2636 return change;
2637}
2638
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002639static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2640 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2641 .name = "Jack Detect",
2642 .count = 1,
2643 .info = snd_ctl_boolean_mono_info,
2644 .get = vt1708_jack_detect_get,
2645 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002646};
2647
Takashi Iwai12daef62011-06-18 17:45:49 +02002648static void fill_dig_outs(struct hda_codec *codec);
2649static void fill_dig_in(struct hda_codec *codec);
2650
2651static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002652{
2653 struct via_spec *spec = codec->spec;
2654 int err;
2655
2656 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2657 if (err < 0)
2658 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002659 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002660 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002661
Takashi Iwai4a796162011-06-17 17:53:38 +02002662 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002663 if (err < 0)
2664 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002665 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002666 if (err < 0)
2667 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002668 err = via_auto_create_speaker_ctls(codec);
2669 if (err < 0)
2670 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002671 err = via_auto_create_loopback_switch(codec);
2672 if (err < 0)
2673 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002674 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002675 if (err < 0)
2676 return err;
2677
2678 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2679
Takashi Iwai12daef62011-06-18 17:45:49 +02002680 fill_dig_outs(codec);
2681 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002682
Takashi Iwai603c4012008-07-30 15:01:44 +02002683 if (spec->kctls.list)
2684 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002685
Joseph Chanc577b8a2006-11-29 15:29:40 +01002686
Takashi Iwai3214b962011-07-18 12:49:25 +02002687 if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002688 err = via_hp_build(codec);
2689 if (err < 0)
2690 return err;
2691 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002692
Takashi Iwaif4a78282011-06-17 18:46:48 +02002693 err = via_smart51_build(codec);
2694 if (err < 0)
2695 return err;
2696
Takashi Iwai5d417622011-06-20 11:32:27 +02002697 /* assign slave outs */
2698 if (spec->slave_dig_outs[0])
2699 codec->slave_dig_outs = spec->slave_dig_outs;
2700
Joseph Chanc577b8a2006-11-29 15:29:40 +01002701 return 1;
2702}
2703
Takashi Iwai5d417622011-06-20 11:32:27 +02002704static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002705{
Lydia Wang25eaba22009-10-10 19:08:43 +08002706 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002707 if (spec->multiout.dig_out_nid)
2708 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2709 if (spec->slave_dig_outs[0])
2710 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2711}
Lydia Wang25eaba22009-10-10 19:08:43 +08002712
Takashi Iwai5d417622011-06-20 11:32:27 +02002713static void via_auto_init_dig_in(struct hda_codec *codec)
2714{
2715 struct via_spec *spec = codec->spec;
2716 if (!spec->dig_in_nid)
2717 return;
2718 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2719 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2720}
2721
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002722/* initialize the unsolicited events */
2723static void via_auto_init_unsol_event(struct hda_codec *codec)
2724{
2725 struct via_spec *spec = codec->spec;
2726 struct auto_pin_cfg *cfg = &spec->autocfg;
2727 unsigned int ev;
2728 int i;
2729
2730 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2731 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2732 AC_VERB_SET_UNSOLICITED_ENABLE,
2733 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2734
2735 if (cfg->speaker_pins[0])
2736 ev = VIA_LINE_EVENT;
2737 else
2738 ev = 0;
2739 for (i = 0; i < cfg->line_outs; i++) {
2740 if (cfg->line_out_pins[i] &&
2741 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002742 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002743 AC_VERB_SET_UNSOLICITED_ENABLE,
2744 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2745 }
2746
2747 for (i = 0; i < cfg->num_inputs; i++) {
2748 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2749 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2750 AC_VERB_SET_UNSOLICITED_ENABLE,
2751 AC_USRSP_EN | VIA_JACK_EVENT);
2752 }
2753}
2754
Takashi Iwai5d417622011-06-20 11:32:27 +02002755static int via_init(struct hda_codec *codec)
2756{
2757 struct via_spec *spec = codec->spec;
2758 int i;
2759
2760 for (i = 0; i < spec->num_iverbs; i++)
2761 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2762
Joseph Chanc577b8a2006-11-29 15:29:40 +01002763 via_auto_init_multi_out(codec);
2764 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002765 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002766 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002767 via_auto_init_dig_outs(codec);
2768 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002769
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002770 via_auto_init_unsol_event(codec);
2771
2772 via_hp_automute(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002773
Joseph Chanc577b8a2006-11-29 15:29:40 +01002774 return 0;
2775}
2776
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002777static void vt1708_update_hp_jack_state(struct work_struct *work)
2778{
2779 struct via_spec *spec = container_of(work, struct via_spec,
2780 vt1708_hp_work.work);
2781 if (spec->codec_type != VT1708)
2782 return;
2783 /* if jack state toggled */
2784 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002785 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002786 spec->vt1708_hp_present ^= 1;
2787 via_hp_automute(spec->codec);
2788 }
2789 vt1708_start_hp_work(spec);
2790}
2791
Takashi Iwai337b9d02009-07-07 18:18:59 +02002792static int get_mux_nids(struct hda_codec *codec)
2793{
2794 struct via_spec *spec = codec->spec;
2795 hda_nid_t nid, conn[8];
2796 unsigned int type;
2797 int i, n;
2798
2799 for (i = 0; i < spec->num_adc_nids; i++) {
2800 nid = spec->adc_nids[i];
2801 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002802 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002803 if (type == AC_WID_PIN)
2804 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002805 n = snd_hda_get_connections(codec, nid, conn,
2806 ARRAY_SIZE(conn));
2807 if (n <= 0)
2808 break;
2809 if (n > 1) {
2810 spec->mux_nids[i] = nid;
2811 break;
2812 }
2813 nid = conn[0];
2814 }
2815 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002816 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002817}
2818
Joseph Chanc577b8a2006-11-29 15:29:40 +01002819static int patch_vt1708(struct hda_codec *codec)
2820{
2821 struct via_spec *spec;
2822 int err;
2823
2824 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002825 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002826 if (spec == NULL)
2827 return -ENOMEM;
2828
Takashi Iwai620e2b22011-06-17 17:19:19 +02002829 spec->aa_mix_nid = 0x17;
2830
Takashi Iwai12daef62011-06-18 17:45:49 +02002831 /* Add HP and CD pin config connect bit re-config action */
2832 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2833 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2834
Joseph Chanc577b8a2006-11-29 15:29:40 +01002835 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002836 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002837 if (err < 0) {
2838 via_free(codec);
2839 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002840 }
2841
Takashi Iwai12daef62011-06-18 17:45:49 +02002842 /* add jack detect on/off control */
2843 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2844 return -ENOMEM;
2845
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002846 /* disable 32bit format on VT1708 */
2847 if (codec->vendor_id == 0x11061708)
2848 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002849
Lydia Wange322a362011-06-29 13:52:02 +08002850 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2851
Joseph Chanc577b8a2006-11-29 15:29:40 +01002852 codec->patch_ops = via_patch_ops;
2853
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002854 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002855 return 0;
2856}
2857
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002858static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002859{
2860 struct via_spec *spec;
2861 int err;
2862
2863 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002864 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002865 if (spec == NULL)
2866 return -ENOMEM;
2867
Takashi Iwai620e2b22011-06-17 17:19:19 +02002868 spec->aa_mix_nid = 0x18;
2869
Takashi Iwai12daef62011-06-18 17:45:49 +02002870 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002871 if (err < 0) {
2872 via_free(codec);
2873 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002874 }
2875
Joseph Chanc577b8a2006-11-29 15:29:40 +01002876 codec->patch_ops = via_patch_ops;
2877
Josepch Chanf7278fd2007-12-13 16:40:40 +01002878 return 0;
2879}
2880
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002881static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2882{
2883 struct via_spec *spec = codec->spec;
2884 int imux_is_smixer;
2885 unsigned int parm;
2886 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002887 if ((spec->codec_type != VT1708B_4CH) &&
2888 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002889 is_8ch = 1;
2890
2891 /* SW0 (17h) = stereo mixer */
2892 imux_is_smixer =
2893 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2894 == ((spec->codec_type == VT1708S) ? 5 : 0));
2895 /* inputs */
2896 /* PW 1/2/5 (1ah/1bh/1eh) */
2897 parm = AC_PWRST_D3;
2898 set_pin_power_state(codec, 0x1a, &parm);
2899 set_pin_power_state(codec, 0x1b, &parm);
2900 set_pin_power_state(codec, 0x1e, &parm);
2901 if (imux_is_smixer)
2902 parm = AC_PWRST_D0;
2903 /* SW0 (17h), AIW 0/1 (13h/14h) */
2904 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2905 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2906 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2907
2908 /* outputs */
2909 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2910 parm = AC_PWRST_D3;
2911 set_pin_power_state(codec, 0x19, &parm);
2912 if (spec->smart51_enabled)
2913 set_pin_power_state(codec, 0x1b, &parm);
2914 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2915 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2916
2917 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2918 if (is_8ch) {
2919 parm = AC_PWRST_D3;
2920 set_pin_power_state(codec, 0x22, &parm);
2921 if (spec->smart51_enabled)
2922 set_pin_power_state(codec, 0x1a, &parm);
2923 snd_hda_codec_write(codec, 0x26, 0,
2924 AC_VERB_SET_POWER_STATE, parm);
2925 snd_hda_codec_write(codec, 0x24, 0,
2926 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002927 } else if (codec->vendor_id == 0x11064397) {
2928 /* PW7(23h), SW2(27h), AOW2(25h) */
2929 parm = AC_PWRST_D3;
2930 set_pin_power_state(codec, 0x23, &parm);
2931 if (spec->smart51_enabled)
2932 set_pin_power_state(codec, 0x1a, &parm);
2933 snd_hda_codec_write(codec, 0x27, 0,
2934 AC_VERB_SET_POWER_STATE, parm);
2935 snd_hda_codec_write(codec, 0x25, 0,
2936 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002937 }
2938
2939 /* PW 3/4/7 (1ch/1dh/23h) */
2940 parm = AC_PWRST_D3;
2941 /* force to D0 for internal Speaker */
2942 set_pin_power_state(codec, 0x1c, &parm);
2943 set_pin_power_state(codec, 0x1d, &parm);
2944 if (is_8ch)
2945 set_pin_power_state(codec, 0x23, &parm);
2946
2947 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2948 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2949 imux_is_smixer ? AC_PWRST_D0 : parm);
2950 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2951 if (is_8ch) {
2952 snd_hda_codec_write(codec, 0x25, 0,
2953 AC_VERB_SET_POWER_STATE, parm);
2954 snd_hda_codec_write(codec, 0x27, 0,
2955 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002956 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2957 snd_hda_codec_write(codec, 0x25, 0,
2958 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002959}
2960
Lydia Wang518bf3b2009-10-10 19:07:29 +08002961static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002962static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002963{
2964 struct via_spec *spec;
2965 int err;
2966
Lydia Wang518bf3b2009-10-10 19:07:29 +08002967 if (get_codec_type(codec) == VT1708BCE)
2968 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002969
Josepch Chanf7278fd2007-12-13 16:40:40 +01002970 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002971 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002972 if (spec == NULL)
2973 return -ENOMEM;
2974
Takashi Iwai620e2b22011-06-17 17:19:19 +02002975 spec->aa_mix_nid = 0x16;
2976
Josepch Chanf7278fd2007-12-13 16:40:40 +01002977 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002978 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002979 if (err < 0) {
2980 via_free(codec);
2981 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002982 }
2983
Josepch Chanf7278fd2007-12-13 16:40:40 +01002984 codec->patch_ops = via_patch_ops;
2985
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002986 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2987
Josepch Chanf7278fd2007-12-13 16:40:40 +01002988 return 0;
2989}
2990
Harald Welted949cac2008-09-09 15:56:01 +08002991/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002992static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002993 /* Enable Mic Boost Volume backdoor */
2994 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002995 /* don't bybass mixer */
2996 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002997 { }
2998};
2999
Takashi Iwai9da29272009-05-07 16:31:14 +02003000/* fill out digital output widgets; one for master and one for slave outputs */
3001static void fill_dig_outs(struct hda_codec *codec)
3002{
3003 struct via_spec *spec = codec->spec;
3004 int i;
3005
3006 for (i = 0; i < spec->autocfg.dig_outs; i++) {
3007 hda_nid_t nid;
3008 int conn;
3009
3010 nid = spec->autocfg.dig_out_pins[i];
3011 if (!nid)
3012 continue;
3013 conn = snd_hda_get_connections(codec, nid, &nid, 1);
3014 if (conn < 1)
3015 continue;
3016 if (!spec->multiout.dig_out_nid)
3017 spec->multiout.dig_out_nid = nid;
3018 else {
3019 spec->slave_dig_outs[0] = nid;
3020 break; /* at most two dig outs */
3021 }
3022 }
3023}
3024
Takashi Iwai12daef62011-06-18 17:45:49 +02003025static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08003026{
3027 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02003028 hda_nid_t dig_nid;
3029 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08003030
Takashi Iwai12daef62011-06-18 17:45:49 +02003031 if (!spec->autocfg.dig_in_pin)
3032 return;
Harald Welted949cac2008-09-09 15:56:01 +08003033
Takashi Iwai12daef62011-06-18 17:45:49 +02003034 dig_nid = codec->start_nid;
3035 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
3036 unsigned int wcaps = get_wcaps(codec, dig_nid);
3037 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
3038 continue;
3039 if (!(wcaps & AC_WCAP_DIGITAL))
3040 continue;
3041 if (!(wcaps & AC_WCAP_CONN_LIST))
3042 continue;
3043 err = get_connection_index(codec, dig_nid,
3044 spec->autocfg.dig_in_pin);
3045 if (err >= 0) {
3046 spec->dig_in_nid = dig_nid;
3047 break;
3048 }
3049 }
Harald Welted949cac2008-09-09 15:56:01 +08003050}
3051
Lydia Wang6369bcf2009-10-10 19:08:31 +08003052static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
3053 int offset, int num_steps, int step_size)
3054{
3055 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
3056 (offset << AC_AMPCAP_OFFSET_SHIFT) |
3057 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
3058 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
3059 (0 << AC_AMPCAP_MUTE_SHIFT));
3060}
3061
Harald Welted949cac2008-09-09 15:56:01 +08003062static int patch_vt1708S(struct hda_codec *codec)
3063{
3064 struct via_spec *spec;
3065 int err;
3066
3067 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003068 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003069 if (spec == NULL)
3070 return -ENOMEM;
3071
Takashi Iwai620e2b22011-06-17 17:19:19 +02003072 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003073 override_mic_boost(codec, 0x1a, 0, 3, 40);
3074 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003075
Harald Welted949cac2008-09-09 15:56:01 +08003076 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003077 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003078 if (err < 0) {
3079 via_free(codec);
3080 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003081 }
3082
Takashi Iwai096a8852011-06-20 12:09:02 +02003083 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003084
Harald Welted949cac2008-09-09 15:56:01 +08003085 codec->patch_ops = via_patch_ops;
3086
Lydia Wang518bf3b2009-10-10 19:07:29 +08003087 /* correct names for VT1708BCE */
3088 if (get_codec_type(codec) == VT1708BCE) {
3089 kfree(codec->chip_name);
3090 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3091 snprintf(codec->bus->card->mixername,
3092 sizeof(codec->bus->card->mixername),
3093 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08003094 }
Lydia Wangbc92df72011-03-23 17:56:05 +08003095 /* correct names for VT1705 */
3096 if (codec->vendor_id == 0x11064397) {
3097 kfree(codec->chip_name);
3098 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3099 snprintf(codec->bus->card->mixername,
3100 sizeof(codec->bus->card->mixername),
3101 "%s %s", codec->vendor_name, codec->chip_name);
3102 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003103 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08003104 return 0;
3105}
3106
3107/* Patch for VT1702 */
3108
Takashi Iwai096a8852011-06-20 12:09:02 +02003109static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003110 /* mixer enable */
3111 {0x1, 0xF88, 0x3},
3112 /* GPIO 0~2 */
3113 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003114 { }
3115};
3116
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003117static void set_widgets_power_state_vt1702(struct hda_codec *codec)
3118{
3119 int imux_is_smixer =
3120 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3121 unsigned int parm;
3122 /* inputs */
3123 /* PW 1/2/5 (14h/15h/18h) */
3124 parm = AC_PWRST_D3;
3125 set_pin_power_state(codec, 0x14, &parm);
3126 set_pin_power_state(codec, 0x15, &parm);
3127 set_pin_power_state(codec, 0x18, &parm);
3128 if (imux_is_smixer)
3129 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
3130 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
3131 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3132 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
3133 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3134 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
3135
3136 /* outputs */
3137 /* PW 3/4 (16h/17h) */
3138 parm = AC_PWRST_D3;
3139 set_pin_power_state(codec, 0x17, &parm);
3140 set_pin_power_state(codec, 0x16, &parm);
3141 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
3142 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
3143 imux_is_smixer ? AC_PWRST_D0 : parm);
3144 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3145 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3146}
3147
Harald Welted949cac2008-09-09 15:56:01 +08003148static int patch_vt1702(struct hda_codec *codec)
3149{
3150 struct via_spec *spec;
3151 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003152
3153 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003154 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003155 if (spec == NULL)
3156 return -ENOMEM;
3157
Takashi Iwai620e2b22011-06-17 17:19:19 +02003158 spec->aa_mix_nid = 0x1a;
3159
Takashi Iwai12daef62011-06-18 17:45:49 +02003160 /* limit AA path volume to 0 dB */
3161 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3162 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3163 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3164 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3165 (1 << AC_AMPCAP_MUTE_SHIFT));
3166
Harald Welted949cac2008-09-09 15:56:01 +08003167 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003168 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003169 if (err < 0) {
3170 via_free(codec);
3171 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003172 }
3173
Takashi Iwai096a8852011-06-20 12:09:02 +02003174 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003175
Harald Welted949cac2008-09-09 15:56:01 +08003176 codec->patch_ops = via_patch_ops;
3177
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003178 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08003179 return 0;
3180}
3181
Lydia Wangeb7188c2009-10-10 19:08:34 +08003182/* Patch for VT1718S */
3183
Takashi Iwai096a8852011-06-20 12:09:02 +02003184static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08003185 /* Enable MW0 adjust Gain 5 */
3186 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003187 /* Enable Boost Volume backdoor */
3188 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02003189
Lydia Wangeb7188c2009-10-10 19:08:34 +08003190 { }
3191};
3192
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003193static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
3194{
3195 struct via_spec *spec = codec->spec;
3196 int imux_is_smixer;
3197 unsigned int parm;
3198 /* MUX6 (1eh) = stereo mixer */
3199 imux_is_smixer =
3200 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3201 /* inputs */
3202 /* PW 5/6/7 (29h/2ah/2bh) */
3203 parm = AC_PWRST_D3;
3204 set_pin_power_state(codec, 0x29, &parm);
3205 set_pin_power_state(codec, 0x2a, &parm);
3206 set_pin_power_state(codec, 0x2b, &parm);
3207 if (imux_is_smixer)
3208 parm = AC_PWRST_D0;
3209 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
3210 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3211 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3212 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3213 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3214
3215 /* outputs */
3216 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
3217 parm = AC_PWRST_D3;
3218 set_pin_power_state(codec, 0x27, &parm);
3219 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
3220 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
3221
3222 /* PW2 (26h), AOW2 (ah) */
3223 parm = AC_PWRST_D3;
3224 set_pin_power_state(codec, 0x26, &parm);
3225 if (spec->smart51_enabled)
3226 set_pin_power_state(codec, 0x2b, &parm);
3227 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
3228
3229 /* PW0 (24h), AOW0 (8h) */
3230 parm = AC_PWRST_D3;
3231 set_pin_power_state(codec, 0x24, &parm);
3232 if (!spec->hp_independent_mode) /* check for redirected HP */
3233 set_pin_power_state(codec, 0x28, &parm);
3234 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3235 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
3236 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
3237 imux_is_smixer ? AC_PWRST_D0 : parm);
3238
3239 /* PW1 (25h), AOW1 (9h) */
3240 parm = AC_PWRST_D3;
3241 set_pin_power_state(codec, 0x25, &parm);
3242 if (spec->smart51_enabled)
3243 set_pin_power_state(codec, 0x2a, &parm);
3244 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
3245
3246 if (spec->hp_independent_mode) {
3247 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3248 parm = AC_PWRST_D3;
3249 set_pin_power_state(codec, 0x28, &parm);
3250 snd_hda_codec_write(codec, 0x1b, 0,
3251 AC_VERB_SET_POWER_STATE, parm);
3252 snd_hda_codec_write(codec, 0x34, 0,
3253 AC_VERB_SET_POWER_STATE, parm);
3254 snd_hda_codec_write(codec, 0xc, 0,
3255 AC_VERB_SET_POWER_STATE, parm);
3256 }
3257}
3258
Takashi Iwai30b45032011-07-11 17:05:04 +02003259/* Add a connection to the primary DAC from AA-mixer for some codecs
3260 * This isn't listed from the raw info, but the chip has a secret connection.
3261 */
3262static int add_secret_dac_path(struct hda_codec *codec)
3263{
3264 struct via_spec *spec = codec->spec;
3265 int i, nums;
3266 hda_nid_t conn[8];
3267 hda_nid_t nid;
3268
3269 if (!spec->aa_mix_nid)
3270 return 0;
3271 nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
3272 ARRAY_SIZE(conn) - 1);
3273 for (i = 0; i < nums; i++) {
3274 if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
3275 return 0;
3276 }
3277
3278 /* find the primary DAC and add to the connection list */
3279 nid = codec->start_nid;
3280 for (i = 0; i < codec->num_nodes; i++, nid++) {
3281 unsigned int caps = get_wcaps(codec, nid);
3282 if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
3283 !(caps & AC_WCAP_DIGITAL)) {
3284 conn[nums++] = nid;
3285 return snd_hda_override_conn_list(codec,
3286 spec->aa_mix_nid,
3287 nums, conn);
3288 }
3289 }
3290 return 0;
3291}
3292
3293
Lydia Wangeb7188c2009-10-10 19:08:34 +08003294static int patch_vt1718S(struct hda_codec *codec)
3295{
3296 struct via_spec *spec;
3297 int err;
3298
3299 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003300 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003301 if (spec == NULL)
3302 return -ENOMEM;
3303
Takashi Iwai620e2b22011-06-17 17:19:19 +02003304 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003305 override_mic_boost(codec, 0x2b, 0, 3, 40);
3306 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003307 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003308
Lydia Wangeb7188c2009-10-10 19:08:34 +08003309 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003310 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003311 if (err < 0) {
3312 via_free(codec);
3313 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003314 }
3315
Takashi Iwai096a8852011-06-20 12:09:02 +02003316 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003317
Lydia Wangeb7188c2009-10-10 19:08:34 +08003318 codec->patch_ops = via_patch_ops;
3319
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003320 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3321
Lydia Wangeb7188c2009-10-10 19:08:34 +08003322 return 0;
3323}
Lydia Wangf3db4232009-10-10 19:08:41 +08003324
3325/* Patch for VT1716S */
3326
3327static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3328 struct snd_ctl_elem_info *uinfo)
3329{
3330 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3331 uinfo->count = 1;
3332 uinfo->value.integer.min = 0;
3333 uinfo->value.integer.max = 1;
3334 return 0;
3335}
3336
3337static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3338 struct snd_ctl_elem_value *ucontrol)
3339{
3340 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3341 int index = 0;
3342
3343 index = snd_hda_codec_read(codec, 0x26, 0,
3344 AC_VERB_GET_CONNECT_SEL, 0);
3345 if (index != -1)
3346 *ucontrol->value.integer.value = index;
3347
3348 return 0;
3349}
3350
3351static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3352 struct snd_ctl_elem_value *ucontrol)
3353{
3354 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3355 struct via_spec *spec = codec->spec;
3356 int index = *ucontrol->value.integer.value;
3357
3358 snd_hda_codec_write(codec, 0x26, 0,
3359 AC_VERB_SET_CONNECT_SEL, index);
3360 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003361 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003362 return 1;
3363}
3364
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003365static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003366 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3367 {
3368 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3369 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003370 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003371 .count = 1,
3372 .info = vt1716s_dmic_info,
3373 .get = vt1716s_dmic_get,
3374 .put = vt1716s_dmic_put,
3375 },
3376 {} /* end */
3377};
3378
3379
3380/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003381static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003382 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3383 { } /* end */
3384};
3385
Takashi Iwai096a8852011-06-20 12:09:02 +02003386static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003387 /* Enable Boost Volume backdoor */
3388 {0x1, 0xf8a, 0x80},
3389 /* don't bybass mixer */
3390 {0x1, 0xf88, 0xc0},
3391 /* Enable mono output */
3392 {0x1, 0xf90, 0x08},
3393 { }
3394};
3395
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003396static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3397{
3398 struct via_spec *spec = codec->spec;
3399 int imux_is_smixer;
3400 unsigned int parm;
3401 unsigned int mono_out, present;
3402 /* SW0 (17h) = stereo mixer */
3403 imux_is_smixer =
3404 (snd_hda_codec_read(codec, 0x17, 0,
3405 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3406 /* inputs */
3407 /* PW 1/2/5 (1ah/1bh/1eh) */
3408 parm = AC_PWRST_D3;
3409 set_pin_power_state(codec, 0x1a, &parm);
3410 set_pin_power_state(codec, 0x1b, &parm);
3411 set_pin_power_state(codec, 0x1e, &parm);
3412 if (imux_is_smixer)
3413 parm = AC_PWRST_D0;
3414 /* SW0 (17h), AIW0(13h) */
3415 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3416 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3417
3418 parm = AC_PWRST_D3;
3419 set_pin_power_state(codec, 0x1e, &parm);
3420 /* PW11 (22h) */
3421 if (spec->dmic_enabled)
3422 set_pin_power_state(codec, 0x22, &parm);
3423 else
3424 snd_hda_codec_write(codec, 0x22, 0,
3425 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3426
3427 /* SW2(26h), AIW1(14h) */
3428 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3429 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3430
3431 /* outputs */
3432 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3433 parm = AC_PWRST_D3;
3434 set_pin_power_state(codec, 0x19, &parm);
3435 /* Smart 5.1 PW2(1bh) */
3436 if (spec->smart51_enabled)
3437 set_pin_power_state(codec, 0x1b, &parm);
3438 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3439 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3440
3441 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3442 parm = AC_PWRST_D3;
3443 set_pin_power_state(codec, 0x23, &parm);
3444 /* Smart 5.1 PW1(1ah) */
3445 if (spec->smart51_enabled)
3446 set_pin_power_state(codec, 0x1a, &parm);
3447 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3448
3449 /* Smart 5.1 PW5(1eh) */
3450 if (spec->smart51_enabled)
3451 set_pin_power_state(codec, 0x1e, &parm);
3452 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3453
3454 /* Mono out */
3455 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3456 present = snd_hda_jack_detect(codec, 0x1c);
3457
3458 if (present)
3459 mono_out = 0;
3460 else {
3461 present = snd_hda_jack_detect(codec, 0x1d);
3462 if (!spec->hp_independent_mode && present)
3463 mono_out = 0;
3464 else
3465 mono_out = 1;
3466 }
3467 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3468 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3469 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3470 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3471
3472 /* PW 3/4 (1ch/1dh) */
3473 parm = AC_PWRST_D3;
3474 set_pin_power_state(codec, 0x1c, &parm);
3475 set_pin_power_state(codec, 0x1d, &parm);
3476 /* HP Independent Mode, power on AOW3 */
3477 if (spec->hp_independent_mode)
3478 snd_hda_codec_write(codec, 0x25, 0,
3479 AC_VERB_SET_POWER_STATE, parm);
3480
3481 /* force to D0 for internal Speaker */
3482 /* MW0 (16h), AOW0 (10h) */
3483 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3484 imux_is_smixer ? AC_PWRST_D0 : parm);
3485 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3486 mono_out ? AC_PWRST_D0 : parm);
3487}
3488
Lydia Wangf3db4232009-10-10 19:08:41 +08003489static int patch_vt1716S(struct hda_codec *codec)
3490{
3491 struct via_spec *spec;
3492 int err;
3493
3494 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003495 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003496 if (spec == NULL)
3497 return -ENOMEM;
3498
Takashi Iwai620e2b22011-06-17 17:19:19 +02003499 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003500 override_mic_boost(codec, 0x1a, 0, 3, 40);
3501 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003502
Lydia Wangf3db4232009-10-10 19:08:41 +08003503 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003504 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003505 if (err < 0) {
3506 via_free(codec);
3507 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003508 }
3509
Takashi Iwai096a8852011-06-20 12:09:02 +02003510 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003511
Lydia Wangf3db4232009-10-10 19:08:41 +08003512 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3513 spec->num_mixers++;
3514
3515 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3516
3517 codec->patch_ops = via_patch_ops;
3518
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003519 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003520 return 0;
3521}
Lydia Wang25eaba22009-10-10 19:08:43 +08003522
3523/* for vt2002P */
3524
Takashi Iwai096a8852011-06-20 12:09:02 +02003525static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003526 /* Class-D speaker related verbs */
3527 {0x1, 0xfe0, 0x4},
3528 {0x1, 0xfe9, 0x80},
3529 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003530 /* Enable Boost Volume backdoor */
3531 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003532 /* Enable AOW0 to MW9 */
3533 {0x1, 0xfb8, 0x88},
3534 { }
3535};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003536
Takashi Iwai096a8852011-06-20 12:09:02 +02003537static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003538 /* Enable Boost Volume backdoor */
3539 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003540 /* Enable AOW0 to MW9 */
3541 {0x1, 0xfb8, 0x88},
3542 { }
3543};
Lydia Wang25eaba22009-10-10 19:08:43 +08003544
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003545static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3546{
3547 struct via_spec *spec = codec->spec;
3548 int imux_is_smixer;
3549 unsigned int parm;
3550 unsigned int present;
3551 /* MUX9 (1eh) = stereo mixer */
3552 imux_is_smixer =
3553 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3554 /* inputs */
3555 /* PW 5/6/7 (29h/2ah/2bh) */
3556 parm = AC_PWRST_D3;
3557 set_pin_power_state(codec, 0x29, &parm);
3558 set_pin_power_state(codec, 0x2a, &parm);
3559 set_pin_power_state(codec, 0x2b, &parm);
3560 parm = AC_PWRST_D0;
3561 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3562 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3563 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3564 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3565 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3566
3567 /* outputs */
3568 /* AOW0 (8h)*/
3569 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3570
Lydia Wang118909562011-03-23 17:57:34 +08003571 if (spec->codec_type == VT1802) {
3572 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3573 parm = AC_PWRST_D3;
3574 set_pin_power_state(codec, 0x28, &parm);
3575 snd_hda_codec_write(codec, 0x18, 0,
3576 AC_VERB_SET_POWER_STATE, parm);
3577 snd_hda_codec_write(codec, 0x38, 0,
3578 AC_VERB_SET_POWER_STATE, parm);
3579 } else {
3580 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3581 parm = AC_PWRST_D3;
3582 set_pin_power_state(codec, 0x26, &parm);
3583 snd_hda_codec_write(codec, 0x1c, 0,
3584 AC_VERB_SET_POWER_STATE, parm);
3585 snd_hda_codec_write(codec, 0x37, 0,
3586 AC_VERB_SET_POWER_STATE, parm);
3587 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003588
Lydia Wang118909562011-03-23 17:57:34 +08003589 if (spec->codec_type == VT1802) {
3590 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3591 parm = AC_PWRST_D3;
3592 set_pin_power_state(codec, 0x25, &parm);
3593 snd_hda_codec_write(codec, 0x15, 0,
3594 AC_VERB_SET_POWER_STATE, parm);
3595 snd_hda_codec_write(codec, 0x35, 0,
3596 AC_VERB_SET_POWER_STATE, parm);
3597 } else {
3598 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3599 parm = AC_PWRST_D3;
3600 set_pin_power_state(codec, 0x25, &parm);
3601 snd_hda_codec_write(codec, 0x19, 0,
3602 AC_VERB_SET_POWER_STATE, parm);
3603 snd_hda_codec_write(codec, 0x35, 0,
3604 AC_VERB_SET_POWER_STATE, parm);
3605 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003606
3607 if (spec->hp_independent_mode)
3608 snd_hda_codec_write(codec, 0x9, 0,
3609 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3610
3611 /* Class-D */
3612 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3613 present = snd_hda_jack_detect(codec, 0x25);
3614
3615 parm = AC_PWRST_D3;
3616 set_pin_power_state(codec, 0x24, &parm);
3617 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003618 if (spec->codec_type == VT1802)
3619 snd_hda_codec_write(codec, 0x14, 0,
3620 AC_VERB_SET_POWER_STATE, parm);
3621 else
3622 snd_hda_codec_write(codec, 0x18, 0,
3623 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003624 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3625
3626 /* Mono Out */
3627 present = snd_hda_jack_detect(codec, 0x26);
3628
3629 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003630 if (spec->codec_type == VT1802) {
3631 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3632 snd_hda_codec_write(codec, 0x33, 0,
3633 AC_VERB_SET_POWER_STATE, parm);
3634 snd_hda_codec_write(codec, 0x1c, 0,
3635 AC_VERB_SET_POWER_STATE, parm);
3636 snd_hda_codec_write(codec, 0x3c, 0,
3637 AC_VERB_SET_POWER_STATE, parm);
3638 } else {
3639 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3640 snd_hda_codec_write(codec, 0x31, 0,
3641 AC_VERB_SET_POWER_STATE, parm);
3642 snd_hda_codec_write(codec, 0x17, 0,
3643 AC_VERB_SET_POWER_STATE, parm);
3644 snd_hda_codec_write(codec, 0x3b, 0,
3645 AC_VERB_SET_POWER_STATE, parm);
3646 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003647 /* MW9 (21h) */
3648 if (imux_is_smixer || !is_aa_path_mute(codec))
3649 snd_hda_codec_write(codec, 0x21, 0,
3650 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3651 else
3652 snd_hda_codec_write(codec, 0x21, 0,
3653 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3654}
Lydia Wang25eaba22009-10-10 19:08:43 +08003655
3656/* patch for vt2002P */
3657static int patch_vt2002P(struct hda_codec *codec)
3658{
3659 struct via_spec *spec;
3660 int err;
3661
3662 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003663 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003664 if (spec == NULL)
3665 return -ENOMEM;
3666
Takashi Iwai620e2b22011-06-17 17:19:19 +02003667 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003668 override_mic_boost(codec, 0x2b, 0, 3, 40);
3669 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003670 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003671
Lydia Wang25eaba22009-10-10 19:08:43 +08003672 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003673 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003674 if (err < 0) {
3675 via_free(codec);
3676 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003677 }
3678
Lydia Wang118909562011-03-23 17:57:34 +08003679 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003680 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003681 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003682 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003683
Lydia Wang25eaba22009-10-10 19:08:43 +08003684 codec->patch_ops = via_patch_ops;
3685
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003686 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003687 return 0;
3688}
Lydia Wangab6734e2009-10-10 19:08:46 +08003689
3690/* for vt1812 */
3691
Takashi Iwai096a8852011-06-20 12:09:02 +02003692static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003693 /* Enable Boost Volume backdoor */
3694 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003695 /* Enable AOW0 to MW9 */
3696 {0x1, 0xfb8, 0xa8},
3697 { }
3698};
3699
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003700static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3701{
3702 struct via_spec *spec = codec->spec;
3703 int imux_is_smixer =
3704 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3705 unsigned int parm;
3706 unsigned int present;
3707 /* MUX10 (1eh) = stereo mixer */
3708 imux_is_smixer =
3709 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3710 /* inputs */
3711 /* PW 5/6/7 (29h/2ah/2bh) */
3712 parm = AC_PWRST_D3;
3713 set_pin_power_state(codec, 0x29, &parm);
3714 set_pin_power_state(codec, 0x2a, &parm);
3715 set_pin_power_state(codec, 0x2b, &parm);
3716 parm = AC_PWRST_D0;
3717 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3718 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3719 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3720 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3721 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3722
3723 /* outputs */
3724 /* AOW0 (8h)*/
3725 snd_hda_codec_write(codec, 0x8, 0,
3726 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3727
3728 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3729 parm = AC_PWRST_D3;
3730 set_pin_power_state(codec, 0x28, &parm);
3731 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3732 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3733
3734 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3735 parm = AC_PWRST_D3;
3736 set_pin_power_state(codec, 0x25, &parm);
3737 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3738 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3739 if (spec->hp_independent_mode)
3740 snd_hda_codec_write(codec, 0x9, 0,
3741 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3742
3743 /* Internal Speaker */
3744 /* PW0 (24h), MW0(14h), MUX0(34h) */
3745 present = snd_hda_jack_detect(codec, 0x25);
3746
3747 parm = AC_PWRST_D3;
3748 set_pin_power_state(codec, 0x24, &parm);
3749 if (present) {
3750 snd_hda_codec_write(codec, 0x14, 0,
3751 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3752 snd_hda_codec_write(codec, 0x34, 0,
3753 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3754 } else {
3755 snd_hda_codec_write(codec, 0x14, 0,
3756 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3757 snd_hda_codec_write(codec, 0x34, 0,
3758 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3759 }
3760
3761
3762 /* Mono Out */
3763 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3764 present = snd_hda_jack_detect(codec, 0x28);
3765
3766 parm = AC_PWRST_D3;
3767 set_pin_power_state(codec, 0x31, &parm);
3768 if (present) {
3769 snd_hda_codec_write(codec, 0x1c, 0,
3770 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3771 snd_hda_codec_write(codec, 0x3c, 0,
3772 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3773 snd_hda_codec_write(codec, 0x3e, 0,
3774 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3775 } else {
3776 snd_hda_codec_write(codec, 0x1c, 0,
3777 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3778 snd_hda_codec_write(codec, 0x3c, 0,
3779 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3780 snd_hda_codec_write(codec, 0x3e, 0,
3781 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3782 }
3783
3784 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3785 parm = AC_PWRST_D3;
3786 set_pin_power_state(codec, 0x33, &parm);
3787 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3788 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3789
3790}
Lydia Wangab6734e2009-10-10 19:08:46 +08003791
3792/* patch for vt1812 */
3793static int patch_vt1812(struct hda_codec *codec)
3794{
3795 struct via_spec *spec;
3796 int err;
3797
3798 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003799 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003800 if (spec == NULL)
3801 return -ENOMEM;
3802
Takashi Iwai620e2b22011-06-17 17:19:19 +02003803 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003804 override_mic_boost(codec, 0x2b, 0, 3, 40);
3805 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003806 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003807
Lydia Wangab6734e2009-10-10 19:08:46 +08003808 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003809 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003810 if (err < 0) {
3811 via_free(codec);
3812 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003813 }
3814
Takashi Iwai096a8852011-06-20 12:09:02 +02003815 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003816
Lydia Wangab6734e2009-10-10 19:08:46 +08003817 codec->patch_ops = via_patch_ops;
3818
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003819 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003820 return 0;
3821}
3822
Joseph Chanc577b8a2006-11-29 15:29:40 +01003823/*
3824 * patch entries
3825 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003826static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003827 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3828 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3829 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3830 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3831 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003832 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003833 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003834 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003835 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003836 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003837 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003838 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003839 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003840 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003841 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003842 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003843 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003844 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003845 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003846 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003847 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003848 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003849 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003850 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003851 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003852 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003853 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003854 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003855 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003856 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003857 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003858 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003859 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003860 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003861 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003862 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003863 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003864 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003865 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003866 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003867 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003868 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003869 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003870 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003871 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003872 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003873 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003874 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003875 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003876 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003877 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003878 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003879 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003880 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003881 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003882 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003883 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003884 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003885 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003886 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003887 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003888 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003889 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003890 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003891 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003892 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003893 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003894 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003895 { .id = 0x11060428, .name = "VT1718S",
3896 .patch = patch_vt1718S},
3897 { .id = 0x11064428, .name = "VT1718S",
3898 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003899 { .id = 0x11060441, .name = "VT2020",
3900 .patch = patch_vt1718S},
3901 { .id = 0x11064441, .name = "VT1828S",
3902 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003903 { .id = 0x11060433, .name = "VT1716S",
3904 .patch = patch_vt1716S},
3905 { .id = 0x1106a721, .name = "VT1716S",
3906 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003907 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3908 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003909 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003910 { .id = 0x11060440, .name = "VT1818S",
3911 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003912 { .id = 0x11060446, .name = "VT1802",
3913 .patch = patch_vt2002P},
3914 { .id = 0x11068446, .name = "VT1802",
3915 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003916 {} /* terminator */
3917};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003918
3919MODULE_ALIAS("snd-hda-codec-id:1106*");
3920
3921static struct hda_codec_preset_list via_list = {
3922 .preset = snd_hda_preset_via,
3923 .owner = THIS_MODULE,
3924};
3925
3926MODULE_LICENSE("GPL");
3927MODULE_DESCRIPTION("VIA HD-audio codec");
3928
3929static int __init patch_via_init(void)
3930{
3931 return snd_hda_add_codec_preset(&via_list);
3932}
3933
3934static void __exit patch_via_exit(void)
3935{
3936 snd_hda_delete_codec_preset(&via_list);
3937}
3938
3939module_init(patch_via_init)
3940module_exit(patch_via_exit)