blob: 5b0342635ebe4bde98b4999e47a8a313cad63002 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010052#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080053#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010054#include "hda_codec.h"
55#include "hda_local.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010056
Joseph Chanc577b8a2006-11-29 15:29:40 +010057/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080058#define VT1708_HP_PIN_NID 0x20
59#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010060
Harald Welted7426322008-09-15 22:43:23 +080061enum VIA_HDA_CODEC {
62 UNKNOWN = -1,
63 VT1708,
64 VT1709_10CH,
65 VT1709_6CH,
66 VT1708B_8CH,
67 VT1708B_4CH,
68 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080069 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080070 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080071 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080072 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080073 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080074 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080075 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080076 CODEC_TYPES,
77};
78
Lydia Wang118909562011-03-23 17:57:34 +080079#define VT2002P_COMPATIBLE(spec) \
80 ((spec)->codec_type == VT2002P ||\
81 (spec)->codec_type == VT1812 ||\
82 (spec)->codec_type == VT1802)
83
Takashi Iwai8e3679d2011-06-21 09:01:36 +020084#define MAX_NID_PATH_DEPTH 5
85
Takashi Iwai09a9ad692011-06-21 15:57:44 +020086/* output-path: DAC -> ... -> pin
87 * idx[] contains the source index number of the next widget;
88 * e.g. idx[0] is the index of the DAC selected by path[1] widget
89 * multi[] indicates whether it's a selector widget with multi-connectors
90 * (i.e. the connection selection is mandatory)
91 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
92 */
Takashi Iwai4a796162011-06-17 17:53:38 +020093struct nid_path {
94 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020095 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad692011-06-21 15:57:44 +020096 unsigned char idx[MAX_NID_PATH_DEPTH];
97 unsigned char multi[MAX_NID_PATH_DEPTH];
98 unsigned int vol_ctl;
99 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200100};
101
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200102/* input-path */
103struct via_input {
104 hda_nid_t pin; /* input-pin or aa-mix */
105 int adc_idx; /* ADC index to be used */
106 int mux_idx; /* MUX index (if any) */
107 const char *label; /* input-source label */
108};
109
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200110#define VIA_MAX_ADCS 3
111
Takashi Iwai3b607e32011-07-18 16:54:40 +0200112enum {
113 STREAM_MULTI_OUT = (1 << 0),
114 STREAM_INDEP_HP = (1 << 1),
115};
116
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800117struct via_spec {
118 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200119 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800120 unsigned int num_mixers;
121
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200122 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800123 unsigned int num_iverbs;
124
Takashi Iwai82673bc2011-06-17 16:24:21 +0200125 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200126 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200127 const struct hda_pcm_stream *stream_analog_playback;
128 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800129
Takashi Iwai82673bc2011-06-17 16:24:21 +0200130 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200131 const struct hda_pcm_stream *stream_digital_playback;
132 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800133
134 /* playback */
135 struct hda_multi_out multiout;
136 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200137 hda_nid_t hp_dac_nid;
Takashi Iwai3214b962011-07-18 12:49:25 +0200138 hda_nid_t speaker_dac_nid;
139 int hp_indep_shared; /* indep HP-DAC is shared with side ch */
Takashi Iwai3b607e32011-07-18 16:54:40 +0200140 int opened_streams; /* STREAM_* bits */
141 int active_streams; /* STREAM_* bits */
Takashi Iwai3214b962011-07-18 12:49:25 +0200142 int aamix_mode; /* loopback is enabled for output-path? */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800143
Takashi Iwai3214b962011-07-18 12:49:25 +0200144 /* Output-paths:
145 * There are different output-paths depending on the setup.
146 * out_path, hp_path and speaker_path are primary paths. If both
147 * direct DAC and aa-loopback routes are available, these contain
148 * the former paths. Meanwhile *_mix_path contain the paths with
149 * loopback mixer. (Since the loopback is only for front channel,
150 * no out_mix_path for surround channels.)
151 * The HP output has another path, hp_indep_path, which is used in
152 * the independent-HP mode.
153 */
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200154 struct nid_path out_path[HDA_SIDE + 1];
Takashi Iwai3214b962011-07-18 12:49:25 +0200155 struct nid_path out_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200156 struct nid_path hp_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200157 struct nid_path hp_mix_path;
158 struct nid_path hp_indep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200159 struct nid_path speaker_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200160 struct nid_path speaker_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200161
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800162 /* capture */
163 unsigned int num_adc_nids;
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200164 hda_nid_t adc_nids[VIA_MAX_ADCS];
165 hda_nid_t mux_nids[VIA_MAX_ADCS];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200166 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800167 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800168
169 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200170 bool dyn_adc_switch;
171 int num_inputs;
172 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200173 unsigned int cur_mux[VIA_MAX_ADCS];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800174
Takashi Iwai3b607e32011-07-18 16:54:40 +0200175 /* dynamic DAC switching */
176 unsigned int cur_dac_stream_tag;
177 unsigned int cur_dac_format;
178 unsigned int cur_hp_stream_tag;
179 unsigned int cur_hp_format;
180
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200181 /* dynamic ADC switching */
182 hda_nid_t cur_adc;
183 unsigned int cur_adc_stream_tag;
184 unsigned int cur_adc_format;
185
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800186 /* PCM information */
187 struct hda_pcm pcm_rec[3];
188
189 /* dynamic controls, init_verbs and input_mux */
190 struct auto_pin_cfg autocfg;
191 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800192 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
193
194 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800195 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800196 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200197 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800198 enum VIA_HDA_CODEC codec_type;
199
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200200 /* smart51 setup */
201 unsigned int smart51_nums;
202 hda_nid_t smart51_pins[2];
203 int smart51_idxs[2];
204 const char *smart51_labels[2];
205 unsigned int smart51_enabled;
206
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800207 /* work to check hp jack state */
208 struct hda_codec *codec;
209 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200210 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800211 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800212
213 void (*set_widgets_power_state)(struct hda_codec *codec);
214
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800215 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200216 int num_loopbacks;
217 struct hda_amp_list loopback_list[8];
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200218
219 /* bind capture-volume */
220 struct hda_bind_ctls *bind_cap_vol;
221 struct hda_bind_ctls *bind_cap_sw;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200222
223 struct mutex config_mutex;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800224};
225
Lydia Wang0341ccd2011-03-22 16:25:03 +0800226static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100227static struct via_spec * via_new_spec(struct hda_codec *codec)
228{
229 struct via_spec *spec;
230
231 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
232 if (spec == NULL)
233 return NULL;
234
Takashi Iwai3b607e32011-07-18 16:54:40 +0200235 mutex_init(&spec->config_mutex);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100236 codec->spec = spec;
237 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800238 spec->codec_type = get_codec_type(codec);
239 /* VT1708BCE & VT1708S are almost same */
240 if (spec->codec_type == VT1708BCE)
241 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100242 return spec;
243}
244
Lydia Wang744ff5f2009-10-10 19:07:26 +0800245static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800246{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800247 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800248 u16 ven_id = vendor_id >> 16;
249 u16 dev_id = vendor_id & 0xffff;
250 enum VIA_HDA_CODEC codec_type;
251
252 /* get codec type */
253 if (ven_id != 0x1106)
254 codec_type = UNKNOWN;
255 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
256 codec_type = VT1708;
257 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
258 codec_type = VT1709_10CH;
259 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
260 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800261 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800262 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800263 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
264 codec_type = VT1708BCE;
265 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800266 codec_type = VT1708B_4CH;
267 else if ((dev_id & 0xfff) == 0x397
268 && (dev_id >> 12) < 8)
269 codec_type = VT1708S;
270 else if ((dev_id & 0xfff) == 0x398
271 && (dev_id >> 12) < 8)
272 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800273 else if ((dev_id & 0xfff) == 0x428
274 && (dev_id >> 12) < 8)
275 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800276 else if (dev_id == 0x0433 || dev_id == 0xa721)
277 codec_type = VT1716S;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +0800278 else if (dev_id == 0x0441 || dev_id == 0x4441)
279 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800280 else if (dev_id == 0x0438 || dev_id == 0x4438)
281 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800282 else if (dev_id == 0x0448)
283 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800284 else if (dev_id == 0x0440)
285 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800286 else if ((dev_id & 0xfff) == 0x446)
287 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800288 else
289 codec_type = UNKNOWN;
290 return codec_type;
291};
292
Lydia Wangec7e7e42011-03-24 12:43:44 +0800293#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800294#define VIA_HP_EVENT 0x01
295#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200296#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800297
Joseph Chanc577b8a2006-11-29 15:29:40 +0100298enum {
299 VIA_CTL_WIDGET_VOL,
300 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800301 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100302};
303
Takashi Iwaiada509e2011-06-20 15:40:19 +0200304static void analog_low_current_mode(struct hda_codec *codec);
305static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800306
307static void vt1708_start_hp_work(struct via_spec *spec)
308{
309 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
310 return;
311 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200312 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800313 if (!delayed_work_pending(&spec->vt1708_hp_work))
314 schedule_delayed_work(&spec->vt1708_hp_work,
315 msecs_to_jiffies(100));
316}
317
318static void vt1708_stop_hp_work(struct via_spec *spec)
319{
320 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
321 return;
322 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
323 && !is_aa_path_mute(spec->codec))
324 return;
325 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200326 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100327 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800328}
Lydia Wangf5271102009-10-10 19:07:35 +0800329
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800330static void set_widgets_power_state(struct hda_codec *codec)
331{
332 struct via_spec *spec = codec->spec;
333 if (spec->set_widgets_power_state)
334 spec->set_widgets_power_state(codec);
335}
Lydia Wang25eaba22009-10-10 19:08:43 +0800336
Lydia Wangf5271102009-10-10 19:07:35 +0800337static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
338 struct snd_ctl_elem_value *ucontrol)
339{
340 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
341 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
342
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800343 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200344 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800345 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
346 if (is_aa_path_mute(codec))
347 vt1708_start_hp_work(codec->spec);
348 else
349 vt1708_stop_hp_work(codec->spec);
350 }
Lydia Wangf5271102009-10-10 19:07:35 +0800351 return change;
352}
353
354/* modify .put = snd_hda_mixer_amp_switch_put */
355#define ANALOG_INPUT_MUTE \
356 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
357 .name = NULL, \
358 .index = 0, \
359 .info = snd_hda_mixer_amp_switch_info, \
360 .get = snd_hda_mixer_amp_switch_get, \
361 .put = analog_input_switch_put, \
362 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
363
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200364static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100365 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
366 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800367 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100368};
369
Lydia Wangab6734e2009-10-10 19:08:46 +0800370
Joseph Chanc577b8a2006-11-29 15:29:40 +0100371/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200372static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
373 const struct snd_kcontrol_new *tmpl,
374 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100375{
376 struct snd_kcontrol_new *knew;
377
Takashi Iwai603c4012008-07-30 15:01:44 +0200378 snd_array_init(&spec->kctls, sizeof(*knew), 32);
379 knew = snd_array_new(&spec->kctls);
380 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200381 return NULL;
382 *knew = *tmpl;
383 if (!name)
384 name = tmpl->name;
385 if (name) {
386 knew->name = kstrdup(name, GFP_KERNEL);
387 if (!knew->name)
388 return NULL;
389 }
390 return knew;
391}
392
393static int __via_add_control(struct via_spec *spec, int type, const char *name,
394 int idx, unsigned long val)
395{
396 struct snd_kcontrol_new *knew;
397
398 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
399 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100400 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200401 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100402 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100403 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100404 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100405 return 0;
406}
407
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200408#define via_add_control(spec, type, name, val) \
409 __via_add_control(spec, type, name, 0, val)
410
Takashi Iwai291c9e32011-06-17 16:15:26 +0200411#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100412
Takashi Iwai603c4012008-07-30 15:01:44 +0200413static void via_free_kctls(struct hda_codec *codec)
414{
415 struct via_spec *spec = codec->spec;
416
417 if (spec->kctls.list) {
418 struct snd_kcontrol_new *kctl = spec->kctls.list;
419 int i;
420 for (i = 0; i < spec->kctls.used; i++)
421 kfree(kctl[i].name);
422 }
423 snd_array_free(&spec->kctls);
424}
425
Joseph Chanc577b8a2006-11-29 15:29:40 +0100426/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800427static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200428 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100429{
430 char name[32];
431 int err;
432
433 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200434 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100435 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
436 if (err < 0)
437 return err;
438 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200439 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100440 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
441 if (err < 0)
442 return err;
443 return 0;
444}
445
Takashi Iwai5d417622011-06-20 11:32:27 +0200446#define get_connection_index(codec, mux, nid) \
Takashi Iwai8d087c72011-06-28 12:45:47 +0200447 snd_hda_get_conn_index(codec, mux, nid, 0)
Takashi Iwai5d417622011-06-20 11:32:27 +0200448
Takashi Iwai8df2a312011-06-21 11:48:29 +0200449static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
450 unsigned int mask)
451{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200452 unsigned int caps;
453 if (!nid)
454 return false;
455 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200456 if (dir == HDA_INPUT)
457 caps &= AC_WCAP_IN_AMP;
458 else
459 caps &= AC_WCAP_OUT_AMP;
460 if (!caps)
461 return false;
462 if (query_amp_caps(codec, nid, dir) & mask)
463 return true;
464 return false;
465}
466
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200467#define have_mute(codec, nid, dir) \
468 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200469
Lydia Wangd69607b32011-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 Wangd69607b32011-07-08 14:02:52 +0800473{
474 int i, num, val;
Lydia Wangd69607b32011-07-08 14:02:52 +0800475
476 if (!path)
477 return;
478 num = snd_hda_get_conn_list(codec, mix_nid, NULL);
Lydia Wangd69607b32011-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 Wangd69607b32011-07-08 14:02:52 +0800484 snd_hda_codec_write(codec, mix_nid, 0,
485 AC_VERB_SET_AMP_GAIN_MUTE, val);
486 }
487}
488
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200489/* enable/disable the output-route */
490static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
491 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200492{
Lydia Wangd69607b32011-07-08 14:02:52 +0800493 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200494 int i;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200495 for (i = 0; i < path->depth; i++) {
496 hda_nid_t src, dst;
497 int idx = path->idx[i];
498 src = path->path[i];
499 if (i < path->depth - 1)
500 dst = path->path[i + 1];
501 else
502 dst = 0;
503 if (enable && path->multi[i])
504 snd_hda_codec_write(codec, dst, 0,
505 AC_VERB_SET_CONNECT_SEL, idx);
Takashi Iwai3214b962011-07-18 12:49:25 +0200506 if (!force && (dst == spec->aa_mix_nid))
Lydia Wange5e14682011-07-01 10:55:07 +0800507 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +0200508 if (have_mute(codec, dst, HDA_INPUT))
509 activate_output_mix(codec, path, dst, idx, enable);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200510 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
511 continue;
512 if (have_mute(codec, src, HDA_OUTPUT)) {
513 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
514 snd_hda_codec_write(codec, src, 0,
515 AC_VERB_SET_AMP_GAIN_MUTE, val);
516 }
517 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200518}
519
520/* set the given pin as output */
521static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
522 int pin_type)
523{
524 if (!pin)
525 return;
526 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
527 pin_type);
528 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
529 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200530 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100531}
532
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200533static void via_auto_init_output(struct hda_codec *codec,
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 Wangd69607b32011-07-08 14:02:52 +0800538 hda_nid_t pin;
Takashi Iwai5d417622011-06-20 11:32:27 +0200539
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200540 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200541 return;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200542 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200543
544 init_output_pin(codec, pin, pin_type);
545 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
546 if (caps & AC_AMPCAP_MUTE) {
547 unsigned int val;
548 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
549 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
550 AMP_OUT_MUTE | val);
551 }
Lydia Wangd69607b32011-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
569static void via_auto_init_hp_out(struct hda_codec *codec)
570{
571 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200572 int shared = spec->hp_indep_shared;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100573
Takashi Iwai3214b962011-07-18 12:49:25 +0200574 if (!spec->hp_path.depth) {
575 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP, true);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200576 return;
577 }
578 if (spec->hp_independent_mode) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200579 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200580 activate_output_path(codec, &spec->hp_mix_path, false, false);
581 if (shared)
582 activate_output_path(codec, &spec->out_path[shared],
583 false, false);
584 via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP, true);
585 } else if (spec->aamix_mode) {
586 activate_output_path(codec, &spec->hp_path, false, false);
587 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP, true);
588 } else {
589 activate_output_path(codec, &spec->hp_mix_path, false, false);
590 via_auto_init_output(codec, &spec->hp_path, PIN_HP, true);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200591 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100592}
593
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200594static void via_auto_init_speaker_out(struct hda_codec *codec)
595{
596 struct via_spec *spec = codec->spec;
597
Takashi Iwai3214b962011-07-18 12:49:25 +0200598 if (!spec->autocfg.speaker_outs)
599 return;
600 if (!spec->speaker_path.depth) {
601 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT,
602 true);
603 return;
604 }
605 if (!spec->aamix_mode) {
606 activate_output_path(codec, &spec->speaker_mix_path,
607 false, false);
Takashi Iwaibac4b922011-07-04 17:35:51 +0200608 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT,
Takashi Iwai3214b962011-07-18 12:49:25 +0200609 true);
610 } else {
611 activate_output_path(codec, &spec->speaker_path, false, false);
612 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT,
613 true);
614 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200615}
616
Takashi Iwaif4a78282011-06-17 18:46:48 +0200617static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200618static void via_hp_automute(struct hda_codec *codec);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200619
Joseph Chanc577b8a2006-11-29 15:29:40 +0100620static void via_auto_init_analog_input(struct hda_codec *codec)
621{
622 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200623 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200624 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200625 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200626 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100627
Takashi Iwai096a8852011-06-20 12:09:02 +0200628 /* init ADCs */
629 for (i = 0; i < spec->num_adc_nids; i++) {
630 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
631 AC_VERB_SET_AMP_GAIN_MUTE,
632 AMP_IN_UNMUTE(0));
633 }
634
635 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200636 for (i = 0; i < cfg->num_inputs; i++) {
637 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200638 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200639 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100640 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200641 ctl = PIN_VREF50;
642 else
643 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100644 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200645 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100646 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200647
648 /* init input-src */
649 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200650 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
651 if (spec->mux_nids[adc_idx]) {
652 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
653 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
654 AC_VERB_SET_CONNECT_SEL,
655 mux_idx);
656 }
657 if (spec->dyn_adc_switch)
658 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200659 }
660
661 /* init aa-mixer */
662 if (!spec->aa_mix_nid)
663 return;
664 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
665 ARRAY_SIZE(conn));
666 for (i = 0; i < num_conns; i++) {
667 unsigned int caps = get_wcaps(codec, conn[i]);
668 if (get_wcaps_type(caps) == AC_WID_PIN)
669 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
670 AC_VERB_SET_AMP_GAIN_MUTE,
671 AMP_IN_MUTE(i));
672 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100673}
Lydia Wangf5271102009-10-10 19:07:35 +0800674
675static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
676 unsigned int *affected_parm)
677{
678 unsigned parm;
679 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
680 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
681 >> AC_DEFCFG_MISC_SHIFT
682 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800683 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200684 unsigned present = 0;
685
686 no_presence |= spec->no_pin_power_ctl;
687 if (!no_presence)
688 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200689 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800690 || ((no_presence || present)
691 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800692 *affected_parm = AC_PWRST_D0; /* if it's connected */
693 parm = AC_PWRST_D0;
694 } else
695 parm = AC_PWRST_D3;
696
697 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
698}
699
Takashi Iwai24088a52011-06-17 16:59:21 +0200700static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
701 struct snd_ctl_elem_info *uinfo)
702{
703 static const char * const texts[] = {
704 "Disabled", "Enabled"
705 };
706
707 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
708 uinfo->count = 1;
709 uinfo->value.enumerated.items = 2;
710 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
711 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
712 strcpy(uinfo->value.enumerated.name,
713 texts[uinfo->value.enumerated.item]);
714 return 0;
715}
716
717static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
718 struct snd_ctl_elem_value *ucontrol)
719{
720 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
721 struct via_spec *spec = codec->spec;
722 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
723 return 0;
724}
725
726static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
727 struct snd_ctl_elem_value *ucontrol)
728{
729 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
730 struct via_spec *spec = codec->spec;
731 unsigned int val = !ucontrol->value.enumerated.item[0];
732
733 if (val == spec->no_pin_power_ctl)
734 return 0;
735 spec->no_pin_power_ctl = val;
736 set_widgets_power_state(codec);
737 return 1;
738}
739
740static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
741 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
742 .name = "Dynamic Power-Control",
743 .info = via_pin_power_ctl_info,
744 .get = via_pin_power_ctl_get,
745 .put = via_pin_power_ctl_put,
746};
747
748
Harald Welte0aa62ae2008-09-09 15:58:27 +0800749static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
750 struct snd_ctl_elem_info *uinfo)
751{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200752 static const char * const texts[] = { "OFF", "ON" };
753
754 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
755 uinfo->count = 1;
756 uinfo->value.enumerated.items = 2;
757 if (uinfo->value.enumerated.item >= 2)
758 uinfo->value.enumerated.item = 1;
759 strcpy(uinfo->value.enumerated.name,
760 texts[uinfo->value.enumerated.item]);
761 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800762}
763
764static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
765 struct snd_ctl_elem_value *ucontrol)
766{
767 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800768 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800769
Takashi Iwaiece8d042011-06-19 16:24:21 +0200770 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800771 return 0;
772}
773
Takashi Iwai3b607e32011-07-18 16:54:40 +0200774/* adjust spec->multiout setup according to the current flags */
775static void setup_playback_multi_pcm(struct via_spec *spec)
776{
777 const struct auto_pin_cfg *cfg = &spec->autocfg;
778 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
779 spec->multiout.hp_nid = 0;
780 if (!spec->hp_independent_mode) {
781 if (!spec->hp_indep_shared)
782 spec->multiout.hp_nid = spec->hp_dac_nid;
783 } else {
784 if (spec->hp_indep_shared)
785 spec->multiout.num_dacs = cfg->line_outs - 1;
786 }
787}
788
789/* update DAC setups according to indep-HP switch;
790 * this function is called only when indep-HP is modified
791 */
792static void switch_indep_hp_dacs(struct hda_codec *codec)
793{
794 struct via_spec *spec = codec->spec;
795 int shared = spec->hp_indep_shared;
796 hda_nid_t shared_dac, hp_dac;
797
798 if (!spec->opened_streams)
799 return;
800
801 shared_dac = shared ? spec->multiout.dac_nids[shared] : 0;
802 hp_dac = spec->hp_dac_nid;
803 if (spec->hp_independent_mode) {
804 /* switch to indep-HP mode */
805 if (spec->active_streams & STREAM_MULTI_OUT) {
806 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
807 __snd_hda_codec_cleanup_stream(codec, shared_dac, 1);
808 }
809 if (spec->active_streams & STREAM_INDEP_HP)
810 snd_hda_codec_setup_stream(codec, hp_dac,
811 spec->cur_hp_stream_tag, 0,
812 spec->cur_hp_format);
813 } else {
814 /* back to HP or shared-DAC */
815 if (spec->active_streams & STREAM_INDEP_HP)
816 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
817 if (spec->active_streams & STREAM_MULTI_OUT) {
818 hda_nid_t dac;
819 int ch;
820 if (shared_dac) { /* reset mutli-ch DAC */
821 dac = shared_dac;
822 ch = shared * 2;
823 } else { /* reset HP DAC */
824 dac = hp_dac;
825 ch = 0;
826 }
827 snd_hda_codec_setup_stream(codec, dac,
828 spec->cur_dac_stream_tag, ch,
829 spec->cur_dac_format);
830 }
831 }
832 setup_playback_multi_pcm(spec);
833}
834
Harald Welte0aa62ae2008-09-09 15:58:27 +0800835static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
836 struct snd_ctl_elem_value *ucontrol)
837{
838 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
839 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200840 int cur, shared;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200841
Takashi Iwai3b607e32011-07-18 16:54:40 +0200842 mutex_lock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200843 cur = !!ucontrol->value.enumerated.item[0];
Takashi Iwai3b607e32011-07-18 16:54:40 +0200844 if (spec->hp_independent_mode == cur) {
845 mutex_unlock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200846 return 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200847 }
Takashi Iwai25250502011-06-30 17:24:47 +0200848 spec->hp_independent_mode = cur;
Takashi Iwai3214b962011-07-18 12:49:25 +0200849 shared = spec->hp_indep_shared;
Takashi Iwai25250502011-06-30 17:24:47 +0200850 if (cur) {
Takashi Iwai3214b962011-07-18 12:49:25 +0200851 activate_output_path(codec, &spec->hp_mix_path, false, false);
852 if (shared)
853 activate_output_path(codec, &spec->out_path[shared],
Takashi Iwai25250502011-06-30 17:24:47 +0200854 false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200855 activate_output_path(codec, &spec->hp_path, true, false);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200856 } else {
857 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200858 if (shared)
859 activate_output_path(codec, &spec->out_path[shared],
Takashi Iwai25250502011-06-30 17:24:47 +0200860 true, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200861 activate_output_path(codec, &spec->hp_mix_path, true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200862 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800863
Takashi Iwai3b607e32011-07-18 16:54:40 +0200864 switch_indep_hp_dacs(codec);
865 mutex_unlock(&spec->config_mutex);
866
Lydia Wangce0e5a92011-03-22 16:22:37 +0800867 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800868 set_widgets_power_state(codec);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200869 via_hp_automute(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200870 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800871}
872
Takashi Iwaiece8d042011-06-19 16:24:21 +0200873static const struct snd_kcontrol_new via_hp_mixer = {
874 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
875 .name = "Independent HP",
876 .info = via_independent_hp_info,
877 .get = via_independent_hp_get,
878 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800879};
880
Takashi Iwai3d83e572010-04-14 14:36:23 +0200881static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100882{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200883 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100884 struct snd_kcontrol_new *knew;
885 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100886
Takashi Iwaiece8d042011-06-19 16:24:21 +0200887 nid = spec->autocfg.hp_pins[0];
888 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200889 if (knew == NULL)
890 return -ENOMEM;
891
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100892 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100893
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100894 return 0;
895}
896
Lydia Wang1564b282009-10-10 19:07:52 +0800897static void notify_aa_path_ctls(struct hda_codec *codec)
898{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200899 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800900 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800901
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200902 for (i = 0; i < spec->smart51_nums; i++) {
903 struct snd_kcontrol *ctl;
904 struct snd_ctl_elem_id id;
905 memset(&id, 0, sizeof(id));
906 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
907 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800908 ctl = snd_hda_find_mixer_ctl(codec, id.name);
909 if (ctl)
910 snd_ctl_notify(codec->bus->card,
911 SNDRV_CTL_EVENT_MASK_VALUE,
912 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800913 }
914}
915
916static void mute_aa_path(struct hda_codec *codec, int mute)
917{
918 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200919 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800920 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200921
Lydia Wang1564b282009-10-10 19:07:52 +0800922 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200923 for (i = 0; i < spec->smart51_nums; i++) {
924 if (spec->smart51_idxs[i] < 0)
925 continue;
926 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
927 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800928 HDA_AMP_MUTE, val);
929 }
930}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200931
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200932static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
933{
934 struct via_spec *spec = codec->spec;
935 int i;
936
937 for (i = 0; i < spec->smart51_nums; i++)
938 if (spec->smart51_pins[i] == pin)
939 return true;
940 return false;
941}
942
Lydia Wang1564b282009-10-10 19:07:52 +0800943static int via_smart51_get(struct snd_kcontrol *kcontrol,
944 struct snd_ctl_elem_value *ucontrol)
945{
946 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
947 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800948
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200949 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800950 return 0;
951}
952
953static int via_smart51_put(struct snd_kcontrol *kcontrol,
954 struct snd_ctl_elem_value *ucontrol)
955{
956 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
957 struct via_spec *spec = codec->spec;
958 int out_in = *ucontrol->value.integer.value
959 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800960 int i;
961
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200962 for (i = 0; i < spec->smart51_nums; i++) {
963 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200964 unsigned int parm;
965
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200966 parm = snd_hda_codec_read(codec, nid, 0,
967 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
968 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
969 parm |= out_in;
970 snd_hda_codec_write(codec, nid, 0,
971 AC_VERB_SET_PIN_WIDGET_CONTROL,
972 parm);
973 if (out_in == AC_PINCTL_OUT_EN) {
974 mute_aa_path(codec, 1);
975 notify_aa_path_ctls(codec);
976 }
Lydia Wang1564b282009-10-10 19:07:52 +0800977 }
978 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800979 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800980 return 1;
981}
982
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200983static const struct snd_kcontrol_new via_smart51_mixer = {
984 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
985 .name = "Smart 5.1",
986 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200987 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200988 .get = via_smart51_get,
989 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800990};
991
Takashi Iwaif4a78282011-06-17 18:46:48 +0200992static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100993{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200994 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100995
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200996 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800997 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200998 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100999 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001000 return 0;
1001}
1002
Takashi Iwaiada509e2011-06-20 15:40:19 +02001003/* check AA path's mute status */
1004static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001005{
Lydia Wangf5271102009-10-10 19:07:35 +08001006 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001007 const struct hda_amp_list *p;
1008 int i, ch, v;
1009
1010 for (i = 0; i < spec->num_loopbacks; i++) {
1011 p = &spec->loopback_list[i];
1012 for (ch = 0; ch < 2; ch++) {
1013 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
1014 p->idx);
1015 if (!(v & HDA_AMP_MUTE) && v > 0)
1016 return false;
Lydia Wangf5271102009-10-10 19:07:35 +08001017 }
1018 }
Takashi Iwaiada509e2011-06-20 15:40:19 +02001019 return true;
Lydia Wangf5271102009-10-10 19:07:35 +08001020}
1021
1022/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +02001023static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001024{
1025 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001026 bool enable;
1027 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +08001028
Takashi Iwai3b607e32011-07-18 16:54:40 +02001029 enable = is_aa_path_mute(codec) && (spec->opened_streams != 0);
Lydia Wangf5271102009-10-10 19:07:35 +08001030
1031 /* decide low current mode's verb & parameter */
1032 switch (spec->codec_type) {
1033 case VT1708B_8CH:
1034 case VT1708B_4CH:
1035 verb = 0xf70;
1036 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1037 break;
1038 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001039 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001040 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001041 verb = 0xf73;
1042 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1043 break;
1044 case VT1702:
1045 verb = 0xf73;
1046 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1047 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001048 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001049 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +08001050 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +08001051 verb = 0xf93;
1052 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1053 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001054 default:
1055 return; /* other codecs are not supported */
1056 }
1057 /* send verb */
1058 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1059}
1060
Joseph Chanc577b8a2006-11-29 15:29:40 +01001061/*
1062 * generic initialization of ADC, input mixers and output mixers
1063 */
Takashi Iwai096a8852011-06-20 12:09:02 +02001064static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001065 /* power down jack detect function */
1066 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001067 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001068};
1069
Takashi Iwai3b607e32011-07-18 16:54:40 +02001070static void set_stream_open(struct hda_codec *codec, int bit, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001071{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001072 struct via_spec *spec = codec->spec;
1073
1074 if (active)
Takashi Iwai3b607e32011-07-18 16:54:40 +02001075 spec->opened_streams |= bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001076 else
Takashi Iwai3b607e32011-07-18 16:54:40 +02001077 spec->opened_streams &= ~bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001078 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001079}
1080
Takashi Iwaiece8d042011-06-19 16:24:21 +02001081static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001082 struct hda_codec *codec,
1083 struct snd_pcm_substream *substream)
1084{
1085 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001086 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001087 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001088
Takashi Iwai25250502011-06-30 17:24:47 +02001089 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
Takashi Iwai25250502011-06-30 17:24:47 +02001090 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001091 set_stream_open(codec, STREAM_MULTI_OUT, true);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001092 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1093 hinfo);
1094 if (err < 0) {
Takashi Iwai3b607e32011-07-18 16:54:40 +02001095 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001096 return err;
1097 }
1098 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001099}
1100
Takashi Iwaiece8d042011-06-19 16:24:21 +02001101static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001102 struct hda_codec *codec,
1103 struct snd_pcm_substream *substream)
1104{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001105 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001106 return 0;
1107}
1108
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001109static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1110 struct hda_codec *codec,
1111 struct snd_pcm_substream *substream)
1112{
1113 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001114
Takashi Iwaiece8d042011-06-19 16:24:21 +02001115 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001116 return -EINVAL;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001117 set_stream_open(codec, STREAM_INDEP_HP, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001118 return 0;
1119}
1120
1121static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1122 struct hda_codec *codec,
1123 struct snd_pcm_substream *substream)
1124{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001125 set_stream_open(codec, STREAM_INDEP_HP, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001126 return 0;
1127}
1128
1129static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1130 struct hda_codec *codec,
1131 unsigned int stream_tag,
1132 unsigned int format,
1133 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001134{
1135 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001136
Takashi Iwai3b607e32011-07-18 16:54:40 +02001137 mutex_lock(&spec->config_mutex);
1138 setup_playback_multi_pcm(spec);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001139 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1140 format, substream);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001141 /* remember for dynamic DAC switch with indep-HP */
1142 spec->active_streams |= STREAM_MULTI_OUT;
1143 spec->cur_dac_stream_tag = stream_tag;
1144 spec->cur_dac_format = format;
1145 mutex_unlock(&spec->config_mutex);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001146 vt1708_start_hp_work(spec);
1147 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001148}
1149
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001150static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1151 struct hda_codec *codec,
1152 unsigned int stream_tag,
1153 unsigned int format,
1154 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001155{
1156 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001157
Takashi Iwai3b607e32011-07-18 16:54:40 +02001158 mutex_lock(&spec->config_mutex);
1159 if (spec->hp_independent_mode)
1160 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1161 stream_tag, 0, format);
1162 spec->active_streams |= STREAM_INDEP_HP;
1163 spec->cur_hp_stream_tag = stream_tag;
1164 spec->cur_hp_format = format;
1165 mutex_unlock(&spec->config_mutex);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001166 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001167 return 0;
1168}
1169
1170static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1171 struct hda_codec *codec,
1172 struct snd_pcm_substream *substream)
1173{
1174 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001175
Takashi Iwai3b607e32011-07-18 16:54:40 +02001176 mutex_lock(&spec->config_mutex);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001177 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001178 spec->active_streams &= ~STREAM_MULTI_OUT;
1179 mutex_unlock(&spec->config_mutex);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001180 vt1708_stop_hp_work(spec);
1181 return 0;
1182}
1183
1184static int via_playback_hp_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;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001189
Takashi Iwai3b607e32011-07-18 16:54:40 +02001190 mutex_lock(&spec->config_mutex);
1191 if (spec->hp_independent_mode)
1192 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
1193 spec->active_streams &= ~STREAM_INDEP_HP;
1194 mutex_unlock(&spec->config_mutex);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001195 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001196 return 0;
1197}
1198
Joseph Chanc577b8a2006-11-29 15:29:40 +01001199/*
1200 * Digital out
1201 */
1202static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1203 struct hda_codec *codec,
1204 struct snd_pcm_substream *substream)
1205{
1206 struct via_spec *spec = codec->spec;
1207 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1208}
1209
1210static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1211 struct hda_codec *codec,
1212 struct snd_pcm_substream *substream)
1213{
1214 struct via_spec *spec = codec->spec;
1215 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1216}
1217
Harald Welte5691ec72008-09-15 22:42:26 +08001218static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001219 struct hda_codec *codec,
1220 unsigned int stream_tag,
1221 unsigned int format,
1222 struct snd_pcm_substream *substream)
1223{
1224 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001225 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1226 stream_tag, format, substream);
1227}
Harald Welte5691ec72008-09-15 22:42:26 +08001228
Takashi Iwai9da29272009-05-07 16:31:14 +02001229static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1230 struct hda_codec *codec,
1231 struct snd_pcm_substream *substream)
1232{
1233 struct via_spec *spec = codec->spec;
1234 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001235 return 0;
1236}
1237
Joseph Chanc577b8a2006-11-29 15:29:40 +01001238/*
1239 * Analog capture
1240 */
1241static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1242 struct hda_codec *codec,
1243 unsigned int stream_tag,
1244 unsigned int format,
1245 struct snd_pcm_substream *substream)
1246{
1247 struct via_spec *spec = codec->spec;
1248
1249 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1250 stream_tag, 0, format);
1251 return 0;
1252}
1253
1254static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1255 struct hda_codec *codec,
1256 struct snd_pcm_substream *substream)
1257{
1258 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001259 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001260 return 0;
1261}
1262
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001263/* analog capture with dynamic ADC switching */
1264static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1265 struct hda_codec *codec,
1266 unsigned int stream_tag,
1267 unsigned int format,
1268 struct snd_pcm_substream *substream)
1269{
1270 struct via_spec *spec = codec->spec;
1271 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1272
Takashi Iwai3b607e32011-07-18 16:54:40 +02001273 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001274 spec->cur_adc = spec->adc_nids[adc_idx];
1275 spec->cur_adc_stream_tag = stream_tag;
1276 spec->cur_adc_format = format;
1277 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001278 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001279 return 0;
1280}
1281
1282static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1283 struct hda_codec *codec,
1284 struct snd_pcm_substream *substream)
1285{
1286 struct via_spec *spec = codec->spec;
1287
Takashi Iwai3b607e32011-07-18 16:54:40 +02001288 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001289 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1290 spec->cur_adc = 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001291 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001292 return 0;
1293}
1294
1295/* re-setup the stream if running; called from input-src put */
1296static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1297{
1298 struct via_spec *spec = codec->spec;
1299 int adc_idx = spec->inputs[cur].adc_idx;
1300 hda_nid_t adc = spec->adc_nids[adc_idx];
Takashi Iwai3b607e32011-07-18 16:54:40 +02001301 bool ret = false;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001302
Takashi Iwai3b607e32011-07-18 16:54:40 +02001303 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001304 if (spec->cur_adc && spec->cur_adc != adc) {
1305 /* stream is running, let's swap the current ADC */
1306 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1307 spec->cur_adc = adc;
1308 snd_hda_codec_setup_stream(codec, adc,
1309 spec->cur_adc_stream_tag, 0,
1310 spec->cur_adc_format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001311 ret = true;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001312 }
Takashi Iwai3b607e32011-07-18 16:54:40 +02001313 mutex_unlock(&spec->config_mutex);
1314 return ret;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001315}
1316
Takashi Iwai9af74212011-06-18 16:17:45 +02001317static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001318 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001319 .channels_min = 2,
1320 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001321 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001322 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001323 .open = via_playback_multi_pcm_open,
1324 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001325 .prepare = via_playback_multi_pcm_prepare,
1326 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001327 },
1328};
1329
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001330static const struct hda_pcm_stream via_pcm_hp_playback = {
1331 .substreams = 1,
1332 .channels_min = 2,
1333 .channels_max = 2,
1334 /* NID is set in via_build_pcms */
1335 .ops = {
1336 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001337 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001338 .prepare = via_playback_hp_pcm_prepare,
1339 .cleanup = via_playback_hp_pcm_cleanup
1340 },
1341};
1342
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001343static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001344 .substreams = 1,
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001345 .channels_min = 2,
1346 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001347 /* NID is set in via_build_pcms */
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001348 /* We got noisy outputs on the right channel on VT1708 when
1349 * 24bit samples are used. Until any workaround is found,
1350 * disable the 24bit format, so far.
1351 */
1352 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1353 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001354 .open = via_playback_multi_pcm_open,
1355 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001356 .prepare = via_playback_multi_pcm_prepare,
1357 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001358 },
1359};
1360
Takashi Iwai9af74212011-06-18 16:17:45 +02001361static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001362 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001363 .channels_min = 2,
1364 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001365 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001366 .ops = {
1367 .prepare = via_capture_pcm_prepare,
1368 .cleanup = via_capture_pcm_cleanup
1369 },
1370};
1371
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001372static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1373 .substreams = 1,
1374 .channels_min = 2,
1375 .channels_max = 2,
1376 /* NID is set in via_build_pcms */
1377 .ops = {
1378 .prepare = via_dyn_adc_capture_pcm_prepare,
1379 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1380 },
1381};
1382
Takashi Iwai9af74212011-06-18 16:17:45 +02001383static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001384 .substreams = 1,
1385 .channels_min = 2,
1386 .channels_max = 2,
1387 /* NID is set in via_build_pcms */
1388 .ops = {
1389 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001390 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001391 .prepare = via_dig_playback_pcm_prepare,
1392 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001393 },
1394};
1395
Takashi Iwai9af74212011-06-18 16:17:45 +02001396static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001397 .substreams = 1,
1398 .channels_min = 2,
1399 .channels_max = 2,
1400};
1401
Takashi Iwai370bafb2011-06-20 12:47:45 +02001402/*
1403 * slave controls for virtual master
1404 */
1405static const char * const via_slave_vols[] = {
1406 "Front Playback Volume",
1407 "Surround Playback Volume",
1408 "Center Playback Volume",
1409 "LFE Playback Volume",
1410 "Side Playback Volume",
1411 "Headphone Playback Volume",
1412 "Speaker Playback Volume",
1413 NULL,
1414};
1415
1416static const char * const via_slave_sws[] = {
1417 "Front Playback Switch",
1418 "Surround Playback Switch",
1419 "Center Playback Switch",
1420 "LFE Playback Switch",
1421 "Side Playback Switch",
1422 "Headphone Playback Switch",
1423 "Speaker Playback Switch",
1424 NULL,
1425};
1426
Joseph Chanc577b8a2006-11-29 15:29:40 +01001427static int via_build_controls(struct hda_codec *codec)
1428{
1429 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001430 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001431 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001432
Takashi Iwai24088a52011-06-17 16:59:21 +02001433 if (spec->set_widgets_power_state)
1434 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1435 return -ENOMEM;
1436
Joseph Chanc577b8a2006-11-29 15:29:40 +01001437 for (i = 0; i < spec->num_mixers; i++) {
1438 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1439 if (err < 0)
1440 return err;
1441 }
1442
1443 if (spec->multiout.dig_out_nid) {
1444 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001445 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001446 spec->multiout.dig_out_nid);
1447 if (err < 0)
1448 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001449 err = snd_hda_create_spdif_share_sw(codec,
1450 &spec->multiout);
1451 if (err < 0)
1452 return err;
1453 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001454 }
1455 if (spec->dig_in_nid) {
1456 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1457 if (err < 0)
1458 return err;
1459 }
Lydia Wang17314372009-10-10 19:07:37 +08001460
Takashi Iwai370bafb2011-06-20 12:47:45 +02001461 /* if we have no master control, let's create it */
1462 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1463 unsigned int vmaster_tlv[4];
1464 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1465 HDA_OUTPUT, vmaster_tlv);
1466 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1467 vmaster_tlv, via_slave_vols);
1468 if (err < 0)
1469 return err;
1470 }
1471 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1472 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1473 NULL, via_slave_sws);
1474 if (err < 0)
1475 return err;
1476 }
1477
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001478 /* assign Capture Source enums to NID */
1479 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1480 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001481 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001482 if (err < 0)
1483 return err;
1484 }
1485
Lydia Wang17314372009-10-10 19:07:37 +08001486 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001487 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001488 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001489
Takashi Iwai603c4012008-07-30 15:01:44 +02001490 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001491 return 0;
1492}
1493
1494static int via_build_pcms(struct hda_codec *codec)
1495{
1496 struct via_spec *spec = codec->spec;
1497 struct hda_pcm *info = spec->pcm_rec;
1498
1499 codec->num_pcms = 1;
1500 codec->pcm_info = info;
1501
Takashi Iwai82673bc2011-06-17 16:24:21 +02001502 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1503 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001504 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001505
1506 if (!spec->stream_analog_playback)
1507 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001508 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001509 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001510 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1511 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001512 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1513 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001514
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001515 if (!spec->stream_analog_capture) {
1516 if (spec->dyn_adc_switch)
1517 spec->stream_analog_capture =
1518 &via_pcm_dyn_adc_analog_capture;
1519 else
1520 spec->stream_analog_capture = &via_pcm_analog_capture;
1521 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001522 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1523 *spec->stream_analog_capture;
1524 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001525 if (!spec->dyn_adc_switch)
1526 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1527 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001528
1529 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1530 codec->num_pcms++;
1531 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001532 snprintf(spec->stream_name_digital,
1533 sizeof(spec->stream_name_digital),
1534 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001535 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001536 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001537 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001538 if (!spec->stream_digital_playback)
1539 spec->stream_digital_playback =
1540 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001541 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001542 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001543 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1544 spec->multiout.dig_out_nid;
1545 }
1546 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001547 if (!spec->stream_digital_capture)
1548 spec->stream_digital_capture =
1549 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001550 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001551 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001552 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1553 spec->dig_in_nid;
1554 }
1555 }
1556
Takashi Iwaiece8d042011-06-19 16:24:21 +02001557 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001558 codec->num_pcms++;
1559 info++;
1560 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1561 "%s HP", codec->chip_name);
1562 info->name = spec->stream_name_hp;
1563 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1564 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001565 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001566 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001567 return 0;
1568}
1569
1570static void via_free(struct hda_codec *codec)
1571{
1572 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001573
1574 if (!spec)
1575 return;
1576
Takashi Iwai603c4012008-07-30 15:01:44 +02001577 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001578 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001579 kfree(spec->bind_cap_vol);
1580 kfree(spec->bind_cap_sw);
1581 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001582}
1583
Takashi Iwai64be2852011-06-17 16:51:39 +02001584/* mute/unmute outputs */
1585static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1586 hda_nid_t *pins, bool mute)
1587{
1588 int i;
Takashi Iwai94994732011-07-11 11:36:44 +02001589 for (i = 0; i < num_pins; i++) {
1590 unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
1591 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1592 if (parm & AC_PINCTL_IN_EN)
1593 continue;
1594 if (mute)
1595 parm &= ~AC_PINCTL_OUT_EN;
1596 else
1597 parm |= AC_PINCTL_OUT_EN;
Takashi Iwai64be2852011-06-17 16:51:39 +02001598 snd_hda_codec_write(codec, pins[i], 0,
Takashi Iwai94994732011-07-11 11:36:44 +02001599 AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
1600 }
Takashi Iwai64be2852011-06-17 16:51:39 +02001601}
1602
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001603/* mute internal speaker if line-out is plugged */
1604static void via_line_automute(struct hda_codec *codec, int present)
1605{
1606 struct via_spec *spec = codec->spec;
1607
1608 if (!spec->autocfg.speaker_outs)
1609 return;
1610 if (!present)
1611 present = snd_hda_jack_detect(codec,
1612 spec->autocfg.line_out_pins[0]);
1613 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1614 spec->autocfg.speaker_pins,
1615 present);
1616}
1617
Harald Welte69e52a82008-09-09 15:57:32 +08001618/* mute internal speaker if HP is plugged */
1619static void via_hp_automute(struct hda_codec *codec)
1620{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001621 int present = 0;
Takashi Iwai6e969d92011-07-11 11:28:13 +02001622 int nums;
Harald Welte69e52a82008-09-09 15:57:32 +08001623 struct via_spec *spec = codec->spec;
1624
Takashi Iwai6e969d92011-07-11 11:28:13 +02001625 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0])
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001626 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai6e969d92011-07-11 11:28:13 +02001627
1628 if (spec->smart51_enabled)
1629 nums = spec->autocfg.line_outs + spec->smart51_nums;
1630 else
1631 nums = spec->autocfg.line_outs;
1632 toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
1633
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001634 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001635}
1636
Harald Welte69e52a82008-09-09 15:57:32 +08001637static void via_gpio_control(struct hda_codec *codec)
1638{
1639 unsigned int gpio_data;
1640 unsigned int vol_counter;
1641 unsigned int vol;
1642 unsigned int master_vol;
1643
1644 struct via_spec *spec = codec->spec;
1645
1646 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1647 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1648
1649 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1650 0xF84, 0) & 0x3F0000) >> 16;
1651
1652 vol = vol_counter & 0x1F;
1653 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1654 AC_VERB_GET_AMP_GAIN_MUTE,
1655 AC_AMP_GET_INPUT);
1656
1657 if (gpio_data == 0x02) {
1658 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001659 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1660 AC_VERB_SET_PIN_WIDGET_CONTROL,
1661 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001662 if (vol_counter & 0x20) {
1663 /* decrease volume */
1664 if (vol > master_vol)
1665 vol = master_vol;
1666 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1667 0, HDA_AMP_VOLMASK,
1668 master_vol-vol);
1669 } else {
1670 /* increase volume */
1671 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1672 HDA_AMP_VOLMASK,
1673 ((master_vol+vol) > 0x2A) ? 0x2A :
1674 (master_vol+vol));
1675 }
1676 } else if (!(gpio_data & 0x02)) {
1677 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001678 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1679 AC_VERB_SET_PIN_WIDGET_CONTROL,
1680 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001681 }
1682}
1683
1684/* unsolicited event for jack sensing */
1685static void via_unsol_event(struct hda_codec *codec,
1686 unsigned int res)
1687{
1688 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001689
Lydia Wanga34df192009-10-10 19:08:01 +08001690 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001691 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001692
1693 res &= ~VIA_JACK_EVENT;
1694
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001695 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001696 via_hp_automute(codec);
1697 else if (res == VIA_GPIO_EVENT)
1698 via_gpio_control(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001699}
1700
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001701#ifdef SND_HDA_NEEDS_RESUME
1702static int via_suspend(struct hda_codec *codec, pm_message_t state)
1703{
1704 struct via_spec *spec = codec->spec;
1705 vt1708_stop_hp_work(spec);
1706 return 0;
1707}
1708#endif
1709
Takashi Iwaicb53c622007-08-10 17:21:45 +02001710#ifdef CONFIG_SND_HDA_POWER_SAVE
1711static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1712{
1713 struct via_spec *spec = codec->spec;
1714 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1715}
1716#endif
1717
Joseph Chanc577b8a2006-11-29 15:29:40 +01001718/*
1719 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001720
1721static int via_init(struct hda_codec *codec);
1722
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001723static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001724 .build_controls = via_build_controls,
1725 .build_pcms = via_build_pcms,
1726 .init = via_init,
1727 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001728 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001729#ifdef SND_HDA_NEEDS_RESUME
1730 .suspend = via_suspend,
1731#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001732#ifdef CONFIG_SND_HDA_POWER_SAVE
1733 .check_power_status = via_check_power_status,
1734#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001735};
1736
Takashi Iwai4a796162011-06-17 17:53:38 +02001737static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001738{
Takashi Iwai4a796162011-06-17 17:53:38 +02001739 struct via_spec *spec = codec->spec;
1740 int i;
1741
1742 for (i = 0; i < spec->multiout.num_dacs; i++) {
1743 if (spec->multiout.dac_nids[i] == dac)
1744 return false;
1745 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001746 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001747 return false;
1748 return true;
1749}
1750
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001751static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001752 hda_nid_t target_dac, int with_aa_mix,
1753 struct nid_path *path, int depth)
Takashi Iwai4a796162011-06-17 17:53:38 +02001754{
Takashi Iwai3214b962011-07-18 12:49:25 +02001755 struct via_spec *spec = codec->spec;
Takashi Iwai4a796162011-06-17 17:53:38 +02001756 hda_nid_t conn[8];
1757 int i, nums;
1758
Takashi Iwai3214b962011-07-18 12:49:25 +02001759 if (nid == spec->aa_mix_nid) {
1760 if (!with_aa_mix)
1761 return false;
1762 with_aa_mix = 2; /* mark aa-mix is included */
1763 }
1764
Takashi Iwai4a796162011-06-17 17:53:38 +02001765 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1766 for (i = 0; i < nums; i++) {
1767 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1768 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001769 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1770 /* aa-mix is requested but not included? */
1771 if (!(spec->aa_mix_nid && with_aa_mix == 1))
1772 goto found;
1773 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001774 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001775 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001776 return false;
1777 for (i = 0; i < nums; i++) {
1778 unsigned int type;
1779 type = get_wcaps_type(get_wcaps(codec, conn[i]));
Takashi Iwai3214b962011-07-18 12:49:25 +02001780 if (type == AC_WID_AUD_OUT)
Takashi Iwai4a796162011-06-17 17:53:38 +02001781 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001782 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai3214b962011-07-18 12:49:25 +02001783 with_aa_mix, path, depth + 1))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001784 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001785 }
1786 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001787
1788 found:
1789 path->path[path->depth] = conn[i];
1790 path->idx[path->depth] = i;
1791 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1792 path->multi[path->depth] = 1;
1793 path->depth++;
1794 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001795}
1796
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001797static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001798 hda_nid_t target_dac, int with_aa_mix,
1799 struct nid_path *path)
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001800{
Takashi Iwai3214b962011-07-18 12:49:25 +02001801 if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001802 path->path[path->depth] = nid;
1803 path->depth++;
Takashi Iwai3214b962011-07-18 12:49:25 +02001804 snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
1805 path->depth, path->path[0], path->path[1],
1806 path->path[2], path->path[3], path->path[4]);
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001807 return true;
1808 }
1809 return false;
1810}
1811
Takashi Iwai4a796162011-06-17 17:53:38 +02001812static int via_auto_fill_dac_nids(struct hda_codec *codec)
1813{
1814 struct via_spec *spec = codec->spec;
1815 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001816 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001817 hda_nid_t nid;
1818
Joseph Chanc577b8a2006-11-29 15:29:40 +01001819 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001820 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001821 for (i = 0; i < cfg->line_outs; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001822 hda_nid_t dac = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001823 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001824 if (!nid)
1825 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001826 if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
1827 dac = spec->out_path[i].path[0];
1828 if (!i && parse_output_path(codec, nid, dac, 1,
1829 &spec->out_mix_path))
1830 dac = spec->out_mix_path.path[0];
1831 if (dac) {
1832 spec->private_dac_nids[i] = dac;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001833 dac_num++;
1834 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001835 }
Takashi Iwai3214b962011-07-18 12:49:25 +02001836 if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
1837 spec->out_path[0] = spec->out_mix_path;
1838 spec->out_mix_path.depth = 0;
1839 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001840 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001841 return 0;
1842}
1843
Takashi Iwai4a796162011-06-17 17:53:38 +02001844static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001845 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001846{
Takashi Iwai4a796162011-06-17 17:53:38 +02001847 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001848 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001849 hda_nid_t dac, pin, sel, nid;
1850 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001851
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001852 dac = check_dac ? path->path[0] : 0;
1853 pin = path->path[path->depth - 1];
1854 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001855
Takashi Iwai8df2a312011-06-21 11:48:29 +02001856 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001857 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001858 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001859 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001860 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1861 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001862 else
1863 nid = 0;
1864 if (nid) {
1865 sprintf(name, "%s Playback Volume", pfx);
1866 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001867 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001868 if (err < 0)
1869 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001870 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001871 }
1872
Takashi Iwai8df2a312011-06-21 11:48:29 +02001873 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001874 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001875 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001876 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001877 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1878 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001879 else
1880 nid = 0;
1881 if (nid) {
1882 sprintf(name, "%s Playback Switch", pfx);
1883 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1884 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1885 if (err < 0)
1886 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001887 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001888 }
1889 return 0;
1890}
1891
Takashi Iwaif4a78282011-06-17 18:46:48 +02001892static void mangle_smart51(struct hda_codec *codec)
1893{
1894 struct via_spec *spec = codec->spec;
1895 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001896 struct auto_pin_cfg_item *ins = cfg->inputs;
1897 int i, j, nums, attr;
1898 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001899
Takashi Iwai0f98c242011-06-21 12:51:33 +02001900 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1901 nums = 0;
1902 for (i = 0; i < cfg->num_inputs; i++) {
1903 unsigned int def;
1904 if (ins[i].type > AUTO_PIN_LINE_IN)
1905 continue;
1906 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1907 if (snd_hda_get_input_pin_attr(def) != attr)
1908 continue;
1909 for (j = 0; j < nums; j++)
1910 if (ins[pins[j]].type < ins[i].type) {
1911 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001912 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001913 break;
1914 }
1915 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001916 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001917 }
1918 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001919 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001920 for (i = 0; i < nums; i++) {
1921 hda_nid_t pin = ins[pins[i]].pin;
1922 spec->smart51_pins[spec->smart51_nums++] = pin;
1923 cfg->line_out_pins[cfg->line_outs++] = pin;
1924 if (cfg->line_outs == 3)
1925 break;
1926 }
1927 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001928 }
1929}
1930
Takashi Iwai4a796162011-06-17 17:53:38 +02001931/* add playback controls from the parsed DAC table */
1932static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1933{
1934 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001935 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai3214b962011-07-18 12:49:25 +02001936 struct nid_path *path;
Takashi Iwaiea734962011-01-17 11:29:34 +01001937 static const char * const chname[4] = {
1938 "Front", "Surround", "C/LFE", "Side"
1939 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001940 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001941 int old_line_outs;
1942
1943 /* check smart51 */
1944 old_line_outs = cfg->line_outs;
1945 if (cfg->line_outs == 1)
1946 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001947
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001948 err = via_auto_fill_dac_nids(codec);
1949 if (err < 0)
1950 return err;
1951
Lydia Wang5c9a5612011-07-08 14:03:43 +08001952 if (spec->multiout.num_dacs < 3) {
1953 spec->smart51_nums = 0;
1954 cfg->line_outs = old_line_outs;
1955 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001956 for (i = 0; i < cfg->line_outs; i++) {
1957 hda_nid_t pin, dac;
1958 pin = cfg->line_out_pins[i];
1959 dac = spec->multiout.dac_nids[i];
1960 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001961 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001962 path = spec->out_path + i;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001963 if (i == HDA_CLFE) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001964 err = create_ch_ctls(codec, "Center", 1, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001965 if (err < 0)
1966 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02001967 err = create_ch_ctls(codec, "LFE", 2, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001968 if (err < 0)
1969 return err;
1970 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001971 const char *pfx = chname[i];
1972 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1973 cfg->line_outs == 1)
1974 pfx = "Speaker";
Takashi Iwai3214b962011-07-18 12:49:25 +02001975 err = create_ch_ctls(codec, pfx, 3, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001976 if (err < 0)
1977 return err;
1978 }
Takashi Iwai3214b962011-07-18 12:49:25 +02001979 if (path != spec->out_path + i) {
1980 spec->out_path[i].vol_ctl = path->vol_ctl;
1981 spec->out_path[i].mute_ctl = path->mute_ctl;
1982 }
1983 if (path == spec->out_path && spec->out_mix_path.depth) {
1984 spec->out_mix_path.vol_ctl = path->vol_ctl;
1985 spec->out_mix_path.mute_ctl = path->mute_ctl;
1986 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001987 }
1988
Takashi Iwai4a796162011-06-17 17:53:38 +02001989 idx = get_connection_index(codec, spec->aa_mix_nid,
1990 spec->multiout.dac_nids[0]);
1991 if (idx >= 0) {
1992 /* add control to mixer */
Takashi Iwai3214b962011-07-18 12:49:25 +02001993 const char *name;
1994 name = spec->out_mix_path.depth ?
1995 "PCM Loopback Playback Volume" : "PCM Playback Volume";
1996 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02001997 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1998 idx, HDA_INPUT));
1999 if (err < 0)
2000 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002001 name = spec->out_mix_path.depth ?
2002 "PCM Loopback Playback Switch" : "PCM Playback Switch";
2003 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002004 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2005 idx, HDA_INPUT));
2006 if (err < 0)
2007 return err;
2008 }
2009
Takashi Iwaif4a78282011-06-17 18:46:48 +02002010 cfg->line_outs = old_line_outs;
2011
Joseph Chanc577b8a2006-11-29 15:29:40 +01002012 return 0;
2013}
2014
Takashi Iwai4a796162011-06-17 17:53:38 +02002015static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002016{
Takashi Iwai4a796162011-06-17 17:53:38 +02002017 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002018 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002019 bool check_dac;
Takashi Iwai3214b962011-07-18 12:49:25 +02002020 int i, err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002021
2022 if (!pin)
2023 return 0;
2024
Takashi Iwai3214b962011-07-18 12:49:25 +02002025 if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
2026 for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
2027 if (i < spec->multiout.num_dacs &&
2028 parse_output_path(codec, pin,
2029 spec->multiout.dac_nids[i], 0,
2030 &spec->hp_indep_path)) {
2031 spec->hp_indep_shared = i;
2032 break;
2033 }
2034 }
Takashi Iwai25250502011-06-30 17:24:47 +02002035 }
Takashi Iwai3214b962011-07-18 12:49:25 +02002036 if (spec->hp_indep_path.depth) {
2037 spec->hp_dac_nid = spec->hp_indep_path.path[0];
2038 if (!spec->hp_indep_shared)
2039 spec->hp_path = spec->hp_indep_path;
2040 }
2041 /* optionally check front-path w/o AA-mix */
2042 if (!spec->hp_path.depth)
2043 parse_output_path(codec, pin,
2044 spec->multiout.dac_nids[HDA_FRONT], 0,
2045 &spec->hp_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002046
Takashi Iwaiece8d042011-06-19 16:24:21 +02002047 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai3214b962011-07-18 12:49:25 +02002048 1, &spec->hp_mix_path) && !spec->hp_path.depth)
Takashi Iwaiece8d042011-06-19 16:24:21 +02002049 return 0;
2050
Takashi Iwai3214b962011-07-18 12:49:25 +02002051 if (spec->hp_path.depth) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002052 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002053 check_dac = true;
2054 } else {
Takashi Iwai3214b962011-07-18 12:49:25 +02002055 path = &spec->hp_mix_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002056 check_dac = false;
2057 }
2058 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002059 if (err < 0)
2060 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002061 if (check_dac) {
2062 spec->hp_mix_path.vol_ctl = path->vol_ctl;
2063 spec->hp_mix_path.mute_ctl = path->mute_ctl;
2064 } else {
2065 spec->hp_path.vol_ctl = path->vol_ctl;
2066 spec->hp_path.mute_ctl = path->mute_ctl;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002067 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002068 return 0;
2069}
2070
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002071static int via_auto_create_speaker_ctls(struct hda_codec *codec)
2072{
2073 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +02002074 struct nid_path *path;
2075 bool check_dac;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002076 hda_nid_t pin, dac;
Takashi Iwai3214b962011-07-18 12:49:25 +02002077 int err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002078
2079 pin = spec->autocfg.speaker_pins[0];
2080 if (!spec->autocfg.speaker_outs || !pin)
2081 return 0;
2082
Takashi Iwai3214b962011-07-18 12:49:25 +02002083 if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02002084 dac = spec->speaker_path.path[0];
Takashi Iwai3214b962011-07-18 12:49:25 +02002085 if (!dac)
2086 parse_output_path(codec, pin,
2087 spec->multiout.dac_nids[HDA_FRONT], 0,
2088 &spec->speaker_path);
2089 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
2090 1, &spec->speaker_mix_path) && !dac)
2091 return 0;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002092
Takashi Iwai3214b962011-07-18 12:49:25 +02002093 /* no AA-path for front? */
2094 if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
2095 dac = 0;
2096
2097 spec->speaker_dac_nid = dac;
2098 spec->multiout.extra_out_nid[0] = dac;
2099 if (dac) {
2100 path = &spec->speaker_path;
2101 check_dac = true;
2102 } else {
2103 path = &spec->speaker_mix_path;
2104 check_dac = false;
2105 }
2106 err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
2107 if (err < 0)
2108 return err;
2109 if (check_dac) {
2110 spec->speaker_mix_path.vol_ctl = path->vol_ctl;
2111 spec->speaker_mix_path.mute_ctl = path->mute_ctl;
2112 } else {
2113 spec->speaker_path.vol_ctl = path->vol_ctl;
2114 spec->speaker_path.mute_ctl = path->mute_ctl;
2115 }
2116 return 0;
2117}
2118
2119#define via_aamix_ctl_info via_pin_power_ctl_info
2120
2121static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol,
2122 struct snd_ctl_elem_value *ucontrol)
2123{
2124 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2125 struct via_spec *spec = codec->spec;
2126 ucontrol->value.enumerated.item[0] = spec->aamix_mode;
2127 return 0;
2128}
2129
2130static void update_aamix_paths(struct hda_codec *codec, int do_mix,
2131 struct nid_path *nomix, struct nid_path *mix)
2132{
2133 if (do_mix) {
2134 activate_output_path(codec, nomix, false, false);
2135 activate_output_path(codec, mix, true, false);
2136 } else {
2137 activate_output_path(codec, mix, false, false);
2138 activate_output_path(codec, nomix, true, false);
2139 }
2140}
2141
2142static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol,
2143 struct snd_ctl_elem_value *ucontrol)
2144{
2145 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2146 struct via_spec *spec = codec->spec;
2147 unsigned int val = ucontrol->value.enumerated.item[0];
2148
2149 if (val == spec->aamix_mode)
2150 return 0;
2151 spec->aamix_mode = val;
2152 /* update front path */
2153 update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
2154 /* update HP path */
2155 if (!spec->hp_independent_mode) {
2156 update_aamix_paths(codec, val, &spec->hp_path,
2157 &spec->hp_mix_path);
2158 }
2159 /* update speaker path */
2160 update_aamix_paths(codec, val, &spec->speaker_path,
2161 &spec->speaker_mix_path);
2162 return 1;
2163}
2164
2165static const struct snd_kcontrol_new via_aamix_ctl_enum = {
2166 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2167 .name = "Loopback Mixing",
2168 .info = via_aamix_ctl_info,
2169 .get = via_aamix_ctl_get,
2170 .put = via_aamix_ctl_put,
2171};
2172
2173static int via_auto_create_loopback_switch(struct hda_codec *codec)
2174{
2175 struct via_spec *spec = codec->spec;
2176
2177 if (!spec->aa_mix_nid || !spec->out_mix_path.depth)
2178 return 0; /* no loopback switching available */
2179 if (!via_clone_control(spec, &via_aamix_ctl_enum))
2180 return -ENOMEM;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002181 return 0;
2182}
2183
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002184/* look for ADCs */
2185static int via_fill_adcs(struct hda_codec *codec)
2186{
2187 struct via_spec *spec = codec->spec;
2188 hda_nid_t nid = codec->start_nid;
2189 int i;
2190
2191 for (i = 0; i < codec->num_nodes; i++, nid++) {
2192 unsigned int wcaps = get_wcaps(codec, nid);
2193 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2194 continue;
2195 if (wcaps & AC_WCAP_DIGITAL)
2196 continue;
2197 if (!(wcaps & AC_WCAP_CONN_LIST))
2198 continue;
2199 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2200 return -ENOMEM;
2201 spec->adc_nids[spec->num_adc_nids++] = nid;
2202 }
2203 return 0;
2204}
2205
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002206/* input-src control */
2207static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2208 struct snd_ctl_elem_info *uinfo)
2209{
2210 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2211 struct via_spec *spec = codec->spec;
2212
2213 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2214 uinfo->count = 1;
2215 uinfo->value.enumerated.items = spec->num_inputs;
2216 if (uinfo->value.enumerated.item >= spec->num_inputs)
2217 uinfo->value.enumerated.item = spec->num_inputs - 1;
2218 strcpy(uinfo->value.enumerated.name,
2219 spec->inputs[uinfo->value.enumerated.item].label);
2220 return 0;
2221}
2222
2223static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2224 struct snd_ctl_elem_value *ucontrol)
2225{
2226 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2227 struct via_spec *spec = codec->spec;
2228 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2229
2230 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2231 return 0;
2232}
2233
2234static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2235 struct snd_ctl_elem_value *ucontrol)
2236{
2237 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2238 struct via_spec *spec = codec->spec;
2239 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2240 hda_nid_t mux;
2241 int cur;
2242
2243 cur = ucontrol->value.enumerated.item[0];
2244 if (cur < 0 || cur >= spec->num_inputs)
2245 return -EINVAL;
2246 if (spec->cur_mux[idx] == cur)
2247 return 0;
2248 spec->cur_mux[idx] = cur;
2249 if (spec->dyn_adc_switch) {
2250 int adc_idx = spec->inputs[cur].adc_idx;
2251 mux = spec->mux_nids[adc_idx];
2252 via_dyn_adc_pcm_resetup(codec, cur);
2253 } else {
2254 mux = spec->mux_nids[idx];
2255 if (snd_BUG_ON(!mux))
2256 return -EINVAL;
2257 }
2258
2259 if (mux) {
2260 /* switch to D0 beofre change index */
2261 if (snd_hda_codec_read(codec, mux, 0,
2262 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2263 snd_hda_codec_write(codec, mux, 0,
2264 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2265 snd_hda_codec_write(codec, mux, 0,
2266 AC_VERB_SET_CONNECT_SEL,
2267 spec->inputs[cur].mux_idx);
2268 }
2269
2270 /* update jack power state */
2271 set_widgets_power_state(codec);
2272 return 0;
2273}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002274
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002275static const struct snd_kcontrol_new via_input_src_ctl = {
2276 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2277 /* The multiple "Capture Source" controls confuse alsamixer
2278 * So call somewhat different..
2279 */
2280 /* .name = "Capture Source", */
2281 .name = "Input Source",
2282 .info = via_mux_enum_info,
2283 .get = via_mux_enum_get,
2284 .put = via_mux_enum_put,
2285};
2286
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002287static int create_input_src_ctls(struct hda_codec *codec, int count)
2288{
2289 struct via_spec *spec = codec->spec;
2290 struct snd_kcontrol_new *knew;
2291
2292 if (spec->num_inputs <= 1 || !count)
2293 return 0; /* no need for single src */
2294
2295 knew = via_clone_control(spec, &via_input_src_ctl);
2296 if (!knew)
2297 return -ENOMEM;
2298 knew->count = count;
2299 return 0;
2300}
2301
2302/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002303static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2304{
2305 struct hda_amp_list *list;
2306
2307 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2308 return;
2309 list = spec->loopback_list + spec->num_loopbacks;
2310 list->nid = mix;
2311 list->dir = HDA_INPUT;
2312 list->idx = idx;
2313 spec->num_loopbacks++;
2314 spec->loopback.amplist = spec->loopback_list;
2315}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002316
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002317static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002318 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002319{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002320 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002321}
2322
2323/* add the input-route to the given pin */
2324static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002325{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002326 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002327 int c, idx;
2328
2329 spec->inputs[spec->num_inputs].adc_idx = -1;
2330 spec->inputs[spec->num_inputs].pin = pin;
2331 for (c = 0; c < spec->num_adc_nids; c++) {
2332 if (spec->mux_nids[c]) {
2333 idx = get_connection_index(codec, spec->mux_nids[c],
2334 pin);
2335 if (idx < 0)
2336 continue;
2337 spec->inputs[spec->num_inputs].mux_idx = idx;
2338 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002339 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002340 continue;
2341 }
2342 spec->inputs[spec->num_inputs].adc_idx = c;
2343 /* Can primary ADC satisfy all inputs? */
2344 if (!spec->dyn_adc_switch &&
2345 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2346 snd_printd(KERN_INFO
2347 "via: dynamic ADC switching enabled\n");
2348 spec->dyn_adc_switch = 1;
2349 }
2350 return true;
2351 }
2352 return false;
2353}
2354
2355static int get_mux_nids(struct hda_codec *codec);
2356
2357/* parse input-routes; fill ADCs, MUXs and input-src entries */
2358static int parse_analog_inputs(struct hda_codec *codec)
2359{
2360 struct via_spec *spec = codec->spec;
2361 const struct auto_pin_cfg *cfg = &spec->autocfg;
2362 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002363
2364 err = via_fill_adcs(codec);
2365 if (err < 0)
2366 return err;
2367 err = get_mux_nids(codec);
2368 if (err < 0)
2369 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002370
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002371 /* fill all input-routes */
2372 for (i = 0; i < cfg->num_inputs; i++) {
2373 if (add_input_route(codec, cfg->inputs[i].pin))
2374 spec->inputs[spec->num_inputs++].label =
2375 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002376 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002377
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002378 /* check for internal loopback recording */
2379 if (spec->aa_mix_nid &&
2380 add_input_route(codec, spec->aa_mix_nid))
2381 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2382
2383 return 0;
2384}
2385
2386/* create analog-loopback volume/switch controls */
2387static int create_loopback_ctls(struct hda_codec *codec)
2388{
2389 struct via_spec *spec = codec->spec;
2390 const struct auto_pin_cfg *cfg = &spec->autocfg;
2391 const char *prev_label = NULL;
2392 int type_idx = 0;
2393 int i, j, err, idx;
2394
2395 if (!spec->aa_mix_nid)
2396 return 0;
2397
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002398 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002399 hda_nid_t pin = cfg->inputs[i].pin;
2400 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2401
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002402 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002403 type_idx++;
2404 else
2405 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002406 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002407 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2408 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002409 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002410 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002411 if (err < 0)
2412 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002413 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002414 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002415
2416 /* remember the label for smart51 control */
2417 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002418 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002419 spec->smart51_idxs[j] = idx;
2420 spec->smart51_labels[j] = label;
2421 break;
2422 }
2423 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002424 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002425 return 0;
2426}
2427
2428/* create mic-boost controls (if present) */
2429static int create_mic_boost_ctls(struct hda_codec *codec)
2430{
2431 struct via_spec *spec = codec->spec;
2432 const struct auto_pin_cfg *cfg = &spec->autocfg;
2433 int i, err;
2434
2435 for (i = 0; i < cfg->num_inputs; i++) {
2436 hda_nid_t pin = cfg->inputs[i].pin;
2437 unsigned int caps;
2438 const char *label;
2439 char name[32];
2440
2441 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2442 continue;
2443 caps = query_amp_caps(codec, pin, HDA_INPUT);
2444 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2445 continue;
2446 label = hda_get_autocfg_input_label(codec, cfg, i);
2447 snprintf(name, sizeof(name), "%s Boost Volume", label);
2448 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2449 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2450 if (err < 0)
2451 return err;
2452 }
2453 return 0;
2454}
2455
2456/* create capture and input-src controls for multiple streams */
2457static int create_multi_adc_ctls(struct hda_codec *codec)
2458{
2459 struct via_spec *spec = codec->spec;
2460 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002461
2462 /* create capture mixer elements */
2463 for (i = 0; i < spec->num_adc_nids; i++) {
2464 hda_nid_t adc = spec->adc_nids[i];
2465 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2466 "Capture Volume", i,
2467 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2468 HDA_INPUT));
2469 if (err < 0)
2470 return err;
2471 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2472 "Capture Switch", i,
2473 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2474 HDA_INPUT));
2475 if (err < 0)
2476 return err;
2477 }
2478
2479 /* input-source control */
2480 for (i = 0; i < spec->num_adc_nids; i++)
2481 if (!spec->mux_nids[i])
2482 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002483 err = create_input_src_ctls(codec, i);
2484 if (err < 0)
2485 return err;
2486 return 0;
2487}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002488
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002489/* bind capture volume/switch */
2490static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2491 HDA_BIND_VOL("Capture Volume", 0);
2492static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2493 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002494
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002495static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2496 struct hda_ctl_ops *ops)
2497{
2498 struct hda_bind_ctls *ctl;
2499 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002500
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002501 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2502 if (!ctl)
2503 return -ENOMEM;
2504 ctl->ops = ops;
2505 for (i = 0; i < spec->num_adc_nids; i++)
2506 ctl->values[i] =
2507 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2508 *ctl_ret = ctl;
2509 return 0;
2510}
2511
2512/* create capture and input-src controls for dynamic ADC-switch case */
2513static int create_dyn_adc_ctls(struct hda_codec *codec)
2514{
2515 struct via_spec *spec = codec->spec;
2516 struct snd_kcontrol_new *knew;
2517 int err;
2518
2519 /* set up the bind capture ctls */
2520 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2521 if (err < 0)
2522 return err;
2523 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2524 if (err < 0)
2525 return err;
2526
2527 /* create capture mixer elements */
2528 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2529 if (!knew)
2530 return -ENOMEM;
2531 knew->private_value = (long)spec->bind_cap_vol;
2532
2533 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2534 if (!knew)
2535 return -ENOMEM;
2536 knew->private_value = (long)spec->bind_cap_sw;
2537
2538 /* input-source control */
2539 err = create_input_src_ctls(codec, 1);
2540 if (err < 0)
2541 return err;
2542 return 0;
2543}
2544
2545/* parse and create capture-related stuff */
2546static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2547{
2548 struct via_spec *spec = codec->spec;
2549 int err;
2550
2551 err = parse_analog_inputs(codec);
2552 if (err < 0)
2553 return err;
2554 if (spec->dyn_adc_switch)
2555 err = create_dyn_adc_ctls(codec);
2556 else
2557 err = create_multi_adc_ctls(codec);
2558 if (err < 0)
2559 return err;
2560 err = create_loopback_ctls(codec);
2561 if (err < 0)
2562 return err;
2563 err = create_mic_boost_ctls(codec);
2564 if (err < 0)
2565 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002566 return 0;
2567}
2568
Harald Welte76d9b0d2008-09-09 15:50:37 +08002569static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2570{
2571 unsigned int def_conf;
2572 unsigned char seqassoc;
2573
Takashi Iwai2f334f92009-02-20 14:37:42 +01002574 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002575 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2576 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002577 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2578 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2579 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2580 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002581 }
2582
2583 return;
2584}
2585
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002586static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002587 struct snd_ctl_elem_value *ucontrol)
2588{
2589 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2590 struct via_spec *spec = codec->spec;
2591
2592 if (spec->codec_type != VT1708)
2593 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002594 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002595 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002596 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002597 return 0;
2598}
2599
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002600static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002601 struct snd_ctl_elem_value *ucontrol)
2602{
2603 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2604 struct via_spec *spec = codec->spec;
2605 int change;
2606
2607 if (spec->codec_type != VT1708)
2608 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002609 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002610 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002611 == !spec->vt1708_jack_detect;
2612 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002613 mute_aa_path(codec, 1);
2614 notify_aa_path_ctls(codec);
2615 }
2616 return change;
2617}
2618
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002619static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2620 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2621 .name = "Jack Detect",
2622 .count = 1,
2623 .info = snd_ctl_boolean_mono_info,
2624 .get = vt1708_jack_detect_get,
2625 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002626};
2627
Takashi Iwai12daef62011-06-18 17:45:49 +02002628static void fill_dig_outs(struct hda_codec *codec);
2629static void fill_dig_in(struct hda_codec *codec);
2630
2631static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002632{
2633 struct via_spec *spec = codec->spec;
2634 int err;
2635
2636 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2637 if (err < 0)
2638 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002639 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002640 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002641
Takashi Iwai4a796162011-06-17 17:53:38 +02002642 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002643 if (err < 0)
2644 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002645 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002646 if (err < 0)
2647 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002648 err = via_auto_create_speaker_ctls(codec);
2649 if (err < 0)
2650 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002651 err = via_auto_create_loopback_switch(codec);
2652 if (err < 0)
2653 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002654 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002655 if (err < 0)
2656 return err;
2657
2658 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2659
Takashi Iwai12daef62011-06-18 17:45:49 +02002660 fill_dig_outs(codec);
2661 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002662
Takashi Iwai603c4012008-07-30 15:01:44 +02002663 if (spec->kctls.list)
2664 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002665
Joseph Chanc577b8a2006-11-29 15:29:40 +01002666
Takashi Iwai3214b962011-07-18 12:49:25 +02002667 if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002668 err = via_hp_build(codec);
2669 if (err < 0)
2670 return err;
2671 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002672
Takashi Iwaif4a78282011-06-17 18:46:48 +02002673 err = via_smart51_build(codec);
2674 if (err < 0)
2675 return err;
2676
Takashi Iwai5d417622011-06-20 11:32:27 +02002677 /* assign slave outs */
2678 if (spec->slave_dig_outs[0])
2679 codec->slave_dig_outs = spec->slave_dig_outs;
2680
Joseph Chanc577b8a2006-11-29 15:29:40 +01002681 return 1;
2682}
2683
Takashi Iwai5d417622011-06-20 11:32:27 +02002684static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002685{
Lydia Wang25eaba22009-10-10 19:08:43 +08002686 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002687 if (spec->multiout.dig_out_nid)
2688 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2689 if (spec->slave_dig_outs[0])
2690 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2691}
Lydia Wang25eaba22009-10-10 19:08:43 +08002692
Takashi Iwai5d417622011-06-20 11:32:27 +02002693static void via_auto_init_dig_in(struct hda_codec *codec)
2694{
2695 struct via_spec *spec = codec->spec;
2696 if (!spec->dig_in_nid)
2697 return;
2698 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2699 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2700}
2701
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002702/* initialize the unsolicited events */
2703static void via_auto_init_unsol_event(struct hda_codec *codec)
2704{
2705 struct via_spec *spec = codec->spec;
2706 struct auto_pin_cfg *cfg = &spec->autocfg;
2707 unsigned int ev;
2708 int i;
2709
2710 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2711 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2712 AC_VERB_SET_UNSOLICITED_ENABLE,
2713 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2714
2715 if (cfg->speaker_pins[0])
2716 ev = VIA_LINE_EVENT;
2717 else
2718 ev = 0;
2719 for (i = 0; i < cfg->line_outs; i++) {
2720 if (cfg->line_out_pins[i] &&
2721 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002722 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002723 AC_VERB_SET_UNSOLICITED_ENABLE,
2724 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2725 }
2726
2727 for (i = 0; i < cfg->num_inputs; i++) {
2728 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2729 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2730 AC_VERB_SET_UNSOLICITED_ENABLE,
2731 AC_USRSP_EN | VIA_JACK_EVENT);
2732 }
2733}
2734
Takashi Iwai5d417622011-06-20 11:32:27 +02002735static int via_init(struct hda_codec *codec)
2736{
2737 struct via_spec *spec = codec->spec;
2738 int i;
2739
2740 for (i = 0; i < spec->num_iverbs; i++)
2741 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2742
Joseph Chanc577b8a2006-11-29 15:29:40 +01002743 via_auto_init_multi_out(codec);
2744 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002745 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002746 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002747 via_auto_init_dig_outs(codec);
2748 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002749
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002750 via_auto_init_unsol_event(codec);
2751
2752 via_hp_automute(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002753
Joseph Chanc577b8a2006-11-29 15:29:40 +01002754 return 0;
2755}
2756
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002757static void vt1708_update_hp_jack_state(struct work_struct *work)
2758{
2759 struct via_spec *spec = container_of(work, struct via_spec,
2760 vt1708_hp_work.work);
2761 if (spec->codec_type != VT1708)
2762 return;
2763 /* if jack state toggled */
2764 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002765 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002766 spec->vt1708_hp_present ^= 1;
2767 via_hp_automute(spec->codec);
2768 }
2769 vt1708_start_hp_work(spec);
2770}
2771
Takashi Iwai337b9d02009-07-07 18:18:59 +02002772static int get_mux_nids(struct hda_codec *codec)
2773{
2774 struct via_spec *spec = codec->spec;
2775 hda_nid_t nid, conn[8];
2776 unsigned int type;
2777 int i, n;
2778
2779 for (i = 0; i < spec->num_adc_nids; i++) {
2780 nid = spec->adc_nids[i];
2781 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002782 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002783 if (type == AC_WID_PIN)
2784 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002785 n = snd_hda_get_connections(codec, nid, conn,
2786 ARRAY_SIZE(conn));
2787 if (n <= 0)
2788 break;
2789 if (n > 1) {
2790 spec->mux_nids[i] = nid;
2791 break;
2792 }
2793 nid = conn[0];
2794 }
2795 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002796 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002797}
2798
Joseph Chanc577b8a2006-11-29 15:29:40 +01002799static int patch_vt1708(struct hda_codec *codec)
2800{
2801 struct via_spec *spec;
2802 int err;
2803
2804 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002805 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002806 if (spec == NULL)
2807 return -ENOMEM;
2808
Takashi Iwai620e2b22011-06-17 17:19:19 +02002809 spec->aa_mix_nid = 0x17;
2810
Takashi Iwai12daef62011-06-18 17:45:49 +02002811 /* Add HP and CD pin config connect bit re-config action */
2812 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2813 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2814
Joseph Chanc577b8a2006-11-29 15:29:40 +01002815 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002816 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002817 if (err < 0) {
2818 via_free(codec);
2819 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002820 }
2821
Takashi Iwai12daef62011-06-18 17:45:49 +02002822 /* add jack detect on/off control */
2823 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2824 return -ENOMEM;
2825
Takashi Iwaibc9b562382008-05-23 17:50:27 +02002826 /* disable 32bit format on VT1708 */
2827 if (codec->vendor_id == 0x11061708)
2828 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002829
Lydia Wange322a362011-06-29 13:52:02 +08002830 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2831
Joseph Chanc577b8a2006-11-29 15:29:40 +01002832 codec->patch_ops = via_patch_ops;
2833
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002834 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002835 return 0;
2836}
2837
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002838static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002839{
2840 struct via_spec *spec;
2841 int err;
2842
2843 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002844 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002845 if (spec == NULL)
2846 return -ENOMEM;
2847
Takashi Iwai620e2b22011-06-17 17:19:19 +02002848 spec->aa_mix_nid = 0x18;
2849
Takashi Iwai12daef62011-06-18 17:45:49 +02002850 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002851 if (err < 0) {
2852 via_free(codec);
2853 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002854 }
2855
Joseph Chanc577b8a2006-11-29 15:29:40 +01002856 codec->patch_ops = via_patch_ops;
2857
Josepch Chanf7278fd2007-12-13 16:40:40 +01002858 return 0;
2859}
2860
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002861static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2862{
2863 struct via_spec *spec = codec->spec;
2864 int imux_is_smixer;
2865 unsigned int parm;
2866 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002867 if ((spec->codec_type != VT1708B_4CH) &&
2868 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002869 is_8ch = 1;
2870
2871 /* SW0 (17h) = stereo mixer */
2872 imux_is_smixer =
2873 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2874 == ((spec->codec_type == VT1708S) ? 5 : 0));
2875 /* inputs */
2876 /* PW 1/2/5 (1ah/1bh/1eh) */
2877 parm = AC_PWRST_D3;
2878 set_pin_power_state(codec, 0x1a, &parm);
2879 set_pin_power_state(codec, 0x1b, &parm);
2880 set_pin_power_state(codec, 0x1e, &parm);
2881 if (imux_is_smixer)
2882 parm = AC_PWRST_D0;
2883 /* SW0 (17h), AIW 0/1 (13h/14h) */
2884 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2885 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2886 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2887
2888 /* outputs */
2889 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2890 parm = AC_PWRST_D3;
2891 set_pin_power_state(codec, 0x19, &parm);
2892 if (spec->smart51_enabled)
2893 set_pin_power_state(codec, 0x1b, &parm);
2894 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2895 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2896
2897 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2898 if (is_8ch) {
2899 parm = AC_PWRST_D3;
2900 set_pin_power_state(codec, 0x22, &parm);
2901 if (spec->smart51_enabled)
2902 set_pin_power_state(codec, 0x1a, &parm);
2903 snd_hda_codec_write(codec, 0x26, 0,
2904 AC_VERB_SET_POWER_STATE, parm);
2905 snd_hda_codec_write(codec, 0x24, 0,
2906 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002907 } else if (codec->vendor_id == 0x11064397) {
2908 /* PW7(23h), SW2(27h), AOW2(25h) */
2909 parm = AC_PWRST_D3;
2910 set_pin_power_state(codec, 0x23, &parm);
2911 if (spec->smart51_enabled)
2912 set_pin_power_state(codec, 0x1a, &parm);
2913 snd_hda_codec_write(codec, 0x27, 0,
2914 AC_VERB_SET_POWER_STATE, parm);
2915 snd_hda_codec_write(codec, 0x25, 0,
2916 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002917 }
2918
2919 /* PW 3/4/7 (1ch/1dh/23h) */
2920 parm = AC_PWRST_D3;
2921 /* force to D0 for internal Speaker */
2922 set_pin_power_state(codec, 0x1c, &parm);
2923 set_pin_power_state(codec, 0x1d, &parm);
2924 if (is_8ch)
2925 set_pin_power_state(codec, 0x23, &parm);
2926
2927 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2928 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2929 imux_is_smixer ? AC_PWRST_D0 : parm);
2930 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2931 if (is_8ch) {
2932 snd_hda_codec_write(codec, 0x25, 0,
2933 AC_VERB_SET_POWER_STATE, parm);
2934 snd_hda_codec_write(codec, 0x27, 0,
2935 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002936 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2937 snd_hda_codec_write(codec, 0x25, 0,
2938 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002939}
2940
Lydia Wang518bf3b2009-10-10 19:07:29 +08002941static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002942static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002943{
2944 struct via_spec *spec;
2945 int err;
2946
Lydia Wang518bf3b2009-10-10 19:07:29 +08002947 if (get_codec_type(codec) == VT1708BCE)
2948 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002949
Josepch Chanf7278fd2007-12-13 16:40:40 +01002950 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002951 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002952 if (spec == NULL)
2953 return -ENOMEM;
2954
Takashi Iwai620e2b22011-06-17 17:19:19 +02002955 spec->aa_mix_nid = 0x16;
2956
Josepch Chanf7278fd2007-12-13 16:40:40 +01002957 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002958 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002959 if (err < 0) {
2960 via_free(codec);
2961 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002962 }
2963
Josepch Chanf7278fd2007-12-13 16:40:40 +01002964 codec->patch_ops = via_patch_ops;
2965
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002966 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2967
Josepch Chanf7278fd2007-12-13 16:40:40 +01002968 return 0;
2969}
2970
Harald Welted949cac2008-09-09 15:56:01 +08002971/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002972static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002973 /* Enable Mic Boost Volume backdoor */
2974 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002975 /* don't bybass mixer */
2976 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002977 { }
2978};
2979
Takashi Iwai9da29272009-05-07 16:31:14 +02002980/* fill out digital output widgets; one for master and one for slave outputs */
2981static void fill_dig_outs(struct hda_codec *codec)
2982{
2983 struct via_spec *spec = codec->spec;
2984 int i;
2985
2986 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2987 hda_nid_t nid;
2988 int conn;
2989
2990 nid = spec->autocfg.dig_out_pins[i];
2991 if (!nid)
2992 continue;
2993 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2994 if (conn < 1)
2995 continue;
2996 if (!spec->multiout.dig_out_nid)
2997 spec->multiout.dig_out_nid = nid;
2998 else {
2999 spec->slave_dig_outs[0] = nid;
3000 break; /* at most two dig outs */
3001 }
3002 }
3003}
3004
Takashi Iwai12daef62011-06-18 17:45:49 +02003005static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08003006{
3007 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02003008 hda_nid_t dig_nid;
3009 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08003010
Takashi Iwai12daef62011-06-18 17:45:49 +02003011 if (!spec->autocfg.dig_in_pin)
3012 return;
Harald Welted949cac2008-09-09 15:56:01 +08003013
Takashi Iwai12daef62011-06-18 17:45:49 +02003014 dig_nid = codec->start_nid;
3015 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
3016 unsigned int wcaps = get_wcaps(codec, dig_nid);
3017 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
3018 continue;
3019 if (!(wcaps & AC_WCAP_DIGITAL))
3020 continue;
3021 if (!(wcaps & AC_WCAP_CONN_LIST))
3022 continue;
3023 err = get_connection_index(codec, dig_nid,
3024 spec->autocfg.dig_in_pin);
3025 if (err >= 0) {
3026 spec->dig_in_nid = dig_nid;
3027 break;
3028 }
3029 }
Harald Welted949cac2008-09-09 15:56:01 +08003030}
3031
Lydia Wang6369bcf2009-10-10 19:08:31 +08003032static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
3033 int offset, int num_steps, int step_size)
3034{
3035 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
3036 (offset << AC_AMPCAP_OFFSET_SHIFT) |
3037 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
3038 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
3039 (0 << AC_AMPCAP_MUTE_SHIFT));
3040}
3041
Harald Welted949cac2008-09-09 15:56:01 +08003042static int patch_vt1708S(struct hda_codec *codec)
3043{
3044 struct via_spec *spec;
3045 int err;
3046
3047 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003048 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003049 if (spec == NULL)
3050 return -ENOMEM;
3051
Takashi Iwai620e2b22011-06-17 17:19:19 +02003052 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003053 override_mic_boost(codec, 0x1a, 0, 3, 40);
3054 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003055
Harald Welted949cac2008-09-09 15:56:01 +08003056 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003057 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003058 if (err < 0) {
3059 via_free(codec);
3060 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003061 }
3062
Takashi Iwai096a8852011-06-20 12:09:02 +02003063 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003064
Harald Welted949cac2008-09-09 15:56:01 +08003065 codec->patch_ops = via_patch_ops;
3066
Lydia Wang518bf3b2009-10-10 19:07:29 +08003067 /* correct names for VT1708BCE */
3068 if (get_codec_type(codec) == VT1708BCE) {
3069 kfree(codec->chip_name);
3070 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3071 snprintf(codec->bus->card->mixername,
3072 sizeof(codec->bus->card->mixername),
3073 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f630f2011-03-22 16:25:56 +08003074 }
Lydia Wangbc92df72011-03-23 17:56:05 +08003075 /* correct names for VT1705 */
3076 if (codec->vendor_id == 0x11064397) {
3077 kfree(codec->chip_name);
3078 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3079 snprintf(codec->bus->card->mixername,
3080 sizeof(codec->bus->card->mixername),
3081 "%s %s", codec->vendor_name, codec->chip_name);
3082 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003083 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08003084 return 0;
3085}
3086
3087/* Patch for VT1702 */
3088
Takashi Iwai096a8852011-06-20 12:09:02 +02003089static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003090 /* mixer enable */
3091 {0x1, 0xF88, 0x3},
3092 /* GPIO 0~2 */
3093 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003094 { }
3095};
3096
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003097static void set_widgets_power_state_vt1702(struct hda_codec *codec)
3098{
3099 int imux_is_smixer =
3100 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3101 unsigned int parm;
3102 /* inputs */
3103 /* PW 1/2/5 (14h/15h/18h) */
3104 parm = AC_PWRST_D3;
3105 set_pin_power_state(codec, 0x14, &parm);
3106 set_pin_power_state(codec, 0x15, &parm);
3107 set_pin_power_state(codec, 0x18, &parm);
3108 if (imux_is_smixer)
3109 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
3110 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
3111 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3112 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
3113 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3114 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
3115
3116 /* outputs */
3117 /* PW 3/4 (16h/17h) */
3118 parm = AC_PWRST_D3;
3119 set_pin_power_state(codec, 0x17, &parm);
3120 set_pin_power_state(codec, 0x16, &parm);
3121 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
3122 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
3123 imux_is_smixer ? AC_PWRST_D0 : parm);
3124 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3125 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3126}
3127
Harald Welted949cac2008-09-09 15:56:01 +08003128static int patch_vt1702(struct hda_codec *codec)
3129{
3130 struct via_spec *spec;
3131 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003132
3133 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003134 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003135 if (spec == NULL)
3136 return -ENOMEM;
3137
Takashi Iwai620e2b22011-06-17 17:19:19 +02003138 spec->aa_mix_nid = 0x1a;
3139
Takashi Iwai12daef62011-06-18 17:45:49 +02003140 /* limit AA path volume to 0 dB */
3141 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3142 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3143 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3144 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3145 (1 << AC_AMPCAP_MUTE_SHIFT));
3146
Harald Welted949cac2008-09-09 15:56:01 +08003147 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003148 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003149 if (err < 0) {
3150 via_free(codec);
3151 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003152 }
3153
Takashi Iwai096a8852011-06-20 12:09:02 +02003154 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003155
Harald Welted949cac2008-09-09 15:56:01 +08003156 codec->patch_ops = via_patch_ops;
3157
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003158 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08003159 return 0;
3160}
3161
Lydia Wangeb7188c2009-10-10 19:08:34 +08003162/* Patch for VT1718S */
3163
Takashi Iwai096a8852011-06-20 12:09:02 +02003164static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08003165 /* Enable MW0 adjust Gain 5 */
3166 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003167 /* Enable Boost Volume backdoor */
3168 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02003169
Lydia Wangeb7188c2009-10-10 19:08:34 +08003170 { }
3171};
3172
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003173static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
3174{
3175 struct via_spec *spec = codec->spec;
3176 int imux_is_smixer;
3177 unsigned int parm;
3178 /* MUX6 (1eh) = stereo mixer */
3179 imux_is_smixer =
3180 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3181 /* inputs */
3182 /* PW 5/6/7 (29h/2ah/2bh) */
3183 parm = AC_PWRST_D3;
3184 set_pin_power_state(codec, 0x29, &parm);
3185 set_pin_power_state(codec, 0x2a, &parm);
3186 set_pin_power_state(codec, 0x2b, &parm);
3187 if (imux_is_smixer)
3188 parm = AC_PWRST_D0;
3189 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
3190 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3191 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3192 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3193 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3194
3195 /* outputs */
3196 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
3197 parm = AC_PWRST_D3;
3198 set_pin_power_state(codec, 0x27, &parm);
3199 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
3200 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
3201
3202 /* PW2 (26h), AOW2 (ah) */
3203 parm = AC_PWRST_D3;
3204 set_pin_power_state(codec, 0x26, &parm);
3205 if (spec->smart51_enabled)
3206 set_pin_power_state(codec, 0x2b, &parm);
3207 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
3208
3209 /* PW0 (24h), AOW0 (8h) */
3210 parm = AC_PWRST_D3;
3211 set_pin_power_state(codec, 0x24, &parm);
3212 if (!spec->hp_independent_mode) /* check for redirected HP */
3213 set_pin_power_state(codec, 0x28, &parm);
3214 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3215 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
3216 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
3217 imux_is_smixer ? AC_PWRST_D0 : parm);
3218
3219 /* PW1 (25h), AOW1 (9h) */
3220 parm = AC_PWRST_D3;
3221 set_pin_power_state(codec, 0x25, &parm);
3222 if (spec->smart51_enabled)
3223 set_pin_power_state(codec, 0x2a, &parm);
3224 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
3225
3226 if (spec->hp_independent_mode) {
3227 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3228 parm = AC_PWRST_D3;
3229 set_pin_power_state(codec, 0x28, &parm);
3230 snd_hda_codec_write(codec, 0x1b, 0,
3231 AC_VERB_SET_POWER_STATE, parm);
3232 snd_hda_codec_write(codec, 0x34, 0,
3233 AC_VERB_SET_POWER_STATE, parm);
3234 snd_hda_codec_write(codec, 0xc, 0,
3235 AC_VERB_SET_POWER_STATE, parm);
3236 }
3237}
3238
Takashi Iwai30b45032011-07-11 17:05:04 +02003239/* Add a connection to the primary DAC from AA-mixer for some codecs
3240 * This isn't listed from the raw info, but the chip has a secret connection.
3241 */
3242static int add_secret_dac_path(struct hda_codec *codec)
3243{
3244 struct via_spec *spec = codec->spec;
3245 int i, nums;
3246 hda_nid_t conn[8];
3247 hda_nid_t nid;
3248
3249 if (!spec->aa_mix_nid)
3250 return 0;
3251 nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
3252 ARRAY_SIZE(conn) - 1);
3253 for (i = 0; i < nums; i++) {
3254 if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
3255 return 0;
3256 }
3257
3258 /* find the primary DAC and add to the connection list */
3259 nid = codec->start_nid;
3260 for (i = 0; i < codec->num_nodes; i++, nid++) {
3261 unsigned int caps = get_wcaps(codec, nid);
3262 if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
3263 !(caps & AC_WCAP_DIGITAL)) {
3264 conn[nums++] = nid;
3265 return snd_hda_override_conn_list(codec,
3266 spec->aa_mix_nid,
3267 nums, conn);
3268 }
3269 }
3270 return 0;
3271}
3272
3273
Lydia Wangeb7188c2009-10-10 19:08:34 +08003274static int patch_vt1718S(struct hda_codec *codec)
3275{
3276 struct via_spec *spec;
3277 int err;
3278
3279 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003280 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003281 if (spec == NULL)
3282 return -ENOMEM;
3283
Takashi Iwai620e2b22011-06-17 17:19:19 +02003284 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003285 override_mic_boost(codec, 0x2b, 0, 3, 40);
3286 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003287 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003288
Lydia Wangeb7188c2009-10-10 19:08:34 +08003289 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003290 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003291 if (err < 0) {
3292 via_free(codec);
3293 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003294 }
3295
Takashi Iwai096a8852011-06-20 12:09:02 +02003296 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003297
Lydia Wangeb7188c2009-10-10 19:08:34 +08003298 codec->patch_ops = via_patch_ops;
3299
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003300 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3301
Lydia Wangeb7188c2009-10-10 19:08:34 +08003302 return 0;
3303}
Lydia Wangf3db4232009-10-10 19:08:41 +08003304
3305/* Patch for VT1716S */
3306
3307static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3308 struct snd_ctl_elem_info *uinfo)
3309{
3310 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3311 uinfo->count = 1;
3312 uinfo->value.integer.min = 0;
3313 uinfo->value.integer.max = 1;
3314 return 0;
3315}
3316
3317static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3318 struct snd_ctl_elem_value *ucontrol)
3319{
3320 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3321 int index = 0;
3322
3323 index = snd_hda_codec_read(codec, 0x26, 0,
3324 AC_VERB_GET_CONNECT_SEL, 0);
3325 if (index != -1)
3326 *ucontrol->value.integer.value = index;
3327
3328 return 0;
3329}
3330
3331static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3332 struct snd_ctl_elem_value *ucontrol)
3333{
3334 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3335 struct via_spec *spec = codec->spec;
3336 int index = *ucontrol->value.integer.value;
3337
3338 snd_hda_codec_write(codec, 0x26, 0,
3339 AC_VERB_SET_CONNECT_SEL, index);
3340 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003341 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003342 return 1;
3343}
3344
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003345static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003346 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3347 {
3348 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3349 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003350 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003351 .count = 1,
3352 .info = vt1716s_dmic_info,
3353 .get = vt1716s_dmic_get,
3354 .put = vt1716s_dmic_put,
3355 },
3356 {} /* end */
3357};
3358
3359
3360/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003361static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003362 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3363 { } /* end */
3364};
3365
Takashi Iwai096a8852011-06-20 12:09:02 +02003366static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003367 /* Enable Boost Volume backdoor */
3368 {0x1, 0xf8a, 0x80},
3369 /* don't bybass mixer */
3370 {0x1, 0xf88, 0xc0},
3371 /* Enable mono output */
3372 {0x1, 0xf90, 0x08},
3373 { }
3374};
3375
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003376static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3377{
3378 struct via_spec *spec = codec->spec;
3379 int imux_is_smixer;
3380 unsigned int parm;
3381 unsigned int mono_out, present;
3382 /* SW0 (17h) = stereo mixer */
3383 imux_is_smixer =
3384 (snd_hda_codec_read(codec, 0x17, 0,
3385 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3386 /* inputs */
3387 /* PW 1/2/5 (1ah/1bh/1eh) */
3388 parm = AC_PWRST_D3;
3389 set_pin_power_state(codec, 0x1a, &parm);
3390 set_pin_power_state(codec, 0x1b, &parm);
3391 set_pin_power_state(codec, 0x1e, &parm);
3392 if (imux_is_smixer)
3393 parm = AC_PWRST_D0;
3394 /* SW0 (17h), AIW0(13h) */
3395 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3396 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3397
3398 parm = AC_PWRST_D3;
3399 set_pin_power_state(codec, 0x1e, &parm);
3400 /* PW11 (22h) */
3401 if (spec->dmic_enabled)
3402 set_pin_power_state(codec, 0x22, &parm);
3403 else
3404 snd_hda_codec_write(codec, 0x22, 0,
3405 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3406
3407 /* SW2(26h), AIW1(14h) */
3408 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3409 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3410
3411 /* outputs */
3412 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3413 parm = AC_PWRST_D3;
3414 set_pin_power_state(codec, 0x19, &parm);
3415 /* Smart 5.1 PW2(1bh) */
3416 if (spec->smart51_enabled)
3417 set_pin_power_state(codec, 0x1b, &parm);
3418 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3419 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3420
3421 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3422 parm = AC_PWRST_D3;
3423 set_pin_power_state(codec, 0x23, &parm);
3424 /* Smart 5.1 PW1(1ah) */
3425 if (spec->smart51_enabled)
3426 set_pin_power_state(codec, 0x1a, &parm);
3427 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3428
3429 /* Smart 5.1 PW5(1eh) */
3430 if (spec->smart51_enabled)
3431 set_pin_power_state(codec, 0x1e, &parm);
3432 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3433
3434 /* Mono out */
3435 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3436 present = snd_hda_jack_detect(codec, 0x1c);
3437
3438 if (present)
3439 mono_out = 0;
3440 else {
3441 present = snd_hda_jack_detect(codec, 0x1d);
3442 if (!spec->hp_independent_mode && present)
3443 mono_out = 0;
3444 else
3445 mono_out = 1;
3446 }
3447 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3448 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3449 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3450 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3451
3452 /* PW 3/4 (1ch/1dh) */
3453 parm = AC_PWRST_D3;
3454 set_pin_power_state(codec, 0x1c, &parm);
3455 set_pin_power_state(codec, 0x1d, &parm);
3456 /* HP Independent Mode, power on AOW3 */
3457 if (spec->hp_independent_mode)
3458 snd_hda_codec_write(codec, 0x25, 0,
3459 AC_VERB_SET_POWER_STATE, parm);
3460
3461 /* force to D0 for internal Speaker */
3462 /* MW0 (16h), AOW0 (10h) */
3463 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3464 imux_is_smixer ? AC_PWRST_D0 : parm);
3465 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3466 mono_out ? AC_PWRST_D0 : parm);
3467}
3468
Lydia Wangf3db4232009-10-10 19:08:41 +08003469static int patch_vt1716S(struct hda_codec *codec)
3470{
3471 struct via_spec *spec;
3472 int err;
3473
3474 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003475 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003476 if (spec == NULL)
3477 return -ENOMEM;
3478
Takashi Iwai620e2b22011-06-17 17:19:19 +02003479 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003480 override_mic_boost(codec, 0x1a, 0, 3, 40);
3481 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003482
Lydia Wangf3db4232009-10-10 19:08:41 +08003483 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003484 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003485 if (err < 0) {
3486 via_free(codec);
3487 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003488 }
3489
Takashi Iwai096a8852011-06-20 12:09:02 +02003490 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003491
Lydia Wangf3db4232009-10-10 19:08:41 +08003492 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3493 spec->num_mixers++;
3494
3495 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3496
3497 codec->patch_ops = via_patch_ops;
3498
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003499 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003500 return 0;
3501}
Lydia Wang25eaba22009-10-10 19:08:43 +08003502
3503/* for vt2002P */
3504
Takashi Iwai096a8852011-06-20 12:09:02 +02003505static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003506 /* Class-D speaker related verbs */
3507 {0x1, 0xfe0, 0x4},
3508 {0x1, 0xfe9, 0x80},
3509 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003510 /* Enable Boost Volume backdoor */
3511 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003512 /* Enable AOW0 to MW9 */
3513 {0x1, 0xfb8, 0x88},
3514 { }
3515};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003516
Takashi Iwai096a8852011-06-20 12:09:02 +02003517static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003518 /* Enable Boost Volume backdoor */
3519 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003520 /* Enable AOW0 to MW9 */
3521 {0x1, 0xfb8, 0x88},
3522 { }
3523};
Lydia Wang25eaba22009-10-10 19:08:43 +08003524
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003525static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3526{
3527 struct via_spec *spec = codec->spec;
3528 int imux_is_smixer;
3529 unsigned int parm;
3530 unsigned int present;
3531 /* MUX9 (1eh) = stereo mixer */
3532 imux_is_smixer =
3533 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3534 /* inputs */
3535 /* PW 5/6/7 (29h/2ah/2bh) */
3536 parm = AC_PWRST_D3;
3537 set_pin_power_state(codec, 0x29, &parm);
3538 set_pin_power_state(codec, 0x2a, &parm);
3539 set_pin_power_state(codec, 0x2b, &parm);
3540 parm = AC_PWRST_D0;
3541 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3542 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3543 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3544 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3545 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3546
3547 /* outputs */
3548 /* AOW0 (8h)*/
3549 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3550
Lydia Wang118909562011-03-23 17:57:34 +08003551 if (spec->codec_type == VT1802) {
3552 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3553 parm = AC_PWRST_D3;
3554 set_pin_power_state(codec, 0x28, &parm);
3555 snd_hda_codec_write(codec, 0x18, 0,
3556 AC_VERB_SET_POWER_STATE, parm);
3557 snd_hda_codec_write(codec, 0x38, 0,
3558 AC_VERB_SET_POWER_STATE, parm);
3559 } else {
3560 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3561 parm = AC_PWRST_D3;
3562 set_pin_power_state(codec, 0x26, &parm);
3563 snd_hda_codec_write(codec, 0x1c, 0,
3564 AC_VERB_SET_POWER_STATE, parm);
3565 snd_hda_codec_write(codec, 0x37, 0,
3566 AC_VERB_SET_POWER_STATE, parm);
3567 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003568
Lydia Wang118909562011-03-23 17:57:34 +08003569 if (spec->codec_type == VT1802) {
3570 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3571 parm = AC_PWRST_D3;
3572 set_pin_power_state(codec, 0x25, &parm);
3573 snd_hda_codec_write(codec, 0x15, 0,
3574 AC_VERB_SET_POWER_STATE, parm);
3575 snd_hda_codec_write(codec, 0x35, 0,
3576 AC_VERB_SET_POWER_STATE, parm);
3577 } else {
3578 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3579 parm = AC_PWRST_D3;
3580 set_pin_power_state(codec, 0x25, &parm);
3581 snd_hda_codec_write(codec, 0x19, 0,
3582 AC_VERB_SET_POWER_STATE, parm);
3583 snd_hda_codec_write(codec, 0x35, 0,
3584 AC_VERB_SET_POWER_STATE, parm);
3585 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003586
3587 if (spec->hp_independent_mode)
3588 snd_hda_codec_write(codec, 0x9, 0,
3589 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3590
3591 /* Class-D */
3592 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3593 present = snd_hda_jack_detect(codec, 0x25);
3594
3595 parm = AC_PWRST_D3;
3596 set_pin_power_state(codec, 0x24, &parm);
3597 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003598 if (spec->codec_type == VT1802)
3599 snd_hda_codec_write(codec, 0x14, 0,
3600 AC_VERB_SET_POWER_STATE, parm);
3601 else
3602 snd_hda_codec_write(codec, 0x18, 0,
3603 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003604 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3605
3606 /* Mono Out */
3607 present = snd_hda_jack_detect(codec, 0x26);
3608
3609 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003610 if (spec->codec_type == VT1802) {
3611 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3612 snd_hda_codec_write(codec, 0x33, 0,
3613 AC_VERB_SET_POWER_STATE, parm);
3614 snd_hda_codec_write(codec, 0x1c, 0,
3615 AC_VERB_SET_POWER_STATE, parm);
3616 snd_hda_codec_write(codec, 0x3c, 0,
3617 AC_VERB_SET_POWER_STATE, parm);
3618 } else {
3619 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3620 snd_hda_codec_write(codec, 0x31, 0,
3621 AC_VERB_SET_POWER_STATE, parm);
3622 snd_hda_codec_write(codec, 0x17, 0,
3623 AC_VERB_SET_POWER_STATE, parm);
3624 snd_hda_codec_write(codec, 0x3b, 0,
3625 AC_VERB_SET_POWER_STATE, parm);
3626 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003627 /* MW9 (21h) */
3628 if (imux_is_smixer || !is_aa_path_mute(codec))
3629 snd_hda_codec_write(codec, 0x21, 0,
3630 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3631 else
3632 snd_hda_codec_write(codec, 0x21, 0,
3633 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3634}
Lydia Wang25eaba22009-10-10 19:08:43 +08003635
3636/* patch for vt2002P */
3637static int patch_vt2002P(struct hda_codec *codec)
3638{
3639 struct via_spec *spec;
3640 int err;
3641
3642 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003643 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003644 if (spec == NULL)
3645 return -ENOMEM;
3646
Takashi Iwai620e2b22011-06-17 17:19:19 +02003647 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003648 override_mic_boost(codec, 0x2b, 0, 3, 40);
3649 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003650 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003651
Lydia Wang25eaba22009-10-10 19:08:43 +08003652 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003653 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003654 if (err < 0) {
3655 via_free(codec);
3656 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003657 }
3658
Lydia Wang118909562011-03-23 17:57:34 +08003659 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003660 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003661 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003662 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003663
Lydia Wang25eaba22009-10-10 19:08:43 +08003664 codec->patch_ops = via_patch_ops;
3665
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003666 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003667 return 0;
3668}
Lydia Wangab6734e2009-10-10 19:08:46 +08003669
3670/* for vt1812 */
3671
Takashi Iwai096a8852011-06-20 12:09:02 +02003672static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003673 /* Enable Boost Volume backdoor */
3674 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003675 /* Enable AOW0 to MW9 */
3676 {0x1, 0xfb8, 0xa8},
3677 { }
3678};
3679
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003680static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3681{
3682 struct via_spec *spec = codec->spec;
3683 int imux_is_smixer =
3684 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3685 unsigned int parm;
3686 unsigned int present;
3687 /* MUX10 (1eh) = stereo mixer */
3688 imux_is_smixer =
3689 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3690 /* inputs */
3691 /* PW 5/6/7 (29h/2ah/2bh) */
3692 parm = AC_PWRST_D3;
3693 set_pin_power_state(codec, 0x29, &parm);
3694 set_pin_power_state(codec, 0x2a, &parm);
3695 set_pin_power_state(codec, 0x2b, &parm);
3696 parm = AC_PWRST_D0;
3697 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3698 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3699 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3700 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3701 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3702
3703 /* outputs */
3704 /* AOW0 (8h)*/
3705 snd_hda_codec_write(codec, 0x8, 0,
3706 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3707
3708 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3709 parm = AC_PWRST_D3;
3710 set_pin_power_state(codec, 0x28, &parm);
3711 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3712 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3713
3714 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3715 parm = AC_PWRST_D3;
3716 set_pin_power_state(codec, 0x25, &parm);
3717 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3718 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3719 if (spec->hp_independent_mode)
3720 snd_hda_codec_write(codec, 0x9, 0,
3721 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3722
3723 /* Internal Speaker */
3724 /* PW0 (24h), MW0(14h), MUX0(34h) */
3725 present = snd_hda_jack_detect(codec, 0x25);
3726
3727 parm = AC_PWRST_D3;
3728 set_pin_power_state(codec, 0x24, &parm);
3729 if (present) {
3730 snd_hda_codec_write(codec, 0x14, 0,
3731 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3732 snd_hda_codec_write(codec, 0x34, 0,
3733 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3734 } else {
3735 snd_hda_codec_write(codec, 0x14, 0,
3736 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3737 snd_hda_codec_write(codec, 0x34, 0,
3738 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3739 }
3740
3741
3742 /* Mono Out */
3743 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3744 present = snd_hda_jack_detect(codec, 0x28);
3745
3746 parm = AC_PWRST_D3;
3747 set_pin_power_state(codec, 0x31, &parm);
3748 if (present) {
3749 snd_hda_codec_write(codec, 0x1c, 0,
3750 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3751 snd_hda_codec_write(codec, 0x3c, 0,
3752 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3753 snd_hda_codec_write(codec, 0x3e, 0,
3754 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3755 } else {
3756 snd_hda_codec_write(codec, 0x1c, 0,
3757 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3758 snd_hda_codec_write(codec, 0x3c, 0,
3759 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3760 snd_hda_codec_write(codec, 0x3e, 0,
3761 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3762 }
3763
3764 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3765 parm = AC_PWRST_D3;
3766 set_pin_power_state(codec, 0x33, &parm);
3767 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3768 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3769
3770}
Lydia Wangab6734e2009-10-10 19:08:46 +08003771
3772/* patch for vt1812 */
3773static int patch_vt1812(struct hda_codec *codec)
3774{
3775 struct via_spec *spec;
3776 int err;
3777
3778 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003779 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003780 if (spec == NULL)
3781 return -ENOMEM;
3782
Takashi Iwai620e2b22011-06-17 17:19:19 +02003783 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003784 override_mic_boost(codec, 0x2b, 0, 3, 40);
3785 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003786 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003787
Lydia Wangab6734e2009-10-10 19:08:46 +08003788 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003789 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003790 if (err < 0) {
3791 via_free(codec);
3792 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003793 }
3794
Takashi Iwai096a8852011-06-20 12:09:02 +02003795 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003796
Lydia Wangab6734e2009-10-10 19:08:46 +08003797 codec->patch_ops = via_patch_ops;
3798
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003799 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003800 return 0;
3801}
3802
Joseph Chanc577b8a2006-11-29 15:29:40 +01003803/*
3804 * patch entries
3805 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003806static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003807 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3808 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3809 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3810 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3811 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003812 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003813 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003814 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003815 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003816 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003817 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003818 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003819 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003820 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003821 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003822 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003823 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003824 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003825 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003826 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003827 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003828 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003829 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003830 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003831 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003832 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003833 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003834 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003835 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003836 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003837 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003838 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003839 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003840 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003841 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003842 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003843 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003844 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003845 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003846 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003847 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003848 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003849 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003850 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003851 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003852 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003853 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003854 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003855 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003856 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003857 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003858 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003859 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003860 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003861 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003862 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003863 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003864 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003865 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003866 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003867 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003868 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003869 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003870 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003871 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003872 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003873 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003874 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003875 { .id = 0x11060428, .name = "VT1718S",
3876 .patch = patch_vt1718S},
3877 { .id = 0x11064428, .name = "VT1718S",
3878 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003879 { .id = 0x11060441, .name = "VT2020",
3880 .patch = patch_vt1718S},
3881 { .id = 0x11064441, .name = "VT1828S",
3882 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003883 { .id = 0x11060433, .name = "VT1716S",
3884 .patch = patch_vt1716S},
3885 { .id = 0x1106a721, .name = "VT1716S",
3886 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003887 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3888 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003889 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003890 { .id = 0x11060440, .name = "VT1818S",
3891 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003892 { .id = 0x11060446, .name = "VT1802",
3893 .patch = patch_vt2002P},
3894 { .id = 0x11068446, .name = "VT1802",
3895 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003896 {} /* terminator */
3897};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003898
3899MODULE_ALIAS("snd-hda-codec-id:1106*");
3900
3901static struct hda_codec_preset_list via_list = {
3902 .preset = snd_hda_preset_via,
3903 .owner = THIS_MODULE,
3904};
3905
3906MODULE_LICENSE("GPL");
3907MODULE_DESCRIPTION("VIA HD-audio codec");
3908
3909static int __init patch_via_init(void)
3910{
3911 return snd_hda_add_codec_preset(&via_list);
3912}
3913
3914static void __exit patch_via_exit(void)
3915{
3916 snd_hda_delete_codec_preset(&via_list);
3917}
3918
3919module_init(patch_via_init)
3920module_exit(patch_via_exit)