blob: 761339a0694d04dfe043cc81dba881a15b6d6cd1 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010052#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080053#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010054#include "hda_codec.h"
55#include "hda_local.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010056
Joseph Chanc577b8a2006-11-29 15:29:40 +010057/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080058#define VT1708_HP_PIN_NID 0x20
59#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010060
Harald Welted7426322008-09-15 22:43:23 +080061enum VIA_HDA_CODEC {
62 UNKNOWN = -1,
63 VT1708,
64 VT1709_10CH,
65 VT1709_6CH,
66 VT1708B_8CH,
67 VT1708B_4CH,
68 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080069 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080070 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080071 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080072 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080073 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080074 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080075 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080076 CODEC_TYPES,
77};
78
Lydia Wang118909562011-03-23 17:57:34 +080079#define VT2002P_COMPATIBLE(spec) \
80 ((spec)->codec_type == VT2002P ||\
81 (spec)->codec_type == VT1812 ||\
82 (spec)->codec_type == VT1802)
83
Takashi Iwai8e3679d2011-06-21 09:01:36 +020084#define MAX_NID_PATH_DEPTH 5
85
Takashi Iwai09a9ad62011-06-21 15:57:44 +020086/* output-path: DAC -> ... -> pin
87 * idx[] contains the source index number of the next widget;
88 * e.g. idx[0] is the index of the DAC selected by path[1] widget
89 * multi[] indicates whether it's a selector widget with multi-connectors
90 * (i.e. the connection selection is mandatory)
91 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
92 */
Takashi Iwai4a796162011-06-17 17:53:38 +020093struct nid_path {
94 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020095 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad62011-06-21 15:57:44 +020096 unsigned char idx[MAX_NID_PATH_DEPTH];
97 unsigned char multi[MAX_NID_PATH_DEPTH];
98 unsigned int vol_ctl;
99 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200100};
101
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200102/* input-path */
103struct via_input {
104 hda_nid_t pin; /* input-pin or aa-mix */
105 int adc_idx; /* ADC index to be used */
106 int mux_idx; /* MUX index (if any) */
107 const char *label; /* input-source label */
108};
109
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200110#define VIA_MAX_ADCS 3
111
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 Wangbb3c6bf2009-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 Iwai09a9ad62011-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 Iwai09a9ad62011-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 Iwai09a9ad62011-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 Iwai09a9ad62011-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 Iwai09a9ad62011-06-21 15:57:44 +0200533static void via_auto_init_output(struct hda_codec *codec,
534 struct nid_path *path, int pin_type,
Takashi Iwai3214b962011-07-18 12:49:25 +0200535 bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200536{
Takashi Iwai5d417622011-06-20 11:32:27 +0200537 unsigned int caps;
Lydia Wangd69607b2011-07-08 14:02:52 +0800538 hda_nid_t pin;
Takashi Iwai5d417622011-06-20 11:32:27 +0200539
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200540 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200541 return;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200542 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200543
544 init_output_pin(codec, pin, pin_type);
545 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
546 if (caps & AC_AMPCAP_MUTE) {
547 unsigned int val;
548 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
549 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
550 AMP_OUT_MUTE | val);
551 }
Lydia Wangd69607b2011-07-08 14:02:52 +0800552 activate_output_path(codec, path, true, force);
Takashi Iwai5d417622011-06-20 11:32:27 +0200553}
554
Joseph Chanc577b8a2006-11-29 15:29:40 +0100555static void via_auto_init_multi_out(struct hda_codec *codec)
556{
557 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200558 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100559 int i;
560
Takashi Iwai3214b962011-07-18 12:49:25 +0200561 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) {
562 path = &spec->out_path[i];
563 if (!i && spec->aamix_mode && spec->out_mix_path.depth)
564 path = &spec->out_mix_path;
565 via_auto_init_output(codec, path, PIN_OUT, true);
566 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100567}
568
Takashi Iwai020066d2011-07-21 13:45:56 +0200569/* deactivate the inactive headphone-paths */
570static void deactivate_hp_paths(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100571{
572 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200573 int shared = spec->hp_indep_shared;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100574
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200575 if (spec->hp_independent_mode) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200576 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200577 activate_output_path(codec, &spec->hp_mix_path, false, false);
578 if (shared)
579 activate_output_path(codec, &spec->out_path[shared],
580 false, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200581 } else if (spec->aamix_mode || !spec->hp_path.depth) {
582 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200583 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200584 } else {
Takashi Iwai020066d2011-07-21 13:45:56 +0200585 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200586 activate_output_path(codec, &spec->hp_mix_path, false, false);
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200587 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100588}
589
Takashi Iwai020066d2011-07-21 13:45:56 +0200590static void via_auto_init_hp_out(struct hda_codec *codec)
591{
592 struct via_spec *spec = codec->spec;
593
594 if (!spec->hp_path.depth) {
595 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP, true);
596 return;
597 }
598 deactivate_hp_paths(codec);
599 if (spec->hp_independent_mode)
600 via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP, true);
601 else if (spec->aamix_mode)
602 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP, true);
603 else
604 via_auto_init_output(codec, &spec->hp_path, PIN_HP, true);
605}
606
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200607static void via_auto_init_speaker_out(struct hda_codec *codec)
608{
609 struct via_spec *spec = codec->spec;
610
Takashi Iwai3214b962011-07-18 12:49:25 +0200611 if (!spec->autocfg.speaker_outs)
612 return;
613 if (!spec->speaker_path.depth) {
614 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT,
615 true);
616 return;
617 }
618 if (!spec->aamix_mode) {
619 activate_output_path(codec, &spec->speaker_mix_path,
620 false, false);
Takashi Iwaibac4b922011-07-04 17:35:51 +0200621 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT,
Takashi Iwai3214b962011-07-18 12:49:25 +0200622 true);
623 } else {
624 activate_output_path(codec, &spec->speaker_path, false, false);
625 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT,
626 true);
627 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200628}
629
Takashi Iwaif4a78282011-06-17 18:46:48 +0200630static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200631static void via_hp_automute(struct hda_codec *codec);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200632
Joseph Chanc577b8a2006-11-29 15:29:40 +0100633static void via_auto_init_analog_input(struct hda_codec *codec)
634{
635 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200636 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200637 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200638 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200639 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100640
Takashi Iwai096a8852011-06-20 12:09:02 +0200641 /* init ADCs */
642 for (i = 0; i < spec->num_adc_nids; i++) {
643 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
644 AC_VERB_SET_AMP_GAIN_MUTE,
645 AMP_IN_UNMUTE(0));
646 }
647
648 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200649 for (i = 0; i < cfg->num_inputs; i++) {
650 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200651 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200652 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100653 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200654 ctl = PIN_VREF50;
655 else
656 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100657 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200658 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100659 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200660
661 /* init input-src */
662 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200663 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
664 if (spec->mux_nids[adc_idx]) {
665 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
666 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
667 AC_VERB_SET_CONNECT_SEL,
668 mux_idx);
669 }
670 if (spec->dyn_adc_switch)
671 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200672 }
673
674 /* init aa-mixer */
675 if (!spec->aa_mix_nid)
676 return;
677 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
678 ARRAY_SIZE(conn));
679 for (i = 0; i < num_conns; i++) {
680 unsigned int caps = get_wcaps(codec, conn[i]);
681 if (get_wcaps_type(caps) == AC_WID_PIN)
682 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
683 AC_VERB_SET_AMP_GAIN_MUTE,
684 AMP_IN_MUTE(i));
685 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100686}
Lydia Wangf5271102009-10-10 19:07:35 +0800687
688static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
689 unsigned int *affected_parm)
690{
691 unsigned parm;
692 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
693 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
694 >> AC_DEFCFG_MISC_SHIFT
695 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800696 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200697 unsigned present = 0;
698
699 no_presence |= spec->no_pin_power_ctl;
700 if (!no_presence)
701 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200702 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800703 || ((no_presence || present)
704 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800705 *affected_parm = AC_PWRST_D0; /* if it's connected */
706 parm = AC_PWRST_D0;
707 } else
708 parm = AC_PWRST_D3;
709
710 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
711}
712
Takashi Iwai24088a52011-06-17 16:59:21 +0200713static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
714 struct snd_ctl_elem_info *uinfo)
715{
716 static const char * const texts[] = {
717 "Disabled", "Enabled"
718 };
719
720 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
721 uinfo->count = 1;
722 uinfo->value.enumerated.items = 2;
723 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
724 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
725 strcpy(uinfo->value.enumerated.name,
726 texts[uinfo->value.enumerated.item]);
727 return 0;
728}
729
730static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
731 struct snd_ctl_elem_value *ucontrol)
732{
733 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
734 struct via_spec *spec = codec->spec;
735 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
736 return 0;
737}
738
739static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
740 struct snd_ctl_elem_value *ucontrol)
741{
742 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
743 struct via_spec *spec = codec->spec;
744 unsigned int val = !ucontrol->value.enumerated.item[0];
745
746 if (val == spec->no_pin_power_ctl)
747 return 0;
748 spec->no_pin_power_ctl = val;
749 set_widgets_power_state(codec);
750 return 1;
751}
752
753static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
754 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
755 .name = "Dynamic Power-Control",
756 .info = via_pin_power_ctl_info,
757 .get = via_pin_power_ctl_get,
758 .put = via_pin_power_ctl_put,
759};
760
761
Harald Welte0aa62ae2008-09-09 15:58:27 +0800762static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
763 struct snd_ctl_elem_info *uinfo)
764{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200765 static const char * const texts[] = { "OFF", "ON" };
766
767 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
768 uinfo->count = 1;
769 uinfo->value.enumerated.items = 2;
770 if (uinfo->value.enumerated.item >= 2)
771 uinfo->value.enumerated.item = 1;
772 strcpy(uinfo->value.enumerated.name,
773 texts[uinfo->value.enumerated.item]);
774 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800775}
776
777static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
778 struct snd_ctl_elem_value *ucontrol)
779{
780 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800781 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800782
Takashi Iwaiece8d042011-06-19 16:24:21 +0200783 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800784 return 0;
785}
786
Takashi Iwai3b607e32011-07-18 16:54:40 +0200787/* adjust spec->multiout setup according to the current flags */
788static void setup_playback_multi_pcm(struct via_spec *spec)
789{
790 const struct auto_pin_cfg *cfg = &spec->autocfg;
791 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
792 spec->multiout.hp_nid = 0;
793 if (!spec->hp_independent_mode) {
794 if (!spec->hp_indep_shared)
795 spec->multiout.hp_nid = spec->hp_dac_nid;
796 } else {
797 if (spec->hp_indep_shared)
798 spec->multiout.num_dacs = cfg->line_outs - 1;
799 }
800}
801
802/* update DAC setups according to indep-HP switch;
803 * this function is called only when indep-HP is modified
804 */
805static void switch_indep_hp_dacs(struct hda_codec *codec)
806{
807 struct via_spec *spec = codec->spec;
808 int shared = spec->hp_indep_shared;
809 hda_nid_t shared_dac, hp_dac;
810
811 if (!spec->opened_streams)
812 return;
813
814 shared_dac = shared ? spec->multiout.dac_nids[shared] : 0;
815 hp_dac = spec->hp_dac_nid;
816 if (spec->hp_independent_mode) {
817 /* switch to indep-HP mode */
818 if (spec->active_streams & STREAM_MULTI_OUT) {
819 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
820 __snd_hda_codec_cleanup_stream(codec, shared_dac, 1);
821 }
822 if (spec->active_streams & STREAM_INDEP_HP)
823 snd_hda_codec_setup_stream(codec, hp_dac,
824 spec->cur_hp_stream_tag, 0,
825 spec->cur_hp_format);
826 } else {
827 /* back to HP or shared-DAC */
828 if (spec->active_streams & STREAM_INDEP_HP)
829 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
830 if (spec->active_streams & STREAM_MULTI_OUT) {
831 hda_nid_t dac;
832 int ch;
833 if (shared_dac) { /* reset mutli-ch DAC */
834 dac = shared_dac;
835 ch = shared * 2;
836 } else { /* reset HP DAC */
837 dac = hp_dac;
838 ch = 0;
839 }
840 snd_hda_codec_setup_stream(codec, dac,
841 spec->cur_dac_stream_tag, ch,
842 spec->cur_dac_format);
843 }
844 }
845 setup_playback_multi_pcm(spec);
846}
847
Harald Welte0aa62ae2008-09-09 15:58:27 +0800848static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
849 struct snd_ctl_elem_value *ucontrol)
850{
851 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
852 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200853 int cur, shared;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200854
Takashi Iwai3b607e32011-07-18 16:54:40 +0200855 mutex_lock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200856 cur = !!ucontrol->value.enumerated.item[0];
Takashi Iwai3b607e32011-07-18 16:54:40 +0200857 if (spec->hp_independent_mode == cur) {
858 mutex_unlock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200859 return 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200860 }
Takashi Iwai25250502011-06-30 17:24:47 +0200861 spec->hp_independent_mode = cur;
Takashi Iwai3214b962011-07-18 12:49:25 +0200862 shared = spec->hp_indep_shared;
Takashi Iwai020066d2011-07-21 13:45:56 +0200863 deactivate_hp_paths(codec);
864 if (cur)
865 activate_output_path(codec, &spec->hp_indep_path, true, false);
866 else {
Takashi Iwai3214b962011-07-18 12:49:25 +0200867 if (shared)
868 activate_output_path(codec, &spec->out_path[shared],
Takashi Iwai25250502011-06-30 17:24:47 +0200869 true, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200870 if (spec->aamix_mode || !spec->hp_path.depth)
871 activate_output_path(codec, &spec->hp_mix_path,
872 true, false);
873 else
874 activate_output_path(codec, &spec->hp_path,
875 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200876 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800877
Takashi Iwai3b607e32011-07-18 16:54:40 +0200878 switch_indep_hp_dacs(codec);
879 mutex_unlock(&spec->config_mutex);
880
Lydia Wangce0e5a92011-03-22 16:22:37 +0800881 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800882 set_widgets_power_state(codec);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200883 via_hp_automute(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200884 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800885}
886
Takashi Iwaiece8d042011-06-19 16:24:21 +0200887static const struct snd_kcontrol_new via_hp_mixer = {
888 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
889 .name = "Independent HP",
890 .info = via_independent_hp_info,
891 .get = via_independent_hp_get,
892 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800893};
894
Takashi Iwai3d83e572010-04-14 14:36:23 +0200895static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100896{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200897 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100898 struct snd_kcontrol_new *knew;
899 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100900
Takashi Iwaiece8d042011-06-19 16:24:21 +0200901 nid = spec->autocfg.hp_pins[0];
902 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200903 if (knew == NULL)
904 return -ENOMEM;
905
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100906 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100907
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100908 return 0;
909}
910
Lydia Wang1564b282009-10-10 19:07:52 +0800911static void notify_aa_path_ctls(struct hda_codec *codec)
912{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200913 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800914 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800915
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200916 for (i = 0; i < spec->smart51_nums; i++) {
917 struct snd_kcontrol *ctl;
918 struct snd_ctl_elem_id id;
919 memset(&id, 0, sizeof(id));
920 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
921 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800922 ctl = snd_hda_find_mixer_ctl(codec, id.name);
923 if (ctl)
924 snd_ctl_notify(codec->bus->card,
925 SNDRV_CTL_EVENT_MASK_VALUE,
926 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800927 }
928}
929
930static void mute_aa_path(struct hda_codec *codec, int mute)
931{
932 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200933 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800934 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200935
Lydia Wang1564b282009-10-10 19:07:52 +0800936 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200937 for (i = 0; i < spec->smart51_nums; i++) {
938 if (spec->smart51_idxs[i] < 0)
939 continue;
940 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
941 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800942 HDA_AMP_MUTE, val);
943 }
944}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200945
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200946static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
947{
948 struct via_spec *spec = codec->spec;
949 int i;
950
951 for (i = 0; i < spec->smart51_nums; i++)
952 if (spec->smart51_pins[i] == pin)
953 return true;
954 return false;
955}
956
Lydia Wang1564b282009-10-10 19:07:52 +0800957static int via_smart51_get(struct snd_kcontrol *kcontrol,
958 struct snd_ctl_elem_value *ucontrol)
959{
960 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
961 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800962
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200963 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800964 return 0;
965}
966
967static int via_smart51_put(struct snd_kcontrol *kcontrol,
968 struct snd_ctl_elem_value *ucontrol)
969{
970 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
971 struct via_spec *spec = codec->spec;
972 int out_in = *ucontrol->value.integer.value
973 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800974 int i;
975
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200976 for (i = 0; i < spec->smart51_nums; i++) {
977 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200978 unsigned int parm;
979
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200980 parm = snd_hda_codec_read(codec, nid, 0,
981 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
982 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
983 parm |= out_in;
984 snd_hda_codec_write(codec, nid, 0,
985 AC_VERB_SET_PIN_WIDGET_CONTROL,
986 parm);
987 if (out_in == AC_PINCTL_OUT_EN) {
988 mute_aa_path(codec, 1);
989 notify_aa_path_ctls(codec);
990 }
Lydia Wang1564b282009-10-10 19:07:52 +0800991 }
992 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800993 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800994 return 1;
995}
996
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200997static const struct snd_kcontrol_new via_smart51_mixer = {
998 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
999 .name = "Smart 5.1",
1000 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001001 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +02001002 .get = via_smart51_get,
1003 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +08001004};
1005
Takashi Iwaif4a78282011-06-17 18:46:48 +02001006static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001007{
Takashi Iwaif4a78282011-06-17 18:46:48 +02001008 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001009
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001010 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +08001011 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001012 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001013 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001014 return 0;
1015}
1016
Takashi Iwaiada509e2011-06-20 15:40:19 +02001017/* check AA path's mute status */
1018static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001019{
Lydia Wangf5271102009-10-10 19:07:35 +08001020 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001021 const struct hda_amp_list *p;
1022 int i, ch, v;
1023
1024 for (i = 0; i < spec->num_loopbacks; i++) {
1025 p = &spec->loopback_list[i];
1026 for (ch = 0; ch < 2; ch++) {
1027 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
1028 p->idx);
1029 if (!(v & HDA_AMP_MUTE) && v > 0)
1030 return false;
Lydia Wangf5271102009-10-10 19:07:35 +08001031 }
1032 }
Takashi Iwaiada509e2011-06-20 15:40:19 +02001033 return true;
Lydia Wangf5271102009-10-10 19:07:35 +08001034}
1035
1036/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +02001037static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001038{
1039 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001040 bool enable;
1041 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +08001042
Takashi Iwai3b607e32011-07-18 16:54:40 +02001043 enable = is_aa_path_mute(codec) && (spec->opened_streams != 0);
Lydia Wangf5271102009-10-10 19:07:35 +08001044
1045 /* decide low current mode's verb & parameter */
1046 switch (spec->codec_type) {
1047 case VT1708B_8CH:
1048 case VT1708B_4CH:
1049 verb = 0xf70;
1050 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1051 break;
1052 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001053 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001054 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001055 verb = 0xf73;
1056 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1057 break;
1058 case VT1702:
1059 verb = 0xf73;
1060 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1061 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001062 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001063 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +08001064 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +08001065 verb = 0xf93;
1066 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1067 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001068 default:
1069 return; /* other codecs are not supported */
1070 }
1071 /* send verb */
1072 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1073}
1074
Joseph Chanc577b8a2006-11-29 15:29:40 +01001075/*
1076 * generic initialization of ADC, input mixers and output mixers
1077 */
Takashi Iwai096a8852011-06-20 12:09:02 +02001078static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001079 /* power down jack detect function */
1080 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001081 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001082};
1083
Takashi Iwai3b607e32011-07-18 16:54:40 +02001084static void set_stream_open(struct hda_codec *codec, int bit, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001085{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001086 struct via_spec *spec = codec->spec;
1087
1088 if (active)
Takashi Iwai3b607e32011-07-18 16:54:40 +02001089 spec->opened_streams |= bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001090 else
Takashi Iwai3b607e32011-07-18 16:54:40 +02001091 spec->opened_streams &= ~bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001092 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001093}
1094
Takashi Iwaiece8d042011-06-19 16:24:21 +02001095static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001096 struct hda_codec *codec,
1097 struct snd_pcm_substream *substream)
1098{
1099 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001100 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001101 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001102
Takashi Iwai25250502011-06-30 17:24:47 +02001103 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
Takashi Iwai25250502011-06-30 17:24:47 +02001104 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001105 set_stream_open(codec, STREAM_MULTI_OUT, true);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001106 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1107 hinfo);
1108 if (err < 0) {
Takashi Iwai3b607e32011-07-18 16:54:40 +02001109 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001110 return err;
1111 }
1112 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001113}
1114
Takashi Iwaiece8d042011-06-19 16:24:21 +02001115static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001116 struct hda_codec *codec,
1117 struct snd_pcm_substream *substream)
1118{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001119 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001120 return 0;
1121}
1122
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001123static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1124 struct hda_codec *codec,
1125 struct snd_pcm_substream *substream)
1126{
1127 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001128
Takashi Iwaiece8d042011-06-19 16:24:21 +02001129 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001130 return -EINVAL;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001131 set_stream_open(codec, STREAM_INDEP_HP, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001132 return 0;
1133}
1134
1135static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1136 struct hda_codec *codec,
1137 struct snd_pcm_substream *substream)
1138{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001139 set_stream_open(codec, STREAM_INDEP_HP, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001140 return 0;
1141}
1142
1143static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1144 struct hda_codec *codec,
1145 unsigned int stream_tag,
1146 unsigned int format,
1147 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001148{
1149 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001150
Takashi Iwai3b607e32011-07-18 16:54:40 +02001151 mutex_lock(&spec->config_mutex);
1152 setup_playback_multi_pcm(spec);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001153 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1154 format, substream);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001155 /* remember for dynamic DAC switch with indep-HP */
1156 spec->active_streams |= STREAM_MULTI_OUT;
1157 spec->cur_dac_stream_tag = stream_tag;
1158 spec->cur_dac_format = format;
1159 mutex_unlock(&spec->config_mutex);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001160 vt1708_start_hp_work(spec);
1161 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001162}
1163
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001164static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1165 struct hda_codec *codec,
1166 unsigned int stream_tag,
1167 unsigned int format,
1168 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001169{
1170 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001171
Takashi Iwai3b607e32011-07-18 16:54:40 +02001172 mutex_lock(&spec->config_mutex);
1173 if (spec->hp_independent_mode)
1174 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1175 stream_tag, 0, format);
1176 spec->active_streams |= STREAM_INDEP_HP;
1177 spec->cur_hp_stream_tag = stream_tag;
1178 spec->cur_hp_format = format;
1179 mutex_unlock(&spec->config_mutex);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001180 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001181 return 0;
1182}
1183
1184static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1185 struct hda_codec *codec,
1186 struct snd_pcm_substream *substream)
1187{
1188 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001189
Takashi Iwai3b607e32011-07-18 16:54:40 +02001190 mutex_lock(&spec->config_mutex);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001191 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001192 spec->active_streams &= ~STREAM_MULTI_OUT;
1193 mutex_unlock(&spec->config_mutex);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001194 vt1708_stop_hp_work(spec);
1195 return 0;
1196}
1197
1198static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1199 struct hda_codec *codec,
1200 struct snd_pcm_substream *substream)
1201{
1202 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001203
Takashi Iwai3b607e32011-07-18 16:54:40 +02001204 mutex_lock(&spec->config_mutex);
1205 if (spec->hp_independent_mode)
1206 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
1207 spec->active_streams &= ~STREAM_INDEP_HP;
1208 mutex_unlock(&spec->config_mutex);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001209 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001210 return 0;
1211}
1212
Joseph Chanc577b8a2006-11-29 15:29:40 +01001213/*
1214 * Digital out
1215 */
1216static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1217 struct hda_codec *codec,
1218 struct snd_pcm_substream *substream)
1219{
1220 struct via_spec *spec = codec->spec;
1221 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1222}
1223
1224static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1225 struct hda_codec *codec,
1226 struct snd_pcm_substream *substream)
1227{
1228 struct via_spec *spec = codec->spec;
1229 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1230}
1231
Harald Welte5691ec72008-09-15 22:42:26 +08001232static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001233 struct hda_codec *codec,
1234 unsigned int stream_tag,
1235 unsigned int format,
1236 struct snd_pcm_substream *substream)
1237{
1238 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001239 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1240 stream_tag, format, substream);
1241}
Harald Welte5691ec72008-09-15 22:42:26 +08001242
Takashi Iwai9da29272009-05-07 16:31:14 +02001243static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1244 struct hda_codec *codec,
1245 struct snd_pcm_substream *substream)
1246{
1247 struct via_spec *spec = codec->spec;
1248 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001249 return 0;
1250}
1251
Joseph Chanc577b8a2006-11-29 15:29:40 +01001252/*
1253 * Analog capture
1254 */
1255static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1256 struct hda_codec *codec,
1257 unsigned int stream_tag,
1258 unsigned int format,
1259 struct snd_pcm_substream *substream)
1260{
1261 struct via_spec *spec = codec->spec;
1262
1263 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1264 stream_tag, 0, format);
1265 return 0;
1266}
1267
1268static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1269 struct hda_codec *codec,
1270 struct snd_pcm_substream *substream)
1271{
1272 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001273 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001274 return 0;
1275}
1276
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001277/* analog capture with dynamic ADC switching */
1278static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1279 struct hda_codec *codec,
1280 unsigned int stream_tag,
1281 unsigned int format,
1282 struct snd_pcm_substream *substream)
1283{
1284 struct via_spec *spec = codec->spec;
1285 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1286
Takashi Iwai3b607e32011-07-18 16:54:40 +02001287 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001288 spec->cur_adc = spec->adc_nids[adc_idx];
1289 spec->cur_adc_stream_tag = stream_tag;
1290 spec->cur_adc_format = format;
1291 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001292 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001293 return 0;
1294}
1295
1296static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1297 struct hda_codec *codec,
1298 struct snd_pcm_substream *substream)
1299{
1300 struct via_spec *spec = codec->spec;
1301
Takashi Iwai3b607e32011-07-18 16:54:40 +02001302 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001303 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1304 spec->cur_adc = 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001305 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001306 return 0;
1307}
1308
1309/* re-setup the stream if running; called from input-src put */
1310static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1311{
1312 struct via_spec *spec = codec->spec;
1313 int adc_idx = spec->inputs[cur].adc_idx;
1314 hda_nid_t adc = spec->adc_nids[adc_idx];
Takashi Iwai3b607e32011-07-18 16:54:40 +02001315 bool ret = false;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001316
Takashi Iwai3b607e32011-07-18 16:54:40 +02001317 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001318 if (spec->cur_adc && spec->cur_adc != adc) {
1319 /* stream is running, let's swap the current ADC */
1320 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1321 spec->cur_adc = adc;
1322 snd_hda_codec_setup_stream(codec, adc,
1323 spec->cur_adc_stream_tag, 0,
1324 spec->cur_adc_format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001325 ret = true;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001326 }
Takashi Iwai3b607e32011-07-18 16:54:40 +02001327 mutex_unlock(&spec->config_mutex);
1328 return ret;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001329}
1330
Takashi Iwai9af74212011-06-18 16:17:45 +02001331static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001332 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001333 .channels_min = 2,
1334 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001335 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001336 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001337 .open = via_playback_multi_pcm_open,
1338 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001339 .prepare = via_playback_multi_pcm_prepare,
1340 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001341 },
1342};
1343
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001344static const struct hda_pcm_stream via_pcm_hp_playback = {
1345 .substreams = 1,
1346 .channels_min = 2,
1347 .channels_max = 2,
1348 /* NID is set in via_build_pcms */
1349 .ops = {
1350 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001351 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001352 .prepare = via_playback_hp_pcm_prepare,
1353 .cleanup = via_playback_hp_pcm_cleanup
1354 },
1355};
1356
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001357static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001358 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001359 .channels_min = 2,
1360 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001361 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001362 /* We got noisy outputs on the right channel on VT1708 when
1363 * 24bit samples are used. Until any workaround is found,
1364 * disable the 24bit format, so far.
1365 */
1366 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1367 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001368 .open = via_playback_multi_pcm_open,
1369 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001370 .prepare = via_playback_multi_pcm_prepare,
1371 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001372 },
1373};
1374
Takashi Iwai9af74212011-06-18 16:17:45 +02001375static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001376 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001377 .channels_min = 2,
1378 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001379 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001380 .ops = {
1381 .prepare = via_capture_pcm_prepare,
1382 .cleanup = via_capture_pcm_cleanup
1383 },
1384};
1385
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001386static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1387 .substreams = 1,
1388 .channels_min = 2,
1389 .channels_max = 2,
1390 /* NID is set in via_build_pcms */
1391 .ops = {
1392 .prepare = via_dyn_adc_capture_pcm_prepare,
1393 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1394 },
1395};
1396
Takashi Iwai9af74212011-06-18 16:17:45 +02001397static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001398 .substreams = 1,
1399 .channels_min = 2,
1400 .channels_max = 2,
1401 /* NID is set in via_build_pcms */
1402 .ops = {
1403 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001404 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001405 .prepare = via_dig_playback_pcm_prepare,
1406 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001407 },
1408};
1409
Takashi Iwai9af74212011-06-18 16:17:45 +02001410static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001411 .substreams = 1,
1412 .channels_min = 2,
1413 .channels_max = 2,
1414};
1415
Takashi Iwai370bafb2011-06-20 12:47:45 +02001416/*
1417 * slave controls for virtual master
1418 */
1419static const char * const via_slave_vols[] = {
1420 "Front Playback Volume",
1421 "Surround Playback Volume",
1422 "Center Playback Volume",
1423 "LFE Playback Volume",
1424 "Side Playback Volume",
1425 "Headphone Playback Volume",
1426 "Speaker Playback Volume",
1427 NULL,
1428};
1429
1430static const char * const via_slave_sws[] = {
1431 "Front Playback Switch",
1432 "Surround Playback Switch",
1433 "Center Playback Switch",
1434 "LFE Playback Switch",
1435 "Side Playback Switch",
1436 "Headphone Playback Switch",
1437 "Speaker Playback Switch",
1438 NULL,
1439};
1440
Joseph Chanc577b8a2006-11-29 15:29:40 +01001441static int via_build_controls(struct hda_codec *codec)
1442{
1443 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001444 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001445 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001446
Takashi Iwai24088a52011-06-17 16:59:21 +02001447 if (spec->set_widgets_power_state)
1448 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1449 return -ENOMEM;
1450
Joseph Chanc577b8a2006-11-29 15:29:40 +01001451 for (i = 0; i < spec->num_mixers; i++) {
1452 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1453 if (err < 0)
1454 return err;
1455 }
1456
1457 if (spec->multiout.dig_out_nid) {
1458 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001459 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001460 spec->multiout.dig_out_nid);
1461 if (err < 0)
1462 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001463 err = snd_hda_create_spdif_share_sw(codec,
1464 &spec->multiout);
1465 if (err < 0)
1466 return err;
1467 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001468 }
1469 if (spec->dig_in_nid) {
1470 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1471 if (err < 0)
1472 return err;
1473 }
Lydia Wang17314372009-10-10 19:07:37 +08001474
Takashi Iwai370bafb2011-06-20 12:47:45 +02001475 /* if we have no master control, let's create it */
1476 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1477 unsigned int vmaster_tlv[4];
1478 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1479 HDA_OUTPUT, vmaster_tlv);
1480 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1481 vmaster_tlv, via_slave_vols);
1482 if (err < 0)
1483 return err;
1484 }
1485 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1486 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1487 NULL, via_slave_sws);
1488 if (err < 0)
1489 return err;
1490 }
1491
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001492 /* assign Capture Source enums to NID */
1493 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1494 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001495 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001496 if (err < 0)
1497 return err;
1498 }
1499
Lydia Wang17314372009-10-10 19:07:37 +08001500 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001501 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001502 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001503
Takashi Iwai603c4012008-07-30 15:01:44 +02001504 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001505 return 0;
1506}
1507
1508static int via_build_pcms(struct hda_codec *codec)
1509{
1510 struct via_spec *spec = codec->spec;
1511 struct hda_pcm *info = spec->pcm_rec;
1512
1513 codec->num_pcms = 1;
1514 codec->pcm_info = info;
1515
Takashi Iwai82673bc2011-06-17 16:24:21 +02001516 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1517 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001518 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001519
1520 if (!spec->stream_analog_playback)
1521 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001522 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001523 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001524 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1525 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001526 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1527 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001528
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001529 if (!spec->stream_analog_capture) {
1530 if (spec->dyn_adc_switch)
1531 spec->stream_analog_capture =
1532 &via_pcm_dyn_adc_analog_capture;
1533 else
1534 spec->stream_analog_capture = &via_pcm_analog_capture;
1535 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001536 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1537 *spec->stream_analog_capture;
1538 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001539 if (!spec->dyn_adc_switch)
1540 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1541 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001542
1543 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1544 codec->num_pcms++;
1545 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001546 snprintf(spec->stream_name_digital,
1547 sizeof(spec->stream_name_digital),
1548 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001549 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001550 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001551 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001552 if (!spec->stream_digital_playback)
1553 spec->stream_digital_playback =
1554 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001555 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001556 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001557 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1558 spec->multiout.dig_out_nid;
1559 }
1560 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001561 if (!spec->stream_digital_capture)
1562 spec->stream_digital_capture =
1563 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001564 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001565 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001566 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1567 spec->dig_in_nid;
1568 }
1569 }
1570
Takashi Iwaiece8d042011-06-19 16:24:21 +02001571 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001572 codec->num_pcms++;
1573 info++;
1574 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1575 "%s HP", codec->chip_name);
1576 info->name = spec->stream_name_hp;
1577 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1578 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001579 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001580 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001581 return 0;
1582}
1583
1584static void via_free(struct hda_codec *codec)
1585{
1586 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001587
1588 if (!spec)
1589 return;
1590
Takashi Iwai603c4012008-07-30 15:01:44 +02001591 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001592 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001593 kfree(spec->bind_cap_vol);
1594 kfree(spec->bind_cap_sw);
1595 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001596}
1597
Takashi Iwai64be2852011-06-17 16:51:39 +02001598/* mute/unmute outputs */
1599static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1600 hda_nid_t *pins, bool mute)
1601{
1602 int i;
Takashi Iwai94994732011-07-11 11:36:44 +02001603 for (i = 0; i < num_pins; i++) {
1604 unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
1605 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1606 if (parm & AC_PINCTL_IN_EN)
1607 continue;
1608 if (mute)
1609 parm &= ~AC_PINCTL_OUT_EN;
1610 else
1611 parm |= AC_PINCTL_OUT_EN;
Takashi Iwai64be2852011-06-17 16:51:39 +02001612 snd_hda_codec_write(codec, pins[i], 0,
Takashi Iwai94994732011-07-11 11:36:44 +02001613 AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
1614 }
Takashi Iwai64be2852011-06-17 16:51:39 +02001615}
1616
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001617/* mute internal speaker if line-out is plugged */
1618static void via_line_automute(struct hda_codec *codec, int present)
1619{
1620 struct via_spec *spec = codec->spec;
1621
1622 if (!spec->autocfg.speaker_outs)
1623 return;
1624 if (!present)
1625 present = snd_hda_jack_detect(codec,
1626 spec->autocfg.line_out_pins[0]);
1627 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1628 spec->autocfg.speaker_pins,
1629 present);
1630}
1631
Harald Welte69e52a82008-09-09 15:57:32 +08001632/* mute internal speaker if HP is plugged */
1633static void via_hp_automute(struct hda_codec *codec)
1634{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001635 int present = 0;
Takashi Iwai6e969d92011-07-11 11:28:13 +02001636 int nums;
Harald Welte69e52a82008-09-09 15:57:32 +08001637 struct via_spec *spec = codec->spec;
1638
Takashi Iwai6e969d92011-07-11 11:28:13 +02001639 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0])
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001640 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai6e969d92011-07-11 11:28:13 +02001641
1642 if (spec->smart51_enabled)
1643 nums = spec->autocfg.line_outs + spec->smart51_nums;
1644 else
1645 nums = spec->autocfg.line_outs;
1646 toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
1647
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001648 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001649}
1650
Harald Welte69e52a82008-09-09 15:57:32 +08001651static void via_gpio_control(struct hda_codec *codec)
1652{
1653 unsigned int gpio_data;
1654 unsigned int vol_counter;
1655 unsigned int vol;
1656 unsigned int master_vol;
1657
1658 struct via_spec *spec = codec->spec;
1659
1660 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1661 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1662
1663 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1664 0xF84, 0) & 0x3F0000) >> 16;
1665
1666 vol = vol_counter & 0x1F;
1667 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1668 AC_VERB_GET_AMP_GAIN_MUTE,
1669 AC_AMP_GET_INPUT);
1670
1671 if (gpio_data == 0x02) {
1672 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001673 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1674 AC_VERB_SET_PIN_WIDGET_CONTROL,
1675 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001676 if (vol_counter & 0x20) {
1677 /* decrease volume */
1678 if (vol > master_vol)
1679 vol = master_vol;
1680 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1681 0, HDA_AMP_VOLMASK,
1682 master_vol-vol);
1683 } else {
1684 /* increase volume */
1685 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1686 HDA_AMP_VOLMASK,
1687 ((master_vol+vol) > 0x2A) ? 0x2A :
1688 (master_vol+vol));
1689 }
1690 } else if (!(gpio_data & 0x02)) {
1691 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001692 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1693 AC_VERB_SET_PIN_WIDGET_CONTROL,
1694 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001695 }
1696}
1697
1698/* unsolicited event for jack sensing */
1699static void via_unsol_event(struct hda_codec *codec,
1700 unsigned int res)
1701{
1702 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001703
Lydia Wanga34df192009-10-10 19:08:01 +08001704 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001705 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001706
1707 res &= ~VIA_JACK_EVENT;
1708
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001709 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001710 via_hp_automute(codec);
1711 else if (res == VIA_GPIO_EVENT)
1712 via_gpio_control(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001713}
1714
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001715#ifdef SND_HDA_NEEDS_RESUME
1716static int via_suspend(struct hda_codec *codec, pm_message_t state)
1717{
1718 struct via_spec *spec = codec->spec;
1719 vt1708_stop_hp_work(spec);
1720 return 0;
1721}
1722#endif
1723
Takashi Iwaicb53c622007-08-10 17:21:45 +02001724#ifdef CONFIG_SND_HDA_POWER_SAVE
1725static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1726{
1727 struct via_spec *spec = codec->spec;
1728 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1729}
1730#endif
1731
Joseph Chanc577b8a2006-11-29 15:29:40 +01001732/*
1733 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001734
1735static int via_init(struct hda_codec *codec);
1736
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001737static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001738 .build_controls = via_build_controls,
1739 .build_pcms = via_build_pcms,
1740 .init = via_init,
1741 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001742 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001743#ifdef SND_HDA_NEEDS_RESUME
1744 .suspend = via_suspend,
1745#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001746#ifdef CONFIG_SND_HDA_POWER_SAVE
1747 .check_power_status = via_check_power_status,
1748#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001749};
1750
Takashi Iwai4a796162011-06-17 17:53:38 +02001751static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001752{
Takashi Iwai4a796162011-06-17 17:53:38 +02001753 struct via_spec *spec = codec->spec;
1754 int i;
1755
1756 for (i = 0; i < spec->multiout.num_dacs; i++) {
1757 if (spec->multiout.dac_nids[i] == dac)
1758 return false;
1759 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001760 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001761 return false;
1762 return true;
1763}
1764
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001765static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001766 hda_nid_t target_dac, int with_aa_mix,
1767 struct nid_path *path, int depth)
Takashi Iwai4a796162011-06-17 17:53:38 +02001768{
Takashi Iwai3214b962011-07-18 12:49:25 +02001769 struct via_spec *spec = codec->spec;
Takashi Iwai4a796162011-06-17 17:53:38 +02001770 hda_nid_t conn[8];
1771 int i, nums;
1772
Takashi Iwai3214b962011-07-18 12:49:25 +02001773 if (nid == spec->aa_mix_nid) {
1774 if (!with_aa_mix)
1775 return false;
1776 with_aa_mix = 2; /* mark aa-mix is included */
1777 }
1778
Takashi Iwai4a796162011-06-17 17:53:38 +02001779 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1780 for (i = 0; i < nums; i++) {
1781 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1782 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001783 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1784 /* aa-mix is requested but not included? */
1785 if (!(spec->aa_mix_nid && with_aa_mix == 1))
1786 goto found;
1787 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001788 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001789 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001790 return false;
1791 for (i = 0; i < nums; i++) {
1792 unsigned int type;
1793 type = get_wcaps_type(get_wcaps(codec, conn[i]));
Takashi Iwai3214b962011-07-18 12:49:25 +02001794 if (type == AC_WID_AUD_OUT)
Takashi Iwai4a796162011-06-17 17:53:38 +02001795 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001796 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai3214b962011-07-18 12:49:25 +02001797 with_aa_mix, path, depth + 1))
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001798 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001799 }
1800 return false;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001801
1802 found:
1803 path->path[path->depth] = conn[i];
1804 path->idx[path->depth] = i;
1805 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1806 path->multi[path->depth] = 1;
1807 path->depth++;
1808 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001809}
1810
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001811static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001812 hda_nid_t target_dac, int with_aa_mix,
1813 struct nid_path *path)
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001814{
Takashi Iwai3214b962011-07-18 12:49:25 +02001815 if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001816 path->path[path->depth] = nid;
1817 path->depth++;
Takashi Iwai3214b962011-07-18 12:49:25 +02001818 snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
1819 path->depth, path->path[0], path->path[1],
1820 path->path[2], path->path[3], path->path[4]);
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001821 return true;
1822 }
1823 return false;
1824}
1825
Takashi Iwai4a796162011-06-17 17:53:38 +02001826static int via_auto_fill_dac_nids(struct hda_codec *codec)
1827{
1828 struct via_spec *spec = codec->spec;
1829 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001830 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001831 hda_nid_t nid;
1832
Joseph Chanc577b8a2006-11-29 15:29:40 +01001833 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001834 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001835 for (i = 0; i < cfg->line_outs; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001836 hda_nid_t dac = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001837 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001838 if (!nid)
1839 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001840 if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
1841 dac = spec->out_path[i].path[0];
1842 if (!i && parse_output_path(codec, nid, dac, 1,
1843 &spec->out_mix_path))
1844 dac = spec->out_mix_path.path[0];
1845 if (dac) {
1846 spec->private_dac_nids[i] = dac;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001847 dac_num++;
1848 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001849 }
Takashi Iwai3214b962011-07-18 12:49:25 +02001850 if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
1851 spec->out_path[0] = spec->out_mix_path;
1852 spec->out_mix_path.depth = 0;
1853 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001854 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001855 return 0;
1856}
1857
Takashi Iwai4a796162011-06-17 17:53:38 +02001858static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001859 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001860{
Takashi Iwai4a796162011-06-17 17:53:38 +02001861 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001862 char name[32];
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001863 hda_nid_t dac, pin, sel, nid;
1864 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001865
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001866 dac = check_dac ? path->path[0] : 0;
1867 pin = path->path[path->depth - 1];
1868 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001869
Takashi Iwai8df2a312011-06-21 11:48:29 +02001870 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001871 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001872 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001873 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001874 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1875 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001876 else
1877 nid = 0;
1878 if (nid) {
1879 sprintf(name, "%s Playback Volume", pfx);
1880 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001881 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001882 if (err < 0)
1883 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001884 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001885 }
1886
Takashi Iwai8df2a312011-06-21 11:48:29 +02001887 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001888 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001889 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001890 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001891 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1892 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001893 else
1894 nid = 0;
1895 if (nid) {
1896 sprintf(name, "%s Playback Switch", pfx);
1897 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1898 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1899 if (err < 0)
1900 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001901 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001902 }
1903 return 0;
1904}
1905
Takashi Iwaif4a78282011-06-17 18:46:48 +02001906static void mangle_smart51(struct hda_codec *codec)
1907{
1908 struct via_spec *spec = codec->spec;
1909 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001910 struct auto_pin_cfg_item *ins = cfg->inputs;
1911 int i, j, nums, attr;
1912 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001913
Takashi Iwai0f98c242011-06-21 12:51:33 +02001914 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1915 nums = 0;
1916 for (i = 0; i < cfg->num_inputs; i++) {
1917 unsigned int def;
1918 if (ins[i].type > AUTO_PIN_LINE_IN)
1919 continue;
1920 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1921 if (snd_hda_get_input_pin_attr(def) != attr)
1922 continue;
1923 for (j = 0; j < nums; j++)
1924 if (ins[pins[j]].type < ins[i].type) {
1925 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001926 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001927 break;
1928 }
1929 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001930 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001931 }
1932 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001933 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001934 for (i = 0; i < nums; i++) {
1935 hda_nid_t pin = ins[pins[i]].pin;
1936 spec->smart51_pins[spec->smart51_nums++] = pin;
1937 cfg->line_out_pins[cfg->line_outs++] = pin;
1938 if (cfg->line_outs == 3)
1939 break;
1940 }
1941 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001942 }
1943}
1944
Takashi Iwai020066d2011-07-21 13:45:56 +02001945static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src)
1946{
1947 dst->vol_ctl = src->vol_ctl;
1948 dst->mute_ctl = src->mute_ctl;
1949}
1950
Takashi Iwai4a796162011-06-17 17:53:38 +02001951/* add playback controls from the parsed DAC table */
1952static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1953{
1954 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001955 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai3214b962011-07-18 12:49:25 +02001956 struct nid_path *path;
Takashi Iwaiea734962011-01-17 11:29:34 +01001957 static const char * const chname[4] = {
1958 "Front", "Surround", "C/LFE", "Side"
1959 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001960 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001961 int old_line_outs;
1962
1963 /* check smart51 */
1964 old_line_outs = cfg->line_outs;
1965 if (cfg->line_outs == 1)
1966 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001967
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001968 err = via_auto_fill_dac_nids(codec);
1969 if (err < 0)
1970 return err;
1971
Lydia Wang5c9a5612011-07-08 14:03:43 +08001972 if (spec->multiout.num_dacs < 3) {
1973 spec->smart51_nums = 0;
1974 cfg->line_outs = old_line_outs;
1975 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001976 for (i = 0; i < cfg->line_outs; i++) {
1977 hda_nid_t pin, dac;
1978 pin = cfg->line_out_pins[i];
1979 dac = spec->multiout.dac_nids[i];
1980 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001981 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001982 path = spec->out_path + i;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001983 if (i == HDA_CLFE) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001984 err = create_ch_ctls(codec, "Center", 1, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001985 if (err < 0)
1986 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02001987 err = create_ch_ctls(codec, "LFE", 2, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001988 if (err < 0)
1989 return err;
1990 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001991 const char *pfx = chname[i];
1992 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1993 cfg->line_outs == 1)
1994 pfx = "Speaker";
Takashi Iwai3214b962011-07-18 12:49:25 +02001995 err = create_ch_ctls(codec, pfx, 3, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001996 if (err < 0)
1997 return err;
1998 }
Takashi Iwai020066d2011-07-21 13:45:56 +02001999 if (path != spec->out_path + i)
2000 copy_path_mixer_ctls(&spec->out_path[i], path);
2001 if (path == spec->out_path && spec->out_mix_path.depth)
2002 copy_path_mixer_ctls(&spec->out_mix_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002003 }
2004
Takashi Iwai4a796162011-06-17 17:53:38 +02002005 idx = get_connection_index(codec, spec->aa_mix_nid,
2006 spec->multiout.dac_nids[0]);
2007 if (idx >= 0) {
2008 /* add control to mixer */
Takashi Iwai3214b962011-07-18 12:49:25 +02002009 const char *name;
2010 name = spec->out_mix_path.depth ?
2011 "PCM Loopback Playback Volume" : "PCM Playback Volume";
2012 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002013 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2014 idx, HDA_INPUT));
2015 if (err < 0)
2016 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002017 name = spec->out_mix_path.depth ?
2018 "PCM Loopback Playback Switch" : "PCM Playback Switch";
2019 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002020 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2021 idx, HDA_INPUT));
2022 if (err < 0)
2023 return err;
2024 }
2025
Takashi Iwaif4a78282011-06-17 18:46:48 +02002026 cfg->line_outs = old_line_outs;
2027
Joseph Chanc577b8a2006-11-29 15:29:40 +01002028 return 0;
2029}
2030
Takashi Iwai4a796162011-06-17 17:53:38 +02002031static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002032{
Takashi Iwai4a796162011-06-17 17:53:38 +02002033 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02002034 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002035 bool check_dac;
Takashi Iwai3214b962011-07-18 12:49:25 +02002036 int i, err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002037
2038 if (!pin)
2039 return 0;
2040
Takashi Iwai3214b962011-07-18 12:49:25 +02002041 if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
2042 for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
2043 if (i < spec->multiout.num_dacs &&
2044 parse_output_path(codec, pin,
2045 spec->multiout.dac_nids[i], 0,
2046 &spec->hp_indep_path)) {
2047 spec->hp_indep_shared = i;
2048 break;
2049 }
2050 }
Takashi Iwai25250502011-06-30 17:24:47 +02002051 }
Takashi Iwai3214b962011-07-18 12:49:25 +02002052 if (spec->hp_indep_path.depth) {
2053 spec->hp_dac_nid = spec->hp_indep_path.path[0];
2054 if (!spec->hp_indep_shared)
2055 spec->hp_path = spec->hp_indep_path;
2056 }
2057 /* optionally check front-path w/o AA-mix */
2058 if (!spec->hp_path.depth)
2059 parse_output_path(codec, pin,
2060 spec->multiout.dac_nids[HDA_FRONT], 0,
2061 &spec->hp_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002062
Takashi Iwaiece8d042011-06-19 16:24:21 +02002063 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai3214b962011-07-18 12:49:25 +02002064 1, &spec->hp_mix_path) && !spec->hp_path.depth)
Takashi Iwaiece8d042011-06-19 16:24:21 +02002065 return 0;
2066
Takashi Iwai3214b962011-07-18 12:49:25 +02002067 if (spec->hp_path.depth) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +02002068 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002069 check_dac = true;
2070 } else {
Takashi Iwai3214b962011-07-18 12:49:25 +02002071 path = &spec->hp_mix_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002072 check_dac = false;
2073 }
2074 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002075 if (err < 0)
2076 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002077 if (check_dac)
2078 copy_path_mixer_ctls(&spec->hp_mix_path, path);
2079 else
2080 copy_path_mixer_ctls(&spec->hp_path, path);
2081 if (spec->hp_indep_path.depth)
2082 copy_path_mixer_ctls(&spec->hp_indep_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002083 return 0;
2084}
2085
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002086static int via_auto_create_speaker_ctls(struct hda_codec *codec)
2087{
2088 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +02002089 struct nid_path *path;
2090 bool check_dac;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002091 hda_nid_t pin, dac;
Takashi Iwai3214b962011-07-18 12:49:25 +02002092 int err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002093
2094 pin = spec->autocfg.speaker_pins[0];
2095 if (!spec->autocfg.speaker_outs || !pin)
2096 return 0;
2097
Takashi Iwai3214b962011-07-18 12:49:25 +02002098 if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02002099 dac = spec->speaker_path.path[0];
Takashi Iwai3214b962011-07-18 12:49:25 +02002100 if (!dac)
2101 parse_output_path(codec, pin,
2102 spec->multiout.dac_nids[HDA_FRONT], 0,
2103 &spec->speaker_path);
2104 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
2105 1, &spec->speaker_mix_path) && !dac)
2106 return 0;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002107
Takashi Iwai3214b962011-07-18 12:49:25 +02002108 /* no AA-path for front? */
2109 if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
2110 dac = 0;
2111
2112 spec->speaker_dac_nid = dac;
2113 spec->multiout.extra_out_nid[0] = dac;
2114 if (dac) {
2115 path = &spec->speaker_path;
2116 check_dac = true;
2117 } else {
2118 path = &spec->speaker_mix_path;
2119 check_dac = false;
2120 }
2121 err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
2122 if (err < 0)
2123 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002124 if (check_dac)
2125 copy_path_mixer_ctls(&spec->speaker_mix_path, path);
2126 else
2127 copy_path_mixer_ctls(&spec->speaker_path, path);
Takashi Iwai3214b962011-07-18 12:49:25 +02002128 return 0;
2129}
2130
2131#define via_aamix_ctl_info via_pin_power_ctl_info
2132
2133static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol,
2134 struct snd_ctl_elem_value *ucontrol)
2135{
2136 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2137 struct via_spec *spec = codec->spec;
2138 ucontrol->value.enumerated.item[0] = spec->aamix_mode;
2139 return 0;
2140}
2141
2142static void update_aamix_paths(struct hda_codec *codec, int do_mix,
2143 struct nid_path *nomix, struct nid_path *mix)
2144{
2145 if (do_mix) {
2146 activate_output_path(codec, nomix, false, false);
2147 activate_output_path(codec, mix, true, false);
2148 } else {
2149 activate_output_path(codec, mix, false, false);
2150 activate_output_path(codec, nomix, true, false);
2151 }
2152}
2153
2154static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol,
2155 struct snd_ctl_elem_value *ucontrol)
2156{
2157 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2158 struct via_spec *spec = codec->spec;
2159 unsigned int val = ucontrol->value.enumerated.item[0];
2160
2161 if (val == spec->aamix_mode)
2162 return 0;
2163 spec->aamix_mode = val;
2164 /* update front path */
2165 update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
2166 /* update HP path */
2167 if (!spec->hp_independent_mode) {
2168 update_aamix_paths(codec, val, &spec->hp_path,
2169 &spec->hp_mix_path);
2170 }
2171 /* update speaker path */
2172 update_aamix_paths(codec, val, &spec->speaker_path,
2173 &spec->speaker_mix_path);
2174 return 1;
2175}
2176
2177static const struct snd_kcontrol_new via_aamix_ctl_enum = {
2178 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2179 .name = "Loopback Mixing",
2180 .info = via_aamix_ctl_info,
2181 .get = via_aamix_ctl_get,
2182 .put = via_aamix_ctl_put,
2183};
2184
2185static int via_auto_create_loopback_switch(struct hda_codec *codec)
2186{
2187 struct via_spec *spec = codec->spec;
2188
2189 if (!spec->aa_mix_nid || !spec->out_mix_path.depth)
2190 return 0; /* no loopback switching available */
2191 if (!via_clone_control(spec, &via_aamix_ctl_enum))
2192 return -ENOMEM;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002193 return 0;
2194}
2195
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002196/* look for ADCs */
2197static int via_fill_adcs(struct hda_codec *codec)
2198{
2199 struct via_spec *spec = codec->spec;
2200 hda_nid_t nid = codec->start_nid;
2201 int i;
2202
2203 for (i = 0; i < codec->num_nodes; i++, nid++) {
2204 unsigned int wcaps = get_wcaps(codec, nid);
2205 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2206 continue;
2207 if (wcaps & AC_WCAP_DIGITAL)
2208 continue;
2209 if (!(wcaps & AC_WCAP_CONN_LIST))
2210 continue;
2211 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2212 return -ENOMEM;
2213 spec->adc_nids[spec->num_adc_nids++] = nid;
2214 }
2215 return 0;
2216}
2217
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002218/* input-src control */
2219static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2220 struct snd_ctl_elem_info *uinfo)
2221{
2222 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2223 struct via_spec *spec = codec->spec;
2224
2225 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2226 uinfo->count = 1;
2227 uinfo->value.enumerated.items = spec->num_inputs;
2228 if (uinfo->value.enumerated.item >= spec->num_inputs)
2229 uinfo->value.enumerated.item = spec->num_inputs - 1;
2230 strcpy(uinfo->value.enumerated.name,
2231 spec->inputs[uinfo->value.enumerated.item].label);
2232 return 0;
2233}
2234
2235static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2236 struct snd_ctl_elem_value *ucontrol)
2237{
2238 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2239 struct via_spec *spec = codec->spec;
2240 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2241
2242 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2243 return 0;
2244}
2245
2246static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2247 struct snd_ctl_elem_value *ucontrol)
2248{
2249 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2250 struct via_spec *spec = codec->spec;
2251 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2252 hda_nid_t mux;
2253 int cur;
2254
2255 cur = ucontrol->value.enumerated.item[0];
2256 if (cur < 0 || cur >= spec->num_inputs)
2257 return -EINVAL;
2258 if (spec->cur_mux[idx] == cur)
2259 return 0;
2260 spec->cur_mux[idx] = cur;
2261 if (spec->dyn_adc_switch) {
2262 int adc_idx = spec->inputs[cur].adc_idx;
2263 mux = spec->mux_nids[adc_idx];
2264 via_dyn_adc_pcm_resetup(codec, cur);
2265 } else {
2266 mux = spec->mux_nids[idx];
2267 if (snd_BUG_ON(!mux))
2268 return -EINVAL;
2269 }
2270
2271 if (mux) {
2272 /* switch to D0 beofre change index */
2273 if (snd_hda_codec_read(codec, mux, 0,
2274 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2275 snd_hda_codec_write(codec, mux, 0,
2276 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2277 snd_hda_codec_write(codec, mux, 0,
2278 AC_VERB_SET_CONNECT_SEL,
2279 spec->inputs[cur].mux_idx);
2280 }
2281
2282 /* update jack power state */
2283 set_widgets_power_state(codec);
2284 return 0;
2285}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002286
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002287static const struct snd_kcontrol_new via_input_src_ctl = {
2288 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2289 /* The multiple "Capture Source" controls confuse alsamixer
2290 * So call somewhat different..
2291 */
2292 /* .name = "Capture Source", */
2293 .name = "Input Source",
2294 .info = via_mux_enum_info,
2295 .get = via_mux_enum_get,
2296 .put = via_mux_enum_put,
2297};
2298
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002299static int create_input_src_ctls(struct hda_codec *codec, int count)
2300{
2301 struct via_spec *spec = codec->spec;
2302 struct snd_kcontrol_new *knew;
2303
2304 if (spec->num_inputs <= 1 || !count)
2305 return 0; /* no need for single src */
2306
2307 knew = via_clone_control(spec, &via_input_src_ctl);
2308 if (!knew)
2309 return -ENOMEM;
2310 knew->count = count;
2311 return 0;
2312}
2313
2314/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002315static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2316{
2317 struct hda_amp_list *list;
2318
2319 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2320 return;
2321 list = spec->loopback_list + spec->num_loopbacks;
2322 list->nid = mix;
2323 list->dir = HDA_INPUT;
2324 list->idx = idx;
2325 spec->num_loopbacks++;
2326 spec->loopback.amplist = spec->loopback_list;
2327}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002328
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002329static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002330 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002331{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002332 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002333}
2334
2335/* add the input-route to the given pin */
2336static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002337{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002338 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002339 int c, idx;
2340
2341 spec->inputs[spec->num_inputs].adc_idx = -1;
2342 spec->inputs[spec->num_inputs].pin = pin;
2343 for (c = 0; c < spec->num_adc_nids; c++) {
2344 if (spec->mux_nids[c]) {
2345 idx = get_connection_index(codec, spec->mux_nids[c],
2346 pin);
2347 if (idx < 0)
2348 continue;
2349 spec->inputs[spec->num_inputs].mux_idx = idx;
2350 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002351 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002352 continue;
2353 }
2354 spec->inputs[spec->num_inputs].adc_idx = c;
2355 /* Can primary ADC satisfy all inputs? */
2356 if (!spec->dyn_adc_switch &&
2357 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2358 snd_printd(KERN_INFO
2359 "via: dynamic ADC switching enabled\n");
2360 spec->dyn_adc_switch = 1;
2361 }
2362 return true;
2363 }
2364 return false;
2365}
2366
2367static int get_mux_nids(struct hda_codec *codec);
2368
2369/* parse input-routes; fill ADCs, MUXs and input-src entries */
2370static int parse_analog_inputs(struct hda_codec *codec)
2371{
2372 struct via_spec *spec = codec->spec;
2373 const struct auto_pin_cfg *cfg = &spec->autocfg;
2374 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002375
2376 err = via_fill_adcs(codec);
2377 if (err < 0)
2378 return err;
2379 err = get_mux_nids(codec);
2380 if (err < 0)
2381 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002382
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002383 /* fill all input-routes */
2384 for (i = 0; i < cfg->num_inputs; i++) {
2385 if (add_input_route(codec, cfg->inputs[i].pin))
2386 spec->inputs[spec->num_inputs++].label =
2387 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002388 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002389
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002390 /* check for internal loopback recording */
2391 if (spec->aa_mix_nid &&
2392 add_input_route(codec, spec->aa_mix_nid))
2393 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2394
2395 return 0;
2396}
2397
2398/* create analog-loopback volume/switch controls */
2399static int create_loopback_ctls(struct hda_codec *codec)
2400{
2401 struct via_spec *spec = codec->spec;
2402 const struct auto_pin_cfg *cfg = &spec->autocfg;
2403 const char *prev_label = NULL;
2404 int type_idx = 0;
2405 int i, j, err, idx;
2406
2407 if (!spec->aa_mix_nid)
2408 return 0;
2409
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002410 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002411 hda_nid_t pin = cfg->inputs[i].pin;
2412 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2413
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002414 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002415 type_idx++;
2416 else
2417 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002418 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002419 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2420 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002421 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002422 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002423 if (err < 0)
2424 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002425 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002426 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002427
2428 /* remember the label for smart51 control */
2429 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002430 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002431 spec->smart51_idxs[j] = idx;
2432 spec->smart51_labels[j] = label;
2433 break;
2434 }
2435 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002436 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002437 return 0;
2438}
2439
2440/* create mic-boost controls (if present) */
2441static int create_mic_boost_ctls(struct hda_codec *codec)
2442{
2443 struct via_spec *spec = codec->spec;
2444 const struct auto_pin_cfg *cfg = &spec->autocfg;
2445 int i, err;
2446
2447 for (i = 0; i < cfg->num_inputs; i++) {
2448 hda_nid_t pin = cfg->inputs[i].pin;
2449 unsigned int caps;
2450 const char *label;
2451 char name[32];
2452
2453 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2454 continue;
2455 caps = query_amp_caps(codec, pin, HDA_INPUT);
2456 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2457 continue;
2458 label = hda_get_autocfg_input_label(codec, cfg, i);
2459 snprintf(name, sizeof(name), "%s Boost Volume", label);
2460 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2461 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2462 if (err < 0)
2463 return err;
2464 }
2465 return 0;
2466}
2467
2468/* create capture and input-src controls for multiple streams */
2469static int create_multi_adc_ctls(struct hda_codec *codec)
2470{
2471 struct via_spec *spec = codec->spec;
2472 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002473
2474 /* create capture mixer elements */
2475 for (i = 0; i < spec->num_adc_nids; i++) {
2476 hda_nid_t adc = spec->adc_nids[i];
2477 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2478 "Capture Volume", i,
2479 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2480 HDA_INPUT));
2481 if (err < 0)
2482 return err;
2483 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2484 "Capture Switch", i,
2485 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2486 HDA_INPUT));
2487 if (err < 0)
2488 return err;
2489 }
2490
2491 /* input-source control */
2492 for (i = 0; i < spec->num_adc_nids; i++)
2493 if (!spec->mux_nids[i])
2494 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002495 err = create_input_src_ctls(codec, i);
2496 if (err < 0)
2497 return err;
2498 return 0;
2499}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002500
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002501/* bind capture volume/switch */
2502static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2503 HDA_BIND_VOL("Capture Volume", 0);
2504static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2505 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002506
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002507static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2508 struct hda_ctl_ops *ops)
2509{
2510 struct hda_bind_ctls *ctl;
2511 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002512
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002513 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2514 if (!ctl)
2515 return -ENOMEM;
2516 ctl->ops = ops;
2517 for (i = 0; i < spec->num_adc_nids; i++)
2518 ctl->values[i] =
2519 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2520 *ctl_ret = ctl;
2521 return 0;
2522}
2523
2524/* create capture and input-src controls for dynamic ADC-switch case */
2525static int create_dyn_adc_ctls(struct hda_codec *codec)
2526{
2527 struct via_spec *spec = codec->spec;
2528 struct snd_kcontrol_new *knew;
2529 int err;
2530
2531 /* set up the bind capture ctls */
2532 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2533 if (err < 0)
2534 return err;
2535 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2536 if (err < 0)
2537 return err;
2538
2539 /* create capture mixer elements */
2540 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2541 if (!knew)
2542 return -ENOMEM;
2543 knew->private_value = (long)spec->bind_cap_vol;
2544
2545 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2546 if (!knew)
2547 return -ENOMEM;
2548 knew->private_value = (long)spec->bind_cap_sw;
2549
2550 /* input-source control */
2551 err = create_input_src_ctls(codec, 1);
2552 if (err < 0)
2553 return err;
2554 return 0;
2555}
2556
2557/* parse and create capture-related stuff */
2558static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2559{
2560 struct via_spec *spec = codec->spec;
2561 int err;
2562
2563 err = parse_analog_inputs(codec);
2564 if (err < 0)
2565 return err;
2566 if (spec->dyn_adc_switch)
2567 err = create_dyn_adc_ctls(codec);
2568 else
2569 err = create_multi_adc_ctls(codec);
2570 if (err < 0)
2571 return err;
2572 err = create_loopback_ctls(codec);
2573 if (err < 0)
2574 return err;
2575 err = create_mic_boost_ctls(codec);
2576 if (err < 0)
2577 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002578 return 0;
2579}
2580
Harald Welte76d9b0d2008-09-09 15:50:37 +08002581static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2582{
2583 unsigned int def_conf;
2584 unsigned char seqassoc;
2585
Takashi Iwai2f334f92009-02-20 14:37:42 +01002586 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002587 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2588 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002589 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2590 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2591 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2592 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002593 }
2594
2595 return;
2596}
2597
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002598static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002599 struct snd_ctl_elem_value *ucontrol)
2600{
2601 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2602 struct via_spec *spec = codec->spec;
2603
2604 if (spec->codec_type != VT1708)
2605 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002606 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002607 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002608 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002609 return 0;
2610}
2611
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002612static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002613 struct snd_ctl_elem_value *ucontrol)
2614{
2615 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2616 struct via_spec *spec = codec->spec;
2617 int change;
2618
2619 if (spec->codec_type != VT1708)
2620 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002621 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002622 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002623 == !spec->vt1708_jack_detect;
2624 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002625 mute_aa_path(codec, 1);
2626 notify_aa_path_ctls(codec);
2627 }
2628 return change;
2629}
2630
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002631static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2632 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2633 .name = "Jack Detect",
2634 .count = 1,
2635 .info = snd_ctl_boolean_mono_info,
2636 .get = vt1708_jack_detect_get,
2637 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002638};
2639
Takashi Iwai12daef62011-06-18 17:45:49 +02002640static void fill_dig_outs(struct hda_codec *codec);
2641static void fill_dig_in(struct hda_codec *codec);
2642
2643static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002644{
2645 struct via_spec *spec = codec->spec;
2646 int err;
2647
2648 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2649 if (err < 0)
2650 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002651 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002652 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002653
Takashi Iwai4a796162011-06-17 17:53:38 +02002654 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002655 if (err < 0)
2656 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002657 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002658 if (err < 0)
2659 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002660 err = via_auto_create_speaker_ctls(codec);
2661 if (err < 0)
2662 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002663 err = via_auto_create_loopback_switch(codec);
2664 if (err < 0)
2665 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002666 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002667 if (err < 0)
2668 return err;
2669
2670 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2671
Takashi Iwai12daef62011-06-18 17:45:49 +02002672 fill_dig_outs(codec);
2673 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002674
Takashi Iwai603c4012008-07-30 15:01:44 +02002675 if (spec->kctls.list)
2676 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002677
Joseph Chanc577b8a2006-11-29 15:29:40 +01002678
Takashi Iwai3214b962011-07-18 12:49:25 +02002679 if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002680 err = via_hp_build(codec);
2681 if (err < 0)
2682 return err;
2683 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002684
Takashi Iwaif4a78282011-06-17 18:46:48 +02002685 err = via_smart51_build(codec);
2686 if (err < 0)
2687 return err;
2688
Takashi Iwai5d417622011-06-20 11:32:27 +02002689 /* assign slave outs */
2690 if (spec->slave_dig_outs[0])
2691 codec->slave_dig_outs = spec->slave_dig_outs;
2692
Joseph Chanc577b8a2006-11-29 15:29:40 +01002693 return 1;
2694}
2695
Takashi Iwai5d417622011-06-20 11:32:27 +02002696static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002697{
Lydia Wang25eaba22009-10-10 19:08:43 +08002698 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002699 if (spec->multiout.dig_out_nid)
2700 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2701 if (spec->slave_dig_outs[0])
2702 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2703}
Lydia Wang25eaba22009-10-10 19:08:43 +08002704
Takashi Iwai5d417622011-06-20 11:32:27 +02002705static void via_auto_init_dig_in(struct hda_codec *codec)
2706{
2707 struct via_spec *spec = codec->spec;
2708 if (!spec->dig_in_nid)
2709 return;
2710 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2711 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2712}
2713
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002714/* initialize the unsolicited events */
2715static void via_auto_init_unsol_event(struct hda_codec *codec)
2716{
2717 struct via_spec *spec = codec->spec;
2718 struct auto_pin_cfg *cfg = &spec->autocfg;
2719 unsigned int ev;
2720 int i;
2721
2722 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2723 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2724 AC_VERB_SET_UNSOLICITED_ENABLE,
2725 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2726
2727 if (cfg->speaker_pins[0])
2728 ev = VIA_LINE_EVENT;
2729 else
2730 ev = 0;
2731 for (i = 0; i < cfg->line_outs; i++) {
2732 if (cfg->line_out_pins[i] &&
2733 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002734 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002735 AC_VERB_SET_UNSOLICITED_ENABLE,
2736 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2737 }
2738
2739 for (i = 0; i < cfg->num_inputs; i++) {
2740 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2741 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2742 AC_VERB_SET_UNSOLICITED_ENABLE,
2743 AC_USRSP_EN | VIA_JACK_EVENT);
2744 }
2745}
2746
Takashi Iwai5d417622011-06-20 11:32:27 +02002747static int via_init(struct hda_codec *codec)
2748{
2749 struct via_spec *spec = codec->spec;
2750 int i;
2751
2752 for (i = 0; i < spec->num_iverbs; i++)
2753 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2754
Joseph Chanc577b8a2006-11-29 15:29:40 +01002755 via_auto_init_multi_out(codec);
2756 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002757 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002758 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002759 via_auto_init_dig_outs(codec);
2760 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002761
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002762 via_auto_init_unsol_event(codec);
2763
2764 via_hp_automute(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002765
Joseph Chanc577b8a2006-11-29 15:29:40 +01002766 return 0;
2767}
2768
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002769static void vt1708_update_hp_jack_state(struct work_struct *work)
2770{
2771 struct via_spec *spec = container_of(work, struct via_spec,
2772 vt1708_hp_work.work);
2773 if (spec->codec_type != VT1708)
2774 return;
2775 /* if jack state toggled */
2776 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002777 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002778 spec->vt1708_hp_present ^= 1;
2779 via_hp_automute(spec->codec);
2780 }
2781 vt1708_start_hp_work(spec);
2782}
2783
Takashi Iwai337b9d02009-07-07 18:18:59 +02002784static int get_mux_nids(struct hda_codec *codec)
2785{
2786 struct via_spec *spec = codec->spec;
2787 hda_nid_t nid, conn[8];
2788 unsigned int type;
2789 int i, n;
2790
2791 for (i = 0; i < spec->num_adc_nids; i++) {
2792 nid = spec->adc_nids[i];
2793 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002794 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002795 if (type == AC_WID_PIN)
2796 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002797 n = snd_hda_get_connections(codec, nid, conn,
2798 ARRAY_SIZE(conn));
2799 if (n <= 0)
2800 break;
2801 if (n > 1) {
2802 spec->mux_nids[i] = nid;
2803 break;
2804 }
2805 nid = conn[0];
2806 }
2807 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002808 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002809}
2810
Joseph Chanc577b8a2006-11-29 15:29:40 +01002811static int patch_vt1708(struct hda_codec *codec)
2812{
2813 struct via_spec *spec;
2814 int err;
2815
2816 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002817 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002818 if (spec == NULL)
2819 return -ENOMEM;
2820
Takashi Iwai620e2b22011-06-17 17:19:19 +02002821 spec->aa_mix_nid = 0x17;
2822
Takashi Iwai12daef62011-06-18 17:45:49 +02002823 /* Add HP and CD pin config connect bit re-config action */
2824 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2825 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2826
Joseph Chanc577b8a2006-11-29 15:29:40 +01002827 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002828 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002829 if (err < 0) {
2830 via_free(codec);
2831 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002832 }
2833
Takashi Iwai12daef62011-06-18 17:45:49 +02002834 /* add jack detect on/off control */
2835 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2836 return -ENOMEM;
2837
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002838 /* disable 32bit format on VT1708 */
2839 if (codec->vendor_id == 0x11061708)
2840 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002841
Lydia Wange322a362011-06-29 13:52:02 +08002842 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2843
Joseph Chanc577b8a2006-11-29 15:29:40 +01002844 codec->patch_ops = via_patch_ops;
2845
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002846 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002847 return 0;
2848}
2849
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002850static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002851{
2852 struct via_spec *spec;
2853 int err;
2854
2855 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002856 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002857 if (spec == NULL)
2858 return -ENOMEM;
2859
Takashi Iwai620e2b22011-06-17 17:19:19 +02002860 spec->aa_mix_nid = 0x18;
2861
Takashi Iwai12daef62011-06-18 17:45:49 +02002862 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002863 if (err < 0) {
2864 via_free(codec);
2865 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002866 }
2867
Joseph Chanc577b8a2006-11-29 15:29:40 +01002868 codec->patch_ops = via_patch_ops;
2869
Josepch Chanf7278fd2007-12-13 16:40:40 +01002870 return 0;
2871}
2872
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002873static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2874{
2875 struct via_spec *spec = codec->spec;
2876 int imux_is_smixer;
2877 unsigned int parm;
2878 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002879 if ((spec->codec_type != VT1708B_4CH) &&
2880 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002881 is_8ch = 1;
2882
2883 /* SW0 (17h) = stereo mixer */
2884 imux_is_smixer =
2885 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2886 == ((spec->codec_type == VT1708S) ? 5 : 0));
2887 /* inputs */
2888 /* PW 1/2/5 (1ah/1bh/1eh) */
2889 parm = AC_PWRST_D3;
2890 set_pin_power_state(codec, 0x1a, &parm);
2891 set_pin_power_state(codec, 0x1b, &parm);
2892 set_pin_power_state(codec, 0x1e, &parm);
2893 if (imux_is_smixer)
2894 parm = AC_PWRST_D0;
2895 /* SW0 (17h), AIW 0/1 (13h/14h) */
2896 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2897 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2898 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2899
2900 /* outputs */
2901 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2902 parm = AC_PWRST_D3;
2903 set_pin_power_state(codec, 0x19, &parm);
2904 if (spec->smart51_enabled)
2905 set_pin_power_state(codec, 0x1b, &parm);
2906 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2907 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2908
2909 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2910 if (is_8ch) {
2911 parm = AC_PWRST_D3;
2912 set_pin_power_state(codec, 0x22, &parm);
2913 if (spec->smart51_enabled)
2914 set_pin_power_state(codec, 0x1a, &parm);
2915 snd_hda_codec_write(codec, 0x26, 0,
2916 AC_VERB_SET_POWER_STATE, parm);
2917 snd_hda_codec_write(codec, 0x24, 0,
2918 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002919 } else if (codec->vendor_id == 0x11064397) {
2920 /* PW7(23h), SW2(27h), AOW2(25h) */
2921 parm = AC_PWRST_D3;
2922 set_pin_power_state(codec, 0x23, &parm);
2923 if (spec->smart51_enabled)
2924 set_pin_power_state(codec, 0x1a, &parm);
2925 snd_hda_codec_write(codec, 0x27, 0,
2926 AC_VERB_SET_POWER_STATE, parm);
2927 snd_hda_codec_write(codec, 0x25, 0,
2928 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002929 }
2930
2931 /* PW 3/4/7 (1ch/1dh/23h) */
2932 parm = AC_PWRST_D3;
2933 /* force to D0 for internal Speaker */
2934 set_pin_power_state(codec, 0x1c, &parm);
2935 set_pin_power_state(codec, 0x1d, &parm);
2936 if (is_8ch)
2937 set_pin_power_state(codec, 0x23, &parm);
2938
2939 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2940 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2941 imux_is_smixer ? AC_PWRST_D0 : parm);
2942 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2943 if (is_8ch) {
2944 snd_hda_codec_write(codec, 0x25, 0,
2945 AC_VERB_SET_POWER_STATE, parm);
2946 snd_hda_codec_write(codec, 0x27, 0,
2947 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002948 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2949 snd_hda_codec_write(codec, 0x25, 0,
2950 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002951}
2952
Lydia Wang518bf3b2009-10-10 19:07:29 +08002953static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002954static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002955{
2956 struct via_spec *spec;
2957 int err;
2958
Lydia Wang518bf3b2009-10-10 19:07:29 +08002959 if (get_codec_type(codec) == VT1708BCE)
2960 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002961
Josepch Chanf7278fd2007-12-13 16:40:40 +01002962 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002963 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002964 if (spec == NULL)
2965 return -ENOMEM;
2966
Takashi Iwai620e2b22011-06-17 17:19:19 +02002967 spec->aa_mix_nid = 0x16;
2968
Josepch Chanf7278fd2007-12-13 16:40:40 +01002969 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002970 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002971 if (err < 0) {
2972 via_free(codec);
2973 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002974 }
2975
Josepch Chanf7278fd2007-12-13 16:40:40 +01002976 codec->patch_ops = via_patch_ops;
2977
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002978 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2979
Josepch Chanf7278fd2007-12-13 16:40:40 +01002980 return 0;
2981}
2982
Harald Welted949cac2008-09-09 15:56:01 +08002983/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002984static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002985 /* Enable Mic Boost Volume backdoor */
2986 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002987 /* don't bybass mixer */
2988 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002989 { }
2990};
2991
Takashi Iwai9da29272009-05-07 16:31:14 +02002992/* fill out digital output widgets; one for master and one for slave outputs */
2993static void fill_dig_outs(struct hda_codec *codec)
2994{
2995 struct via_spec *spec = codec->spec;
2996 int i;
2997
2998 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2999 hda_nid_t nid;
3000 int conn;
3001
3002 nid = spec->autocfg.dig_out_pins[i];
3003 if (!nid)
3004 continue;
3005 conn = snd_hda_get_connections(codec, nid, &nid, 1);
3006 if (conn < 1)
3007 continue;
3008 if (!spec->multiout.dig_out_nid)
3009 spec->multiout.dig_out_nid = nid;
3010 else {
3011 spec->slave_dig_outs[0] = nid;
3012 break; /* at most two dig outs */
3013 }
3014 }
3015}
3016
Takashi Iwai12daef62011-06-18 17:45:49 +02003017static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08003018{
3019 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02003020 hda_nid_t dig_nid;
3021 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08003022
Takashi Iwai12daef62011-06-18 17:45:49 +02003023 if (!spec->autocfg.dig_in_pin)
3024 return;
Harald Welted949cac2008-09-09 15:56:01 +08003025
Takashi Iwai12daef62011-06-18 17:45:49 +02003026 dig_nid = codec->start_nid;
3027 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
3028 unsigned int wcaps = get_wcaps(codec, dig_nid);
3029 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
3030 continue;
3031 if (!(wcaps & AC_WCAP_DIGITAL))
3032 continue;
3033 if (!(wcaps & AC_WCAP_CONN_LIST))
3034 continue;
3035 err = get_connection_index(codec, dig_nid,
3036 spec->autocfg.dig_in_pin);
3037 if (err >= 0) {
3038 spec->dig_in_nid = dig_nid;
3039 break;
3040 }
3041 }
Harald Welted949cac2008-09-09 15:56:01 +08003042}
3043
Lydia Wang6369bcf2009-10-10 19:08:31 +08003044static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
3045 int offset, int num_steps, int step_size)
3046{
3047 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
3048 (offset << AC_AMPCAP_OFFSET_SHIFT) |
3049 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
3050 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
3051 (0 << AC_AMPCAP_MUTE_SHIFT));
3052}
3053
Harald Welted949cac2008-09-09 15:56:01 +08003054static int patch_vt1708S(struct hda_codec *codec)
3055{
3056 struct via_spec *spec;
3057 int err;
3058
3059 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003060 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003061 if (spec == NULL)
3062 return -ENOMEM;
3063
Takashi Iwai620e2b22011-06-17 17:19:19 +02003064 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003065 override_mic_boost(codec, 0x1a, 0, 3, 40);
3066 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003067
Harald Welted949cac2008-09-09 15:56:01 +08003068 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003069 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003070 if (err < 0) {
3071 via_free(codec);
3072 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003073 }
3074
Takashi Iwai096a8852011-06-20 12:09:02 +02003075 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003076
Harald Welted949cac2008-09-09 15:56:01 +08003077 codec->patch_ops = via_patch_ops;
3078
Lydia Wang518bf3b2009-10-10 19:07:29 +08003079 /* correct names for VT1708BCE */
3080 if (get_codec_type(codec) == VT1708BCE) {
3081 kfree(codec->chip_name);
3082 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3083 snprintf(codec->bus->card->mixername,
3084 sizeof(codec->bus->card->mixername),
3085 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08003086 }
Lydia Wangbc92df72011-03-23 17:56:05 +08003087 /* correct names for VT1705 */
3088 if (codec->vendor_id == 0x11064397) {
3089 kfree(codec->chip_name);
3090 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3091 snprintf(codec->bus->card->mixername,
3092 sizeof(codec->bus->card->mixername),
3093 "%s %s", codec->vendor_name, codec->chip_name);
3094 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003095 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08003096 return 0;
3097}
3098
3099/* Patch for VT1702 */
3100
Takashi Iwai096a8852011-06-20 12:09:02 +02003101static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003102 /* mixer enable */
3103 {0x1, 0xF88, 0x3},
3104 /* GPIO 0~2 */
3105 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003106 { }
3107};
3108
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003109static void set_widgets_power_state_vt1702(struct hda_codec *codec)
3110{
3111 int imux_is_smixer =
3112 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3113 unsigned int parm;
3114 /* inputs */
3115 /* PW 1/2/5 (14h/15h/18h) */
3116 parm = AC_PWRST_D3;
3117 set_pin_power_state(codec, 0x14, &parm);
3118 set_pin_power_state(codec, 0x15, &parm);
3119 set_pin_power_state(codec, 0x18, &parm);
3120 if (imux_is_smixer)
3121 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
3122 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
3123 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3124 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
3125 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3126 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
3127
3128 /* outputs */
3129 /* PW 3/4 (16h/17h) */
3130 parm = AC_PWRST_D3;
3131 set_pin_power_state(codec, 0x17, &parm);
3132 set_pin_power_state(codec, 0x16, &parm);
3133 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
3134 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
3135 imux_is_smixer ? AC_PWRST_D0 : parm);
3136 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3137 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3138}
3139
Harald Welted949cac2008-09-09 15:56:01 +08003140static int patch_vt1702(struct hda_codec *codec)
3141{
3142 struct via_spec *spec;
3143 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003144
3145 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003146 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003147 if (spec == NULL)
3148 return -ENOMEM;
3149
Takashi Iwai620e2b22011-06-17 17:19:19 +02003150 spec->aa_mix_nid = 0x1a;
3151
Takashi Iwai12daef62011-06-18 17:45:49 +02003152 /* limit AA path volume to 0 dB */
3153 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3154 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3155 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3156 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3157 (1 << AC_AMPCAP_MUTE_SHIFT));
3158
Harald Welted949cac2008-09-09 15:56:01 +08003159 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003160 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003161 if (err < 0) {
3162 via_free(codec);
3163 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003164 }
3165
Takashi Iwai096a8852011-06-20 12:09:02 +02003166 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003167
Harald Welted949cac2008-09-09 15:56:01 +08003168 codec->patch_ops = via_patch_ops;
3169
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003170 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08003171 return 0;
3172}
3173
Lydia Wangeb7188c2009-10-10 19:08:34 +08003174/* Patch for VT1718S */
3175
Takashi Iwai096a8852011-06-20 12:09:02 +02003176static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08003177 /* Enable MW0 adjust Gain 5 */
3178 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003179 /* Enable Boost Volume backdoor */
3180 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02003181
Lydia Wangeb7188c2009-10-10 19:08:34 +08003182 { }
3183};
3184
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003185static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
3186{
3187 struct via_spec *spec = codec->spec;
3188 int imux_is_smixer;
3189 unsigned int parm;
3190 /* MUX6 (1eh) = stereo mixer */
3191 imux_is_smixer =
3192 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3193 /* inputs */
3194 /* PW 5/6/7 (29h/2ah/2bh) */
3195 parm = AC_PWRST_D3;
3196 set_pin_power_state(codec, 0x29, &parm);
3197 set_pin_power_state(codec, 0x2a, &parm);
3198 set_pin_power_state(codec, 0x2b, &parm);
3199 if (imux_is_smixer)
3200 parm = AC_PWRST_D0;
3201 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
3202 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3203 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3204 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3205 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3206
3207 /* outputs */
3208 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
3209 parm = AC_PWRST_D3;
3210 set_pin_power_state(codec, 0x27, &parm);
3211 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
3212 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
3213
3214 /* PW2 (26h), AOW2 (ah) */
3215 parm = AC_PWRST_D3;
3216 set_pin_power_state(codec, 0x26, &parm);
3217 if (spec->smart51_enabled)
3218 set_pin_power_state(codec, 0x2b, &parm);
3219 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
3220
3221 /* PW0 (24h), AOW0 (8h) */
3222 parm = AC_PWRST_D3;
3223 set_pin_power_state(codec, 0x24, &parm);
3224 if (!spec->hp_independent_mode) /* check for redirected HP */
3225 set_pin_power_state(codec, 0x28, &parm);
3226 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3227 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
3228 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
3229 imux_is_smixer ? AC_PWRST_D0 : parm);
3230
3231 /* PW1 (25h), AOW1 (9h) */
3232 parm = AC_PWRST_D3;
3233 set_pin_power_state(codec, 0x25, &parm);
3234 if (spec->smart51_enabled)
3235 set_pin_power_state(codec, 0x2a, &parm);
3236 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
3237
3238 if (spec->hp_independent_mode) {
3239 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3240 parm = AC_PWRST_D3;
3241 set_pin_power_state(codec, 0x28, &parm);
3242 snd_hda_codec_write(codec, 0x1b, 0,
3243 AC_VERB_SET_POWER_STATE, parm);
3244 snd_hda_codec_write(codec, 0x34, 0,
3245 AC_VERB_SET_POWER_STATE, parm);
3246 snd_hda_codec_write(codec, 0xc, 0,
3247 AC_VERB_SET_POWER_STATE, parm);
3248 }
3249}
3250
Takashi Iwai30b45032011-07-11 17:05:04 +02003251/* Add a connection to the primary DAC from AA-mixer for some codecs
3252 * This isn't listed from the raw info, but the chip has a secret connection.
3253 */
3254static int add_secret_dac_path(struct hda_codec *codec)
3255{
3256 struct via_spec *spec = codec->spec;
3257 int i, nums;
3258 hda_nid_t conn[8];
3259 hda_nid_t nid;
3260
3261 if (!spec->aa_mix_nid)
3262 return 0;
3263 nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
3264 ARRAY_SIZE(conn) - 1);
3265 for (i = 0; i < nums; i++) {
3266 if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
3267 return 0;
3268 }
3269
3270 /* find the primary DAC and add to the connection list */
3271 nid = codec->start_nid;
3272 for (i = 0; i < codec->num_nodes; i++, nid++) {
3273 unsigned int caps = get_wcaps(codec, nid);
3274 if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
3275 !(caps & AC_WCAP_DIGITAL)) {
3276 conn[nums++] = nid;
3277 return snd_hda_override_conn_list(codec,
3278 spec->aa_mix_nid,
3279 nums, conn);
3280 }
3281 }
3282 return 0;
3283}
3284
3285
Lydia Wangeb7188c2009-10-10 19:08:34 +08003286static int patch_vt1718S(struct hda_codec *codec)
3287{
3288 struct via_spec *spec;
3289 int err;
3290
3291 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003292 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003293 if (spec == NULL)
3294 return -ENOMEM;
3295
Takashi Iwai620e2b22011-06-17 17:19:19 +02003296 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003297 override_mic_boost(codec, 0x2b, 0, 3, 40);
3298 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003299 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003300
Lydia Wangeb7188c2009-10-10 19:08:34 +08003301 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003302 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003303 if (err < 0) {
3304 via_free(codec);
3305 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003306 }
3307
Takashi Iwai096a8852011-06-20 12:09:02 +02003308 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003309
Lydia Wangeb7188c2009-10-10 19:08:34 +08003310 codec->patch_ops = via_patch_ops;
3311
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003312 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3313
Lydia Wangeb7188c2009-10-10 19:08:34 +08003314 return 0;
3315}
Lydia Wangf3db4232009-10-10 19:08:41 +08003316
3317/* Patch for VT1716S */
3318
3319static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3320 struct snd_ctl_elem_info *uinfo)
3321{
3322 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3323 uinfo->count = 1;
3324 uinfo->value.integer.min = 0;
3325 uinfo->value.integer.max = 1;
3326 return 0;
3327}
3328
3329static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3330 struct snd_ctl_elem_value *ucontrol)
3331{
3332 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3333 int index = 0;
3334
3335 index = snd_hda_codec_read(codec, 0x26, 0,
3336 AC_VERB_GET_CONNECT_SEL, 0);
3337 if (index != -1)
3338 *ucontrol->value.integer.value = index;
3339
3340 return 0;
3341}
3342
3343static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3344 struct snd_ctl_elem_value *ucontrol)
3345{
3346 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3347 struct via_spec *spec = codec->spec;
3348 int index = *ucontrol->value.integer.value;
3349
3350 snd_hda_codec_write(codec, 0x26, 0,
3351 AC_VERB_SET_CONNECT_SEL, index);
3352 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003353 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003354 return 1;
3355}
3356
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003357static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003358 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3359 {
3360 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3361 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003362 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003363 .count = 1,
3364 .info = vt1716s_dmic_info,
3365 .get = vt1716s_dmic_get,
3366 .put = vt1716s_dmic_put,
3367 },
3368 {} /* end */
3369};
3370
3371
3372/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003373static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003374 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3375 { } /* end */
3376};
3377
Takashi Iwai096a8852011-06-20 12:09:02 +02003378static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003379 /* Enable Boost Volume backdoor */
3380 {0x1, 0xf8a, 0x80},
3381 /* don't bybass mixer */
3382 {0x1, 0xf88, 0xc0},
3383 /* Enable mono output */
3384 {0x1, 0xf90, 0x08},
3385 { }
3386};
3387
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003388static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3389{
3390 struct via_spec *spec = codec->spec;
3391 int imux_is_smixer;
3392 unsigned int parm;
3393 unsigned int mono_out, present;
3394 /* SW0 (17h) = stereo mixer */
3395 imux_is_smixer =
3396 (snd_hda_codec_read(codec, 0x17, 0,
3397 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3398 /* inputs */
3399 /* PW 1/2/5 (1ah/1bh/1eh) */
3400 parm = AC_PWRST_D3;
3401 set_pin_power_state(codec, 0x1a, &parm);
3402 set_pin_power_state(codec, 0x1b, &parm);
3403 set_pin_power_state(codec, 0x1e, &parm);
3404 if (imux_is_smixer)
3405 parm = AC_PWRST_D0;
3406 /* SW0 (17h), AIW0(13h) */
3407 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3408 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3409
3410 parm = AC_PWRST_D3;
3411 set_pin_power_state(codec, 0x1e, &parm);
3412 /* PW11 (22h) */
3413 if (spec->dmic_enabled)
3414 set_pin_power_state(codec, 0x22, &parm);
3415 else
3416 snd_hda_codec_write(codec, 0x22, 0,
3417 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3418
3419 /* SW2(26h), AIW1(14h) */
3420 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3421 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3422
3423 /* outputs */
3424 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3425 parm = AC_PWRST_D3;
3426 set_pin_power_state(codec, 0x19, &parm);
3427 /* Smart 5.1 PW2(1bh) */
3428 if (spec->smart51_enabled)
3429 set_pin_power_state(codec, 0x1b, &parm);
3430 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3431 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3432
3433 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3434 parm = AC_PWRST_D3;
3435 set_pin_power_state(codec, 0x23, &parm);
3436 /* Smart 5.1 PW1(1ah) */
3437 if (spec->smart51_enabled)
3438 set_pin_power_state(codec, 0x1a, &parm);
3439 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3440
3441 /* Smart 5.1 PW5(1eh) */
3442 if (spec->smart51_enabled)
3443 set_pin_power_state(codec, 0x1e, &parm);
3444 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3445
3446 /* Mono out */
3447 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3448 present = snd_hda_jack_detect(codec, 0x1c);
3449
3450 if (present)
3451 mono_out = 0;
3452 else {
3453 present = snd_hda_jack_detect(codec, 0x1d);
3454 if (!spec->hp_independent_mode && present)
3455 mono_out = 0;
3456 else
3457 mono_out = 1;
3458 }
3459 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3460 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3461 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3462 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3463
3464 /* PW 3/4 (1ch/1dh) */
3465 parm = AC_PWRST_D3;
3466 set_pin_power_state(codec, 0x1c, &parm);
3467 set_pin_power_state(codec, 0x1d, &parm);
3468 /* HP Independent Mode, power on AOW3 */
3469 if (spec->hp_independent_mode)
3470 snd_hda_codec_write(codec, 0x25, 0,
3471 AC_VERB_SET_POWER_STATE, parm);
3472
3473 /* force to D0 for internal Speaker */
3474 /* MW0 (16h), AOW0 (10h) */
3475 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3476 imux_is_smixer ? AC_PWRST_D0 : parm);
3477 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3478 mono_out ? AC_PWRST_D0 : parm);
3479}
3480
Lydia Wangf3db4232009-10-10 19:08:41 +08003481static int patch_vt1716S(struct hda_codec *codec)
3482{
3483 struct via_spec *spec;
3484 int err;
3485
3486 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003487 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003488 if (spec == NULL)
3489 return -ENOMEM;
3490
Takashi Iwai620e2b22011-06-17 17:19:19 +02003491 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003492 override_mic_boost(codec, 0x1a, 0, 3, 40);
3493 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003494
Lydia Wangf3db4232009-10-10 19:08:41 +08003495 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003496 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003497 if (err < 0) {
3498 via_free(codec);
3499 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003500 }
3501
Takashi Iwai096a8852011-06-20 12:09:02 +02003502 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003503
Lydia Wangf3db4232009-10-10 19:08:41 +08003504 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3505 spec->num_mixers++;
3506
3507 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3508
3509 codec->patch_ops = via_patch_ops;
3510
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003511 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003512 return 0;
3513}
Lydia Wang25eaba22009-10-10 19:08:43 +08003514
3515/* for vt2002P */
3516
Takashi Iwai096a8852011-06-20 12:09:02 +02003517static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003518 /* Class-D speaker related verbs */
3519 {0x1, 0xfe0, 0x4},
3520 {0x1, 0xfe9, 0x80},
3521 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003522 /* Enable Boost Volume backdoor */
3523 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003524 /* Enable AOW0 to MW9 */
3525 {0x1, 0xfb8, 0x88},
3526 { }
3527};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003528
Takashi Iwai096a8852011-06-20 12:09:02 +02003529static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003530 /* Enable Boost Volume backdoor */
3531 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003532 /* Enable AOW0 to MW9 */
3533 {0x1, 0xfb8, 0x88},
3534 { }
3535};
Lydia Wang25eaba22009-10-10 19:08:43 +08003536
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003537static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3538{
3539 struct via_spec *spec = codec->spec;
3540 int imux_is_smixer;
3541 unsigned int parm;
3542 unsigned int present;
3543 /* MUX9 (1eh) = stereo mixer */
3544 imux_is_smixer =
3545 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3546 /* inputs */
3547 /* PW 5/6/7 (29h/2ah/2bh) */
3548 parm = AC_PWRST_D3;
3549 set_pin_power_state(codec, 0x29, &parm);
3550 set_pin_power_state(codec, 0x2a, &parm);
3551 set_pin_power_state(codec, 0x2b, &parm);
3552 parm = AC_PWRST_D0;
3553 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3554 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3555 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3556 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3557 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3558
3559 /* outputs */
3560 /* AOW0 (8h)*/
3561 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3562
Lydia Wang118909562011-03-23 17:57:34 +08003563 if (spec->codec_type == VT1802) {
3564 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3565 parm = AC_PWRST_D3;
3566 set_pin_power_state(codec, 0x28, &parm);
3567 snd_hda_codec_write(codec, 0x18, 0,
3568 AC_VERB_SET_POWER_STATE, parm);
3569 snd_hda_codec_write(codec, 0x38, 0,
3570 AC_VERB_SET_POWER_STATE, parm);
3571 } else {
3572 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3573 parm = AC_PWRST_D3;
3574 set_pin_power_state(codec, 0x26, &parm);
3575 snd_hda_codec_write(codec, 0x1c, 0,
3576 AC_VERB_SET_POWER_STATE, parm);
3577 snd_hda_codec_write(codec, 0x37, 0,
3578 AC_VERB_SET_POWER_STATE, parm);
3579 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003580
Lydia Wang118909562011-03-23 17:57:34 +08003581 if (spec->codec_type == VT1802) {
3582 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3583 parm = AC_PWRST_D3;
3584 set_pin_power_state(codec, 0x25, &parm);
3585 snd_hda_codec_write(codec, 0x15, 0,
3586 AC_VERB_SET_POWER_STATE, parm);
3587 snd_hda_codec_write(codec, 0x35, 0,
3588 AC_VERB_SET_POWER_STATE, parm);
3589 } else {
3590 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3591 parm = AC_PWRST_D3;
3592 set_pin_power_state(codec, 0x25, &parm);
3593 snd_hda_codec_write(codec, 0x19, 0,
3594 AC_VERB_SET_POWER_STATE, parm);
3595 snd_hda_codec_write(codec, 0x35, 0,
3596 AC_VERB_SET_POWER_STATE, parm);
3597 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003598
3599 if (spec->hp_independent_mode)
3600 snd_hda_codec_write(codec, 0x9, 0,
3601 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3602
3603 /* Class-D */
3604 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3605 present = snd_hda_jack_detect(codec, 0x25);
3606
3607 parm = AC_PWRST_D3;
3608 set_pin_power_state(codec, 0x24, &parm);
3609 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003610 if (spec->codec_type == VT1802)
3611 snd_hda_codec_write(codec, 0x14, 0,
3612 AC_VERB_SET_POWER_STATE, parm);
3613 else
3614 snd_hda_codec_write(codec, 0x18, 0,
3615 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003616 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3617
3618 /* Mono Out */
3619 present = snd_hda_jack_detect(codec, 0x26);
3620
3621 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003622 if (spec->codec_type == VT1802) {
3623 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3624 snd_hda_codec_write(codec, 0x33, 0,
3625 AC_VERB_SET_POWER_STATE, parm);
3626 snd_hda_codec_write(codec, 0x1c, 0,
3627 AC_VERB_SET_POWER_STATE, parm);
3628 snd_hda_codec_write(codec, 0x3c, 0,
3629 AC_VERB_SET_POWER_STATE, parm);
3630 } else {
3631 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3632 snd_hda_codec_write(codec, 0x31, 0,
3633 AC_VERB_SET_POWER_STATE, parm);
3634 snd_hda_codec_write(codec, 0x17, 0,
3635 AC_VERB_SET_POWER_STATE, parm);
3636 snd_hda_codec_write(codec, 0x3b, 0,
3637 AC_VERB_SET_POWER_STATE, parm);
3638 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003639 /* MW9 (21h) */
3640 if (imux_is_smixer || !is_aa_path_mute(codec))
3641 snd_hda_codec_write(codec, 0x21, 0,
3642 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3643 else
3644 snd_hda_codec_write(codec, 0x21, 0,
3645 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3646}
Lydia Wang25eaba22009-10-10 19:08:43 +08003647
3648/* patch for vt2002P */
3649static int patch_vt2002P(struct hda_codec *codec)
3650{
3651 struct via_spec *spec;
3652 int err;
3653
3654 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003655 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003656 if (spec == NULL)
3657 return -ENOMEM;
3658
Takashi Iwai620e2b22011-06-17 17:19:19 +02003659 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003660 override_mic_boost(codec, 0x2b, 0, 3, 40);
3661 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003662 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003663
Lydia Wang25eaba22009-10-10 19:08:43 +08003664 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003665 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003666 if (err < 0) {
3667 via_free(codec);
3668 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003669 }
3670
Lydia Wang118909562011-03-23 17:57:34 +08003671 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003672 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003673 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003674 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003675
Lydia Wang25eaba22009-10-10 19:08:43 +08003676 codec->patch_ops = via_patch_ops;
3677
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003678 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003679 return 0;
3680}
Lydia Wangab6734e2009-10-10 19:08:46 +08003681
3682/* for vt1812 */
3683
Takashi Iwai096a8852011-06-20 12:09:02 +02003684static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003685 /* Enable Boost Volume backdoor */
3686 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003687 /* Enable AOW0 to MW9 */
3688 {0x1, 0xfb8, 0xa8},
3689 { }
3690};
3691
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003692static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3693{
3694 struct via_spec *spec = codec->spec;
3695 int imux_is_smixer =
3696 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3697 unsigned int parm;
3698 unsigned int present;
3699 /* MUX10 (1eh) = stereo mixer */
3700 imux_is_smixer =
3701 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3702 /* inputs */
3703 /* PW 5/6/7 (29h/2ah/2bh) */
3704 parm = AC_PWRST_D3;
3705 set_pin_power_state(codec, 0x29, &parm);
3706 set_pin_power_state(codec, 0x2a, &parm);
3707 set_pin_power_state(codec, 0x2b, &parm);
3708 parm = AC_PWRST_D0;
3709 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3710 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3711 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3712 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3713 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3714
3715 /* outputs */
3716 /* AOW0 (8h)*/
3717 snd_hda_codec_write(codec, 0x8, 0,
3718 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3719
3720 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3721 parm = AC_PWRST_D3;
3722 set_pin_power_state(codec, 0x28, &parm);
3723 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3724 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3725
3726 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3727 parm = AC_PWRST_D3;
3728 set_pin_power_state(codec, 0x25, &parm);
3729 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3730 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3731 if (spec->hp_independent_mode)
3732 snd_hda_codec_write(codec, 0x9, 0,
3733 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3734
3735 /* Internal Speaker */
3736 /* PW0 (24h), MW0(14h), MUX0(34h) */
3737 present = snd_hda_jack_detect(codec, 0x25);
3738
3739 parm = AC_PWRST_D3;
3740 set_pin_power_state(codec, 0x24, &parm);
3741 if (present) {
3742 snd_hda_codec_write(codec, 0x14, 0,
3743 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3744 snd_hda_codec_write(codec, 0x34, 0,
3745 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3746 } else {
3747 snd_hda_codec_write(codec, 0x14, 0,
3748 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3749 snd_hda_codec_write(codec, 0x34, 0,
3750 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3751 }
3752
3753
3754 /* Mono Out */
3755 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3756 present = snd_hda_jack_detect(codec, 0x28);
3757
3758 parm = AC_PWRST_D3;
3759 set_pin_power_state(codec, 0x31, &parm);
3760 if (present) {
3761 snd_hda_codec_write(codec, 0x1c, 0,
3762 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3763 snd_hda_codec_write(codec, 0x3c, 0,
3764 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3765 snd_hda_codec_write(codec, 0x3e, 0,
3766 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3767 } else {
3768 snd_hda_codec_write(codec, 0x1c, 0,
3769 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3770 snd_hda_codec_write(codec, 0x3c, 0,
3771 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3772 snd_hda_codec_write(codec, 0x3e, 0,
3773 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3774 }
3775
3776 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3777 parm = AC_PWRST_D3;
3778 set_pin_power_state(codec, 0x33, &parm);
3779 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3780 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3781
3782}
Lydia Wangab6734e2009-10-10 19:08:46 +08003783
3784/* patch for vt1812 */
3785static int patch_vt1812(struct hda_codec *codec)
3786{
3787 struct via_spec *spec;
3788 int err;
3789
3790 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003791 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003792 if (spec == NULL)
3793 return -ENOMEM;
3794
Takashi Iwai620e2b22011-06-17 17:19:19 +02003795 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003796 override_mic_boost(codec, 0x2b, 0, 3, 40);
3797 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003798 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003799
Lydia Wangab6734e2009-10-10 19:08:46 +08003800 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003801 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003802 if (err < 0) {
3803 via_free(codec);
3804 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003805 }
3806
Takashi Iwai096a8852011-06-20 12:09:02 +02003807 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003808
Lydia Wangab6734e2009-10-10 19:08:46 +08003809 codec->patch_ops = via_patch_ops;
3810
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003811 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003812 return 0;
3813}
3814
Joseph Chanc577b8a2006-11-29 15:29:40 +01003815/*
3816 * patch entries
3817 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003818static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003819 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3820 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3821 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3822 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3823 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003824 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003825 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003826 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003827 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003828 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003829 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003830 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003831 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003832 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003833 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003834 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003835 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003836 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003837 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003838 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003839 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003840 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003841 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003842 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003843 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003844 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003845 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003846 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003847 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003848 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003849 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003850 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003851 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003852 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003853 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003854 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003855 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003856 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003857 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003858 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003859 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003860 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003861 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003862 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003863 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003864 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003865 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003866 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003867 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003868 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003869 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003870 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003871 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003872 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003873 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003874 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003875 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003876 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003877 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003878 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003879 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003880 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003881 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003882 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003883 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003884 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003885 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003886 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003887 { .id = 0x11060428, .name = "VT1718S",
3888 .patch = patch_vt1718S},
3889 { .id = 0x11064428, .name = "VT1718S",
3890 .patch = patch_vt1718S},
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08003891 { .id = 0x11060441, .name = "VT2020",
3892 .patch = patch_vt1718S},
3893 { .id = 0x11064441, .name = "VT1828S",
3894 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003895 { .id = 0x11060433, .name = "VT1716S",
3896 .patch = patch_vt1716S},
3897 { .id = 0x1106a721, .name = "VT1716S",
3898 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003899 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3900 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003901 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003902 { .id = 0x11060440, .name = "VT1818S",
3903 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003904 { .id = 0x11060446, .name = "VT1802",
3905 .patch = patch_vt2002P},
3906 { .id = 0x11068446, .name = "VT1802",
3907 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003908 {} /* terminator */
3909};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003910
3911MODULE_ALIAS("snd-hda-codec-id:1106*");
3912
3913static struct hda_codec_preset_list via_list = {
3914 .preset = snd_hda_preset_via,
3915 .owner = THIS_MODULE,
3916};
3917
3918MODULE_LICENSE("GPL");
3919MODULE_DESCRIPTION("VIA HD-audio codec");
3920
3921static int __init patch_via_init(void)
3922{
3923 return snd_hda_add_codec_preset(&via_list);
3924}
3925
3926static void __exit patch_via_exit(void)
3927{
3928 snd_hda_delete_codec_preset(&via_list);
3929}
3930
3931module_init(patch_via_init)
3932module_exit(patch_via_exit)