blob: 3467d0c23fe94395b0deb3b2382b0ae3cd8568a7 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Paul Gortmakerda155d52011-07-15 12:38:28 -040052#include <linux/module.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010053#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080054#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010055#include "hda_codec.h"
56#include "hda_local.h"
Takashi Iwai1835a0f2011-10-27 22:12:46 +020057#include "hda_jack.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010058
Joseph Chanc577b8a2006-11-29 15:29:40 +010059/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080060#define VT1708_HP_PIN_NID 0x20
61#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010062
Harald Welted7426322008-09-15 22:43:23 +080063enum VIA_HDA_CODEC {
64 UNKNOWN = -1,
65 VT1708,
66 VT1709_10CH,
67 VT1709_6CH,
68 VT1708B_8CH,
69 VT1708B_4CH,
70 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080071 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080072 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080073 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080074 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080075 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080076 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080077 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080078 CODEC_TYPES,
79};
80
Lydia Wang118909562011-03-23 17:57:34 +080081#define VT2002P_COMPATIBLE(spec) \
82 ((spec)->codec_type == VT2002P ||\
83 (spec)->codec_type == VT1812 ||\
84 (spec)->codec_type == VT1802)
85
Takashi Iwai8e3679d2011-06-21 09:01:36 +020086#define MAX_NID_PATH_DEPTH 5
87
Takashi Iwai09a9ad62011-06-21 15:57:44 +020088/* output-path: DAC -> ... -> pin
89 * idx[] contains the source index number of the next widget;
90 * e.g. idx[0] is the index of the DAC selected by path[1] widget
91 * multi[] indicates whether it's a selector widget with multi-connectors
92 * (i.e. the connection selection is mandatory)
93 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
94 */
Takashi Iwai4a796162011-06-17 17:53:38 +020095struct nid_path {
96 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020097 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad62011-06-21 15:57:44 +020098 unsigned char idx[MAX_NID_PATH_DEPTH];
99 unsigned char multi[MAX_NID_PATH_DEPTH];
100 unsigned int vol_ctl;
101 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200102};
103
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200104/* input-path */
105struct via_input {
106 hda_nid_t pin; /* input-pin or aa-mix */
107 int adc_idx; /* ADC index to be used */
108 int mux_idx; /* MUX index (if any) */
109 const char *label; /* input-source label */
110};
111
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200112#define VIA_MAX_ADCS 3
113
Takashi Iwai3b607e32011-07-18 16:54:40 +0200114enum {
115 STREAM_MULTI_OUT = (1 << 0),
116 STREAM_INDEP_HP = (1 << 1),
117};
118
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800119struct via_spec {
120 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200121 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800122 unsigned int num_mixers;
123
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200124 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800125 unsigned int num_iverbs;
126
Takashi Iwai82673bc2011-06-17 16:24:21 +0200127 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200128 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200129 const struct hda_pcm_stream *stream_analog_playback;
130 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800131
Takashi Iwai82673bc2011-06-17 16:24:21 +0200132 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200133 const struct hda_pcm_stream *stream_digital_playback;
134 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800135
136 /* playback */
137 struct hda_multi_out multiout;
138 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200139 hda_nid_t hp_dac_nid;
Takashi Iwai3214b962011-07-18 12:49:25 +0200140 hda_nid_t speaker_dac_nid;
141 int hp_indep_shared; /* indep HP-DAC is shared with side ch */
Takashi Iwai3b607e32011-07-18 16:54:40 +0200142 int opened_streams; /* STREAM_* bits */
143 int active_streams; /* STREAM_* bits */
Takashi Iwai3214b962011-07-18 12:49:25 +0200144 int aamix_mode; /* loopback is enabled for output-path? */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800145
Takashi Iwai3214b962011-07-18 12:49:25 +0200146 /* Output-paths:
147 * There are different output-paths depending on the setup.
148 * out_path, hp_path and speaker_path are primary paths. If both
149 * direct DAC and aa-loopback routes are available, these contain
150 * the former paths. Meanwhile *_mix_path contain the paths with
151 * loopback mixer. (Since the loopback is only for front channel,
152 * no out_mix_path for surround channels.)
153 * The HP output has another path, hp_indep_path, which is used in
154 * the independent-HP mode.
155 */
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200156 struct nid_path out_path[HDA_SIDE + 1];
Takashi Iwai3214b962011-07-18 12:49:25 +0200157 struct nid_path out_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200158 struct nid_path hp_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200159 struct nid_path hp_mix_path;
160 struct nid_path hp_indep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200161 struct nid_path speaker_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200162 struct nid_path speaker_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200163
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800164 /* capture */
165 unsigned int num_adc_nids;
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200166 hda_nid_t adc_nids[VIA_MAX_ADCS];
167 hda_nid_t mux_nids[VIA_MAX_ADCS];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200168 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800169 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800170
171 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200172 bool dyn_adc_switch;
173 int num_inputs;
174 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200175 unsigned int cur_mux[VIA_MAX_ADCS];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800176
Takashi Iwai3b607e32011-07-18 16:54:40 +0200177 /* dynamic DAC switching */
178 unsigned int cur_dac_stream_tag;
179 unsigned int cur_dac_format;
180 unsigned int cur_hp_stream_tag;
181 unsigned int cur_hp_format;
182
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200183 /* dynamic ADC switching */
184 hda_nid_t cur_adc;
185 unsigned int cur_adc_stream_tag;
186 unsigned int cur_adc_format;
187
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800188 /* PCM information */
189 struct hda_pcm pcm_rec[3];
190
191 /* dynamic controls, init_verbs and input_mux */
192 struct auto_pin_cfg autocfg;
193 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800194 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
195
196 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800197 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800198 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200199 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800200 enum VIA_HDA_CODEC codec_type;
201
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200202 /* smart51 setup */
203 unsigned int smart51_nums;
204 hda_nid_t smart51_pins[2];
205 int smart51_idxs[2];
206 const char *smart51_labels[2];
207 unsigned int smart51_enabled;
208
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800209 /* work to check hp jack state */
210 struct hda_codec *codec;
211 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200212 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800213 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800214
215 void (*set_widgets_power_state)(struct hda_codec *codec);
216
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800217 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200218 int num_loopbacks;
219 struct hda_amp_list loopback_list[8];
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200220
221 /* bind capture-volume */
222 struct hda_bind_ctls *bind_cap_vol;
223 struct hda_bind_ctls *bind_cap_sw;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200224
225 struct mutex config_mutex;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800226};
227
Lydia Wang0341ccd2011-03-22 16:25:03 +0800228static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100229static struct via_spec * via_new_spec(struct hda_codec *codec)
230{
231 struct via_spec *spec;
232
233 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
234 if (spec == NULL)
235 return NULL;
236
Takashi Iwai3b607e32011-07-18 16:54:40 +0200237 mutex_init(&spec->config_mutex);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100238 codec->spec = spec;
239 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800240 spec->codec_type = get_codec_type(codec);
241 /* VT1708BCE & VT1708S are almost same */
242 if (spec->codec_type == VT1708BCE)
243 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100244 return spec;
245}
246
Lydia Wang744ff5f2009-10-10 19:07:26 +0800247static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800248{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800249 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800250 u16 ven_id = vendor_id >> 16;
251 u16 dev_id = vendor_id & 0xffff;
252 enum VIA_HDA_CODEC codec_type;
253
254 /* get codec type */
255 if (ven_id != 0x1106)
256 codec_type = UNKNOWN;
257 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
258 codec_type = VT1708;
259 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
260 codec_type = VT1709_10CH;
261 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
262 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800263 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800264 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800265 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
266 codec_type = VT1708BCE;
267 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800268 codec_type = VT1708B_4CH;
269 else if ((dev_id & 0xfff) == 0x397
270 && (dev_id >> 12) < 8)
271 codec_type = VT1708S;
272 else if ((dev_id & 0xfff) == 0x398
273 && (dev_id >> 12) < 8)
274 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800275 else if ((dev_id & 0xfff) == 0x428
276 && (dev_id >> 12) < 8)
277 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800278 else if (dev_id == 0x0433 || dev_id == 0xa721)
279 codec_type = VT1716S;
Lydia Wangbb3c6bf2009-10-10 19:08:39 +0800280 else if (dev_id == 0x0441 || dev_id == 0x4441)
281 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800282 else if (dev_id == 0x0438 || dev_id == 0x4438)
283 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800284 else if (dev_id == 0x0448)
285 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800286 else if (dev_id == 0x0440)
287 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800288 else if ((dev_id & 0xfff) == 0x446)
289 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800290 else
291 codec_type = UNKNOWN;
292 return codec_type;
293};
294
Lydia Wangec7e7e42011-03-24 12:43:44 +0800295#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800296#define VIA_HP_EVENT 0x01
297#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200298#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800299
Joseph Chanc577b8a2006-11-29 15:29:40 +0100300enum {
301 VIA_CTL_WIDGET_VOL,
302 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800303 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100304};
305
Takashi Iwaiada509e2011-06-20 15:40:19 +0200306static void analog_low_current_mode(struct hda_codec *codec);
307static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800308
309static void vt1708_start_hp_work(struct via_spec *spec)
310{
311 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
312 return;
313 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200314 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800315 if (!delayed_work_pending(&spec->vt1708_hp_work))
316 schedule_delayed_work(&spec->vt1708_hp_work,
317 msecs_to_jiffies(100));
318}
319
320static void vt1708_stop_hp_work(struct via_spec *spec)
321{
322 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
323 return;
324 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
325 && !is_aa_path_mute(spec->codec))
326 return;
327 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200328 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100329 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800330}
Lydia Wangf5271102009-10-10 19:07:35 +0800331
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800332static void set_widgets_power_state(struct hda_codec *codec)
333{
334 struct via_spec *spec = codec->spec;
335 if (spec->set_widgets_power_state)
336 spec->set_widgets_power_state(codec);
337}
Lydia Wang25eaba22009-10-10 19:08:43 +0800338
Lydia Wangf5271102009-10-10 19:07:35 +0800339static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
340 struct snd_ctl_elem_value *ucontrol)
341{
342 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
343 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
344
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800345 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200346 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800347 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
348 if (is_aa_path_mute(codec))
349 vt1708_start_hp_work(codec->spec);
350 else
351 vt1708_stop_hp_work(codec->spec);
352 }
Lydia Wangf5271102009-10-10 19:07:35 +0800353 return change;
354}
355
356/* modify .put = snd_hda_mixer_amp_switch_put */
357#define ANALOG_INPUT_MUTE \
358 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
359 .name = NULL, \
360 .index = 0, \
361 .info = snd_hda_mixer_amp_switch_info, \
362 .get = snd_hda_mixer_amp_switch_get, \
363 .put = analog_input_switch_put, \
364 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
365
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200366static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100367 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
368 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800369 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100370};
371
Lydia Wangab6734e2009-10-10 19:08:46 +0800372
Joseph Chanc577b8a2006-11-29 15:29:40 +0100373/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200374static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
375 const struct snd_kcontrol_new *tmpl,
376 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100377{
378 struct snd_kcontrol_new *knew;
379
Takashi Iwai603c4012008-07-30 15:01:44 +0200380 snd_array_init(&spec->kctls, sizeof(*knew), 32);
381 knew = snd_array_new(&spec->kctls);
382 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200383 return NULL;
384 *knew = *tmpl;
385 if (!name)
386 name = tmpl->name;
387 if (name) {
388 knew->name = kstrdup(name, GFP_KERNEL);
389 if (!knew->name)
390 return NULL;
391 }
392 return knew;
393}
394
395static int __via_add_control(struct via_spec *spec, int type, const char *name,
396 int idx, unsigned long val)
397{
398 struct snd_kcontrol_new *knew;
399
400 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
401 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100402 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200403 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100404 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100405 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100406 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100407 return 0;
408}
409
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200410#define via_add_control(spec, type, name, val) \
411 __via_add_control(spec, type, name, 0, val)
412
Takashi Iwai291c9e32011-06-17 16:15:26 +0200413#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100414
Takashi Iwai603c4012008-07-30 15:01:44 +0200415static void via_free_kctls(struct hda_codec *codec)
416{
417 struct via_spec *spec = codec->spec;
418
419 if (spec->kctls.list) {
420 struct snd_kcontrol_new *kctl = spec->kctls.list;
421 int i;
422 for (i = 0; i < spec->kctls.used; i++)
423 kfree(kctl[i].name);
424 }
425 snd_array_free(&spec->kctls);
426}
427
Joseph Chanc577b8a2006-11-29 15:29:40 +0100428/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800429static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200430 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100431{
432 char name[32];
433 int err;
434
435 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200436 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100437 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
438 if (err < 0)
439 return err;
440 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200441 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100442 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
443 if (err < 0)
444 return err;
445 return 0;
446}
447
Takashi Iwai5d417622011-06-20 11:32:27 +0200448#define get_connection_index(codec, mux, nid) \
Takashi Iwai8d087c72011-06-28 12:45:47 +0200449 snd_hda_get_conn_index(codec, mux, nid, 0)
Takashi Iwai5d417622011-06-20 11:32:27 +0200450
Takashi Iwai8df2a312011-06-21 11:48:29 +0200451static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
452 unsigned int mask)
453{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200454 unsigned int caps;
455 if (!nid)
456 return false;
457 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200458 if (dir == HDA_INPUT)
459 caps &= AC_WCAP_IN_AMP;
460 else
461 caps &= AC_WCAP_OUT_AMP;
462 if (!caps)
463 return false;
464 if (query_amp_caps(codec, nid, dir) & mask)
465 return true;
466 return false;
467}
468
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200469#define have_mute(codec, nid, dir) \
470 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200471
Lydia Wangd69607b2011-07-08 14:02:52 +0800472/* enable/disable the output-route mixers */
473static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
Takashi Iwai3214b962011-07-18 12:49:25 +0200474 hda_nid_t mix_nid, int idx, bool enable)
Lydia Wangd69607b2011-07-08 14:02:52 +0800475{
476 int i, num, val;
Lydia Wangd69607b2011-07-08 14:02:52 +0800477
478 if (!path)
479 return;
480 num = snd_hda_get_conn_list(codec, mix_nid, NULL);
Lydia Wangd69607b2011-07-08 14:02:52 +0800481 for (i = 0; i < num; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +0200482 if (i == idx)
483 val = AMP_IN_UNMUTE(i);
484 else
485 val = AMP_IN_MUTE(i);
Lydia Wangd69607b2011-07-08 14:02:52 +0800486 snd_hda_codec_write(codec, mix_nid, 0,
487 AC_VERB_SET_AMP_GAIN_MUTE, val);
488 }
489}
490
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200491/* enable/disable the output-route */
492static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
493 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200494{
Lydia Wangd69607b2011-07-08 14:02:52 +0800495 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200496 int i;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200497 for (i = 0; i < path->depth; i++) {
498 hda_nid_t src, dst;
499 int idx = path->idx[i];
500 src = path->path[i];
501 if (i < path->depth - 1)
502 dst = path->path[i + 1];
503 else
504 dst = 0;
505 if (enable && path->multi[i])
506 snd_hda_codec_write(codec, dst, 0,
507 AC_VERB_SET_CONNECT_SEL, idx);
Takashi Iwai3214b962011-07-18 12:49:25 +0200508 if (!force && (dst == spec->aa_mix_nid))
Lydia Wange5e14682011-07-01 10:55:07 +0800509 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +0200510 if (have_mute(codec, dst, HDA_INPUT))
511 activate_output_mix(codec, path, dst, idx, enable);
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200512 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
513 continue;
514 if (have_mute(codec, src, HDA_OUTPUT)) {
515 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
516 snd_hda_codec_write(codec, src, 0,
517 AC_VERB_SET_AMP_GAIN_MUTE, val);
518 }
519 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200520}
521
522/* set the given pin as output */
523static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
524 int pin_type)
525{
526 if (!pin)
527 return;
528 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
529 pin_type);
530 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
531 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200532 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100533}
534
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200535static void via_auto_init_output(struct hda_codec *codec,
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200536 struct nid_path *path, int pin_type)
Takashi Iwai5d417622011-06-20 11:32:27 +0200537{
Takashi Iwai5d417622011-06-20 11:32:27 +0200538 unsigned int caps;
Lydia Wangd69607b2011-07-08 14:02:52 +0800539 hda_nid_t pin;
Takashi Iwai5d417622011-06-20 11:32:27 +0200540
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200541 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200542 return;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200543 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200544
545 init_output_pin(codec, pin, pin_type);
546 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
547 if (caps & AC_AMPCAP_MUTE) {
548 unsigned int val;
549 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
550 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
551 AMP_OUT_MUTE | val);
552 }
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200553 activate_output_path(codec, path, true, true); /* force on */
Takashi Iwai5d417622011-06-20 11:32:27 +0200554}
555
Joseph Chanc577b8a2006-11-29 15:29:40 +0100556static void via_auto_init_multi_out(struct hda_codec *codec)
557{
558 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200559 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100560 int i;
561
Takashi Iwai3214b962011-07-18 12:49:25 +0200562 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) {
563 path = &spec->out_path[i];
564 if (!i && spec->aamix_mode && spec->out_mix_path.depth)
565 path = &spec->out_mix_path;
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200566 via_auto_init_output(codec, path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200567 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100568}
569
Takashi Iwai020066d2011-07-21 13:45:56 +0200570/* deactivate the inactive headphone-paths */
571static void deactivate_hp_paths(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100572{
573 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200574 int shared = spec->hp_indep_shared;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100575
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200576 if (spec->hp_independent_mode) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200577 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200578 activate_output_path(codec, &spec->hp_mix_path, false, false);
579 if (shared)
580 activate_output_path(codec, &spec->out_path[shared],
581 false, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200582 } else if (spec->aamix_mode || !spec->hp_path.depth) {
583 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200584 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200585 } else {
Takashi Iwai020066d2011-07-21 13:45:56 +0200586 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200587 activate_output_path(codec, &spec->hp_mix_path, false, false);
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200588 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100589}
590
Takashi Iwai020066d2011-07-21 13:45:56 +0200591static void via_auto_init_hp_out(struct hda_codec *codec)
592{
593 struct via_spec *spec = codec->spec;
594
595 if (!spec->hp_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200596 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200597 return;
598 }
599 deactivate_hp_paths(codec);
600 if (spec->hp_independent_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200601 via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200602 else if (spec->aamix_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200603 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200604 else
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200605 via_auto_init_output(codec, &spec->hp_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200606}
607
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200608static void via_auto_init_speaker_out(struct hda_codec *codec)
609{
610 struct via_spec *spec = codec->spec;
611
Takashi Iwai3214b962011-07-18 12:49:25 +0200612 if (!spec->autocfg.speaker_outs)
613 return;
614 if (!spec->speaker_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200615 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200616 return;
617 }
618 if (!spec->aamix_mode) {
619 activate_output_path(codec, &spec->speaker_mix_path,
620 false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200621 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200622 } else {
623 activate_output_path(codec, &spec->speaker_path, false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200624 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200625 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200626}
627
Takashi Iwaif4a78282011-06-17 18:46:48 +0200628static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200629static void via_hp_automute(struct hda_codec *codec);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200630
Joseph Chanc577b8a2006-11-29 15:29:40 +0100631static void via_auto_init_analog_input(struct hda_codec *codec)
632{
633 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200634 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200635 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200636 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200637 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100638
Takashi Iwai096a8852011-06-20 12:09:02 +0200639 /* init ADCs */
640 for (i = 0; i < spec->num_adc_nids; i++) {
641 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
642 AC_VERB_SET_AMP_GAIN_MUTE,
643 AMP_IN_UNMUTE(0));
644 }
645
646 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200647 for (i = 0; i < cfg->num_inputs; i++) {
648 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200649 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200650 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100651 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200652 ctl = PIN_VREF50;
653 else
654 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100655 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200656 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100657 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200658
659 /* init input-src */
660 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200661 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
662 if (spec->mux_nids[adc_idx]) {
663 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
664 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
665 AC_VERB_SET_CONNECT_SEL,
666 mux_idx);
667 }
668 if (spec->dyn_adc_switch)
669 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200670 }
671
672 /* init aa-mixer */
673 if (!spec->aa_mix_nid)
674 return;
675 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
676 ARRAY_SIZE(conn));
677 for (i = 0; i < num_conns; i++) {
678 unsigned int caps = get_wcaps(codec, conn[i]);
679 if (get_wcaps_type(caps) == AC_WID_PIN)
680 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
681 AC_VERB_SET_AMP_GAIN_MUTE,
682 AMP_IN_MUTE(i));
683 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100684}
Lydia Wangf5271102009-10-10 19:07:35 +0800685
686static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
687 unsigned int *affected_parm)
688{
689 unsigned parm;
690 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
691 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
692 >> AC_DEFCFG_MISC_SHIFT
693 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800694 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200695 unsigned present = 0;
696
697 no_presence |= spec->no_pin_power_ctl;
698 if (!no_presence)
699 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200700 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800701 || ((no_presence || present)
702 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800703 *affected_parm = AC_PWRST_D0; /* if it's connected */
704 parm = AC_PWRST_D0;
705 } else
706 parm = AC_PWRST_D3;
707
708 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
709}
710
Takashi Iwai24088a52011-06-17 16:59:21 +0200711static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
712 struct snd_ctl_elem_info *uinfo)
713{
714 static const char * const texts[] = {
715 "Disabled", "Enabled"
716 };
717
718 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
719 uinfo->count = 1;
720 uinfo->value.enumerated.items = 2;
721 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
722 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
723 strcpy(uinfo->value.enumerated.name,
724 texts[uinfo->value.enumerated.item]);
725 return 0;
726}
727
728static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
729 struct snd_ctl_elem_value *ucontrol)
730{
731 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
732 struct via_spec *spec = codec->spec;
733 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
734 return 0;
735}
736
737static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
738 struct snd_ctl_elem_value *ucontrol)
739{
740 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
741 struct via_spec *spec = codec->spec;
742 unsigned int val = !ucontrol->value.enumerated.item[0];
743
744 if (val == spec->no_pin_power_ctl)
745 return 0;
746 spec->no_pin_power_ctl = val;
747 set_widgets_power_state(codec);
748 return 1;
749}
750
751static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
752 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
753 .name = "Dynamic Power-Control",
754 .info = via_pin_power_ctl_info,
755 .get = via_pin_power_ctl_get,
756 .put = via_pin_power_ctl_put,
757};
758
759
Harald Welte0aa62ae2008-09-09 15:58:27 +0800760static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
761 struct snd_ctl_elem_info *uinfo)
762{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200763 static const char * const texts[] = { "OFF", "ON" };
764
765 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
766 uinfo->count = 1;
767 uinfo->value.enumerated.items = 2;
768 if (uinfo->value.enumerated.item >= 2)
769 uinfo->value.enumerated.item = 1;
770 strcpy(uinfo->value.enumerated.name,
771 texts[uinfo->value.enumerated.item]);
772 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800773}
774
775static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
776 struct snd_ctl_elem_value *ucontrol)
777{
778 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800779 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800780
Takashi Iwaiece8d042011-06-19 16:24:21 +0200781 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800782 return 0;
783}
784
Takashi Iwai3b607e32011-07-18 16:54:40 +0200785/* adjust spec->multiout setup according to the current flags */
786static void setup_playback_multi_pcm(struct via_spec *spec)
787{
788 const struct auto_pin_cfg *cfg = &spec->autocfg;
789 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
790 spec->multiout.hp_nid = 0;
791 if (!spec->hp_independent_mode) {
792 if (!spec->hp_indep_shared)
793 spec->multiout.hp_nid = spec->hp_dac_nid;
794 } else {
795 if (spec->hp_indep_shared)
796 spec->multiout.num_dacs = cfg->line_outs - 1;
797 }
798}
799
800/* update DAC setups according to indep-HP switch;
801 * this function is called only when indep-HP is modified
802 */
803static void switch_indep_hp_dacs(struct hda_codec *codec)
804{
805 struct via_spec *spec = codec->spec;
806 int shared = spec->hp_indep_shared;
807 hda_nid_t shared_dac, hp_dac;
808
809 if (!spec->opened_streams)
810 return;
811
812 shared_dac = shared ? spec->multiout.dac_nids[shared] : 0;
813 hp_dac = spec->hp_dac_nid;
814 if (spec->hp_independent_mode) {
815 /* switch to indep-HP mode */
816 if (spec->active_streams & STREAM_MULTI_OUT) {
817 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
818 __snd_hda_codec_cleanup_stream(codec, shared_dac, 1);
819 }
820 if (spec->active_streams & STREAM_INDEP_HP)
821 snd_hda_codec_setup_stream(codec, hp_dac,
822 spec->cur_hp_stream_tag, 0,
823 spec->cur_hp_format);
824 } else {
825 /* back to HP or shared-DAC */
826 if (spec->active_streams & STREAM_INDEP_HP)
827 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
828 if (spec->active_streams & STREAM_MULTI_OUT) {
829 hda_nid_t dac;
830 int ch;
831 if (shared_dac) { /* reset mutli-ch DAC */
832 dac = shared_dac;
833 ch = shared * 2;
834 } else { /* reset HP DAC */
835 dac = hp_dac;
836 ch = 0;
837 }
838 snd_hda_codec_setup_stream(codec, dac,
839 spec->cur_dac_stream_tag, ch,
840 spec->cur_dac_format);
841 }
842 }
843 setup_playback_multi_pcm(spec);
844}
845
Harald Welte0aa62ae2008-09-09 15:58:27 +0800846static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
847 struct snd_ctl_elem_value *ucontrol)
848{
849 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
850 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200851 int cur, shared;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200852
Takashi Iwai3b607e32011-07-18 16:54:40 +0200853 mutex_lock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200854 cur = !!ucontrol->value.enumerated.item[0];
Takashi Iwai3b607e32011-07-18 16:54:40 +0200855 if (spec->hp_independent_mode == cur) {
856 mutex_unlock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200857 return 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200858 }
Takashi Iwai25250502011-06-30 17:24:47 +0200859 spec->hp_independent_mode = cur;
Takashi Iwai3214b962011-07-18 12:49:25 +0200860 shared = spec->hp_indep_shared;
Takashi Iwai020066d2011-07-21 13:45:56 +0200861 deactivate_hp_paths(codec);
862 if (cur)
863 activate_output_path(codec, &spec->hp_indep_path, true, false);
864 else {
Takashi Iwai3214b962011-07-18 12:49:25 +0200865 if (shared)
866 activate_output_path(codec, &spec->out_path[shared],
Takashi Iwai25250502011-06-30 17:24:47 +0200867 true, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200868 if (spec->aamix_mode || !spec->hp_path.depth)
869 activate_output_path(codec, &spec->hp_mix_path,
870 true, false);
871 else
872 activate_output_path(codec, &spec->hp_path,
873 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200874 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800875
Takashi Iwai3b607e32011-07-18 16:54:40 +0200876 switch_indep_hp_dacs(codec);
877 mutex_unlock(&spec->config_mutex);
878
Lydia Wangce0e5a92011-03-22 16:22:37 +0800879 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800880 set_widgets_power_state(codec);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200881 via_hp_automute(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200882 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800883}
884
Takashi Iwaiece8d042011-06-19 16:24:21 +0200885static const struct snd_kcontrol_new via_hp_mixer = {
886 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
887 .name = "Independent HP",
888 .info = via_independent_hp_info,
889 .get = via_independent_hp_get,
890 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800891};
892
Takashi Iwai3d83e572010-04-14 14:36:23 +0200893static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100894{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200895 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100896 struct snd_kcontrol_new *knew;
897 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100898
Takashi Iwaiece8d042011-06-19 16:24:21 +0200899 nid = spec->autocfg.hp_pins[0];
900 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200901 if (knew == NULL)
902 return -ENOMEM;
903
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100904 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100905
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100906 return 0;
907}
908
Lydia Wang1564b282009-10-10 19:07:52 +0800909static void notify_aa_path_ctls(struct hda_codec *codec)
910{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200911 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800912 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800913
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200914 for (i = 0; i < spec->smart51_nums; i++) {
915 struct snd_kcontrol *ctl;
916 struct snd_ctl_elem_id id;
917 memset(&id, 0, sizeof(id));
918 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
919 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800920 ctl = snd_hda_find_mixer_ctl(codec, id.name);
921 if (ctl)
922 snd_ctl_notify(codec->bus->card,
923 SNDRV_CTL_EVENT_MASK_VALUE,
924 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800925 }
926}
927
928static void mute_aa_path(struct hda_codec *codec, int mute)
929{
930 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200931 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800932 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200933
Lydia Wang1564b282009-10-10 19:07:52 +0800934 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200935 for (i = 0; i < spec->smart51_nums; i++) {
936 if (spec->smart51_idxs[i] < 0)
937 continue;
938 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
939 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800940 HDA_AMP_MUTE, val);
941 }
942}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200943
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200944static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
945{
946 struct via_spec *spec = codec->spec;
947 int i;
948
949 for (i = 0; i < spec->smart51_nums; i++)
950 if (spec->smart51_pins[i] == pin)
951 return true;
952 return false;
953}
954
Lydia Wang1564b282009-10-10 19:07:52 +0800955static int via_smart51_get(struct snd_kcontrol *kcontrol,
956 struct snd_ctl_elem_value *ucontrol)
957{
958 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
959 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800960
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200961 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800962 return 0;
963}
964
965static int via_smart51_put(struct snd_kcontrol *kcontrol,
966 struct snd_ctl_elem_value *ucontrol)
967{
968 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
969 struct via_spec *spec = codec->spec;
970 int out_in = *ucontrol->value.integer.value
971 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800972 int i;
973
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200974 for (i = 0; i < spec->smart51_nums; i++) {
975 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200976 unsigned int parm;
977
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200978 parm = snd_hda_codec_read(codec, nid, 0,
979 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
980 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
981 parm |= out_in;
982 snd_hda_codec_write(codec, nid, 0,
983 AC_VERB_SET_PIN_WIDGET_CONTROL,
984 parm);
985 if (out_in == AC_PINCTL_OUT_EN) {
986 mute_aa_path(codec, 1);
987 notify_aa_path_ctls(codec);
988 }
Lydia Wang1564b282009-10-10 19:07:52 +0800989 }
990 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800991 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800992 return 1;
993}
994
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200995static const struct snd_kcontrol_new via_smart51_mixer = {
996 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
997 .name = "Smart 5.1",
998 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200999 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +02001000 .get = via_smart51_get,
1001 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +08001002};
1003
Takashi Iwaif4a78282011-06-17 18:46:48 +02001004static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001005{
Takashi Iwaif4a78282011-06-17 18:46:48 +02001006 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001007
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001008 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +08001009 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001010 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001011 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001012 return 0;
1013}
1014
Takashi Iwaiada509e2011-06-20 15:40:19 +02001015/* check AA path's mute status */
1016static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001017{
Lydia Wangf5271102009-10-10 19:07:35 +08001018 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001019 const struct hda_amp_list *p;
1020 int i, ch, v;
1021
1022 for (i = 0; i < spec->num_loopbacks; i++) {
1023 p = &spec->loopback_list[i];
1024 for (ch = 0; ch < 2; ch++) {
1025 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
1026 p->idx);
1027 if (!(v & HDA_AMP_MUTE) && v > 0)
1028 return false;
Lydia Wangf5271102009-10-10 19:07:35 +08001029 }
1030 }
Takashi Iwaiada509e2011-06-20 15:40:19 +02001031 return true;
Lydia Wangf5271102009-10-10 19:07:35 +08001032}
1033
1034/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +02001035static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001036{
1037 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001038 bool enable;
1039 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +08001040
Takashi Iwai3b607e32011-07-18 16:54:40 +02001041 enable = is_aa_path_mute(codec) && (spec->opened_streams != 0);
Lydia Wangf5271102009-10-10 19:07:35 +08001042
1043 /* decide low current mode's verb & parameter */
1044 switch (spec->codec_type) {
1045 case VT1708B_8CH:
1046 case VT1708B_4CH:
1047 verb = 0xf70;
1048 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1049 break;
1050 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001051 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001052 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001053 verb = 0xf73;
1054 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1055 break;
1056 case VT1702:
1057 verb = 0xf73;
1058 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1059 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001060 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001061 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +08001062 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +08001063 verb = 0xf93;
1064 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1065 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001066 default:
1067 return; /* other codecs are not supported */
1068 }
1069 /* send verb */
1070 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1071}
1072
Joseph Chanc577b8a2006-11-29 15:29:40 +01001073/*
1074 * generic initialization of ADC, input mixers and output mixers
1075 */
Takashi Iwai096a8852011-06-20 12:09:02 +02001076static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001077 /* power down jack detect function */
1078 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001079 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001080};
1081
Takashi Iwai3b607e32011-07-18 16:54:40 +02001082static void set_stream_open(struct hda_codec *codec, int bit, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001083{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001084 struct via_spec *spec = codec->spec;
1085
1086 if (active)
Takashi Iwai3b607e32011-07-18 16:54:40 +02001087 spec->opened_streams |= bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001088 else
Takashi Iwai3b607e32011-07-18 16:54:40 +02001089 spec->opened_streams &= ~bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001090 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001091}
1092
Takashi Iwaiece8d042011-06-19 16:24:21 +02001093static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001094 struct hda_codec *codec,
1095 struct snd_pcm_substream *substream)
1096{
1097 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001098 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001099 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001100
Takashi Iwai25250502011-06-30 17:24:47 +02001101 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
Takashi Iwai25250502011-06-30 17:24:47 +02001102 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001103 set_stream_open(codec, STREAM_MULTI_OUT, true);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001104 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1105 hinfo);
1106 if (err < 0) {
Takashi Iwai3b607e32011-07-18 16:54:40 +02001107 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001108 return err;
1109 }
1110 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001111}
1112
Takashi Iwaiece8d042011-06-19 16:24:21 +02001113static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001114 struct hda_codec *codec,
1115 struct snd_pcm_substream *substream)
1116{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001117 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001118 return 0;
1119}
1120
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001121static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1122 struct hda_codec *codec,
1123 struct snd_pcm_substream *substream)
1124{
1125 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001126
Takashi Iwaiece8d042011-06-19 16:24:21 +02001127 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001128 return -EINVAL;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001129 set_stream_open(codec, STREAM_INDEP_HP, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001130 return 0;
1131}
1132
1133static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1134 struct hda_codec *codec,
1135 struct snd_pcm_substream *substream)
1136{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001137 set_stream_open(codec, STREAM_INDEP_HP, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001138 return 0;
1139}
1140
1141static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1142 struct hda_codec *codec,
1143 unsigned int stream_tag,
1144 unsigned int format,
1145 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001146{
1147 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001148
Takashi Iwai3b607e32011-07-18 16:54:40 +02001149 mutex_lock(&spec->config_mutex);
1150 setup_playback_multi_pcm(spec);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001151 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1152 format, substream);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001153 /* remember for dynamic DAC switch with indep-HP */
1154 spec->active_streams |= STREAM_MULTI_OUT;
1155 spec->cur_dac_stream_tag = stream_tag;
1156 spec->cur_dac_format = format;
1157 mutex_unlock(&spec->config_mutex);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001158 vt1708_start_hp_work(spec);
1159 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001160}
1161
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001162static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1163 struct hda_codec *codec,
1164 unsigned int stream_tag,
1165 unsigned int format,
1166 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001167{
1168 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001169
Takashi Iwai3b607e32011-07-18 16:54:40 +02001170 mutex_lock(&spec->config_mutex);
1171 if (spec->hp_independent_mode)
1172 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1173 stream_tag, 0, format);
1174 spec->active_streams |= STREAM_INDEP_HP;
1175 spec->cur_hp_stream_tag = stream_tag;
1176 spec->cur_hp_format = format;
1177 mutex_unlock(&spec->config_mutex);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001178 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001179 return 0;
1180}
1181
1182static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1183 struct hda_codec *codec,
1184 struct snd_pcm_substream *substream)
1185{
1186 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001187
Takashi Iwai3b607e32011-07-18 16:54:40 +02001188 mutex_lock(&spec->config_mutex);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001189 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001190 spec->active_streams &= ~STREAM_MULTI_OUT;
1191 mutex_unlock(&spec->config_mutex);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001192 vt1708_stop_hp_work(spec);
1193 return 0;
1194}
1195
1196static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1197 struct hda_codec *codec,
1198 struct snd_pcm_substream *substream)
1199{
1200 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001201
Takashi Iwai3b607e32011-07-18 16:54:40 +02001202 mutex_lock(&spec->config_mutex);
1203 if (spec->hp_independent_mode)
1204 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
1205 spec->active_streams &= ~STREAM_INDEP_HP;
1206 mutex_unlock(&spec->config_mutex);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001207 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001208 return 0;
1209}
1210
Joseph Chanc577b8a2006-11-29 15:29:40 +01001211/*
1212 * Digital out
1213 */
1214static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1215 struct hda_codec *codec,
1216 struct snd_pcm_substream *substream)
1217{
1218 struct via_spec *spec = codec->spec;
1219 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1220}
1221
1222static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1223 struct hda_codec *codec,
1224 struct snd_pcm_substream *substream)
1225{
1226 struct via_spec *spec = codec->spec;
1227 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1228}
1229
Harald Welte5691ec72008-09-15 22:42:26 +08001230static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001231 struct hda_codec *codec,
1232 unsigned int stream_tag,
1233 unsigned int format,
1234 struct snd_pcm_substream *substream)
1235{
1236 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001237 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1238 stream_tag, format, substream);
1239}
Harald Welte5691ec72008-09-15 22:42:26 +08001240
Takashi Iwai9da29272009-05-07 16:31:14 +02001241static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1242 struct hda_codec *codec,
1243 struct snd_pcm_substream *substream)
1244{
1245 struct via_spec *spec = codec->spec;
1246 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001247 return 0;
1248}
1249
Joseph Chanc577b8a2006-11-29 15:29:40 +01001250/*
1251 * Analog capture
1252 */
1253static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1254 struct hda_codec *codec,
1255 unsigned int stream_tag,
1256 unsigned int format,
1257 struct snd_pcm_substream *substream)
1258{
1259 struct via_spec *spec = codec->spec;
1260
1261 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1262 stream_tag, 0, format);
1263 return 0;
1264}
1265
1266static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1267 struct hda_codec *codec,
1268 struct snd_pcm_substream *substream)
1269{
1270 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001271 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001272 return 0;
1273}
1274
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001275/* analog capture with dynamic ADC switching */
1276static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1277 struct hda_codec *codec,
1278 unsigned int stream_tag,
1279 unsigned int format,
1280 struct snd_pcm_substream *substream)
1281{
1282 struct via_spec *spec = codec->spec;
1283 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1284
Takashi Iwai3b607e32011-07-18 16:54:40 +02001285 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001286 spec->cur_adc = spec->adc_nids[adc_idx];
1287 spec->cur_adc_stream_tag = stream_tag;
1288 spec->cur_adc_format = format;
1289 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001290 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001291 return 0;
1292}
1293
1294static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1295 struct hda_codec *codec,
1296 struct snd_pcm_substream *substream)
1297{
1298 struct via_spec *spec = codec->spec;
1299
Takashi Iwai3b607e32011-07-18 16:54:40 +02001300 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001301 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1302 spec->cur_adc = 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001303 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001304 return 0;
1305}
1306
1307/* re-setup the stream if running; called from input-src put */
1308static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1309{
1310 struct via_spec *spec = codec->spec;
1311 int adc_idx = spec->inputs[cur].adc_idx;
1312 hda_nid_t adc = spec->adc_nids[adc_idx];
Takashi Iwai3b607e32011-07-18 16:54:40 +02001313 bool ret = false;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001314
Takashi Iwai3b607e32011-07-18 16:54:40 +02001315 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001316 if (spec->cur_adc && spec->cur_adc != adc) {
1317 /* stream is running, let's swap the current ADC */
1318 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1319 spec->cur_adc = adc;
1320 snd_hda_codec_setup_stream(codec, adc,
1321 spec->cur_adc_stream_tag, 0,
1322 spec->cur_adc_format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001323 ret = true;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001324 }
Takashi Iwai3b607e32011-07-18 16:54:40 +02001325 mutex_unlock(&spec->config_mutex);
1326 return ret;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001327}
1328
Takashi Iwai9af74212011-06-18 16:17:45 +02001329static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001330 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001331 .channels_min = 2,
1332 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001333 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001334 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001335 .open = via_playback_multi_pcm_open,
1336 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001337 .prepare = via_playback_multi_pcm_prepare,
1338 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001339 },
1340};
1341
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001342static const struct hda_pcm_stream via_pcm_hp_playback = {
1343 .substreams = 1,
1344 .channels_min = 2,
1345 .channels_max = 2,
1346 /* NID is set in via_build_pcms */
1347 .ops = {
1348 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001349 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001350 .prepare = via_playback_hp_pcm_prepare,
1351 .cleanup = via_playback_hp_pcm_cleanup
1352 },
1353};
1354
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001355static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001356 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001357 .channels_min = 2,
1358 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001359 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001360 /* We got noisy outputs on the right channel on VT1708 when
1361 * 24bit samples are used. Until any workaround is found,
1362 * disable the 24bit format, so far.
1363 */
1364 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1365 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001366 .open = via_playback_multi_pcm_open,
1367 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001368 .prepare = via_playback_multi_pcm_prepare,
1369 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001370 },
1371};
1372
Takashi Iwai9af74212011-06-18 16:17:45 +02001373static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001374 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001375 .channels_min = 2,
1376 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001377 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001378 .ops = {
1379 .prepare = via_capture_pcm_prepare,
1380 .cleanup = via_capture_pcm_cleanup
1381 },
1382};
1383
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001384static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1385 .substreams = 1,
1386 .channels_min = 2,
1387 .channels_max = 2,
1388 /* NID is set in via_build_pcms */
1389 .ops = {
1390 .prepare = via_dyn_adc_capture_pcm_prepare,
1391 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1392 },
1393};
1394
Takashi Iwai9af74212011-06-18 16:17:45 +02001395static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001396 .substreams = 1,
1397 .channels_min = 2,
1398 .channels_max = 2,
1399 /* NID is set in via_build_pcms */
1400 .ops = {
1401 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001402 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001403 .prepare = via_dig_playback_pcm_prepare,
1404 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001405 },
1406};
1407
Takashi Iwai9af74212011-06-18 16:17:45 +02001408static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001409 .substreams = 1,
1410 .channels_min = 2,
1411 .channels_max = 2,
1412};
1413
Takashi Iwai370bafb2011-06-20 12:47:45 +02001414/*
1415 * slave controls for virtual master
1416 */
1417static const char * const via_slave_vols[] = {
1418 "Front Playback Volume",
1419 "Surround Playback Volume",
1420 "Center Playback Volume",
1421 "LFE Playback Volume",
1422 "Side Playback Volume",
1423 "Headphone Playback Volume",
1424 "Speaker Playback Volume",
1425 NULL,
1426};
1427
1428static const char * const via_slave_sws[] = {
1429 "Front Playback Switch",
1430 "Surround Playback Switch",
1431 "Center Playback Switch",
1432 "LFE Playback Switch",
1433 "Side Playback Switch",
1434 "Headphone Playback Switch",
1435 "Speaker Playback Switch",
1436 NULL,
1437};
1438
Joseph Chanc577b8a2006-11-29 15:29:40 +01001439static int via_build_controls(struct hda_codec *codec)
1440{
1441 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001442 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001443 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001444
Takashi Iwai24088a52011-06-17 16:59:21 +02001445 if (spec->set_widgets_power_state)
1446 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1447 return -ENOMEM;
1448
Joseph Chanc577b8a2006-11-29 15:29:40 +01001449 for (i = 0; i < spec->num_mixers; i++) {
1450 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1451 if (err < 0)
1452 return err;
1453 }
1454
1455 if (spec->multiout.dig_out_nid) {
1456 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001457 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001458 spec->multiout.dig_out_nid);
1459 if (err < 0)
1460 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001461 err = snd_hda_create_spdif_share_sw(codec,
1462 &spec->multiout);
1463 if (err < 0)
1464 return err;
1465 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001466 }
1467 if (spec->dig_in_nid) {
1468 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1469 if (err < 0)
1470 return err;
1471 }
Lydia Wang17314372009-10-10 19:07:37 +08001472
Takashi Iwai370bafb2011-06-20 12:47:45 +02001473 /* if we have no master control, let's create it */
1474 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1475 unsigned int vmaster_tlv[4];
1476 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1477 HDA_OUTPUT, vmaster_tlv);
1478 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1479 vmaster_tlv, via_slave_vols);
1480 if (err < 0)
1481 return err;
1482 }
1483 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1484 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1485 NULL, via_slave_sws);
1486 if (err < 0)
1487 return err;
1488 }
1489
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001490 /* assign Capture Source enums to NID */
1491 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1492 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001493 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001494 if (err < 0)
1495 return err;
1496 }
1497
Lydia Wang17314372009-10-10 19:07:37 +08001498 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001499 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001500 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001501
Takashi Iwai603c4012008-07-30 15:01:44 +02001502 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001503 return 0;
1504}
1505
1506static int via_build_pcms(struct hda_codec *codec)
1507{
1508 struct via_spec *spec = codec->spec;
1509 struct hda_pcm *info = spec->pcm_rec;
1510
Takashi Iwaia5973102011-09-28 16:43:36 +02001511 codec->num_pcms = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001512 codec->pcm_info = info;
1513
Takashi Iwaia5973102011-09-28 16:43:36 +02001514 if (spec->multiout.num_dacs || spec->num_adc_nids) {
1515 snprintf(spec->stream_name_analog,
1516 sizeof(spec->stream_name_analog),
1517 "%s Analog", codec->chip_name);
1518 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001519
Takashi Iwaia5973102011-09-28 16:43:36 +02001520 if (spec->multiout.num_dacs) {
1521 if (!spec->stream_analog_playback)
1522 spec->stream_analog_playback =
1523 &via_pcm_analog_playback;
1524 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1525 *spec->stream_analog_playback;
1526 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1527 spec->multiout.dac_nids[0];
1528 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1529 spec->multiout.max_channels;
1530 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001531
Takashi Iwaia5973102011-09-28 16:43:36 +02001532 if (!spec->stream_analog_capture) {
1533 if (spec->dyn_adc_switch)
1534 spec->stream_analog_capture =
1535 &via_pcm_dyn_adc_analog_capture;
1536 else
1537 spec->stream_analog_capture =
1538 &via_pcm_analog_capture;
1539 }
1540 if (spec->num_adc_nids) {
1541 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1542 *spec->stream_analog_capture;
1543 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1544 spec->adc_nids[0];
1545 if (!spec->dyn_adc_switch)
1546 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1547 spec->num_adc_nids;
1548 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001549 codec->num_pcms++;
1550 info++;
Takashi Iwaia5973102011-09-28 16:43:36 +02001551 }
1552
1553 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
Takashi Iwai82673bc2011-06-17 16:24:21 +02001554 snprintf(spec->stream_name_digital,
1555 sizeof(spec->stream_name_digital),
1556 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001557 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001558 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001559 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001560 if (!spec->stream_digital_playback)
1561 spec->stream_digital_playback =
1562 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001563 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001564 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001565 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1566 spec->multiout.dig_out_nid;
1567 }
1568 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001569 if (!spec->stream_digital_capture)
1570 spec->stream_digital_capture =
1571 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001572 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001573 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001574 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1575 spec->dig_in_nid;
1576 }
Takashi Iwaia5973102011-09-28 16:43:36 +02001577 codec->num_pcms++;
1578 info++;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001579 }
1580
Takashi Iwaiece8d042011-06-19 16:24:21 +02001581 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001582 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1583 "%s HP", codec->chip_name);
1584 info->name = spec->stream_name_hp;
1585 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1586 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001587 spec->hp_dac_nid;
Takashi Iwaia5973102011-09-28 16:43:36 +02001588 codec->num_pcms++;
1589 info++;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001590 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001591 return 0;
1592}
1593
1594static void via_free(struct hda_codec *codec)
1595{
1596 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001597
1598 if (!spec)
1599 return;
1600
Takashi Iwai603c4012008-07-30 15:01:44 +02001601 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001602 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001603 kfree(spec->bind_cap_vol);
1604 kfree(spec->bind_cap_sw);
1605 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001606}
1607
Takashi Iwai64be2852011-06-17 16:51:39 +02001608/* mute/unmute outputs */
1609static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1610 hda_nid_t *pins, bool mute)
1611{
1612 int i;
Takashi Iwai94994732011-07-11 11:36:44 +02001613 for (i = 0; i < num_pins; i++) {
1614 unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
1615 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1616 if (parm & AC_PINCTL_IN_EN)
1617 continue;
1618 if (mute)
1619 parm &= ~AC_PINCTL_OUT_EN;
1620 else
1621 parm |= AC_PINCTL_OUT_EN;
Takashi Iwai64be2852011-06-17 16:51:39 +02001622 snd_hda_codec_write(codec, pins[i], 0,
Takashi Iwai94994732011-07-11 11:36:44 +02001623 AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
1624 }
Takashi Iwai64be2852011-06-17 16:51:39 +02001625}
1626
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001627/* mute internal speaker if line-out is plugged */
1628static void via_line_automute(struct hda_codec *codec, int present)
1629{
1630 struct via_spec *spec = codec->spec;
1631
1632 if (!spec->autocfg.speaker_outs)
1633 return;
1634 if (!present)
1635 present = snd_hda_jack_detect(codec,
1636 spec->autocfg.line_out_pins[0]);
1637 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1638 spec->autocfg.speaker_pins,
1639 present);
1640}
1641
Harald Welte69e52a82008-09-09 15:57:32 +08001642/* mute internal speaker if HP is plugged */
1643static void via_hp_automute(struct hda_codec *codec)
1644{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001645 int present = 0;
Takashi Iwai6e969d92011-07-11 11:28:13 +02001646 int nums;
Harald Welte69e52a82008-09-09 15:57:32 +08001647 struct via_spec *spec = codec->spec;
1648
Takashi Iwai6e969d92011-07-11 11:28:13 +02001649 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0])
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001650 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai6e969d92011-07-11 11:28:13 +02001651
1652 if (spec->smart51_enabled)
1653 nums = spec->autocfg.line_outs + spec->smart51_nums;
1654 else
1655 nums = spec->autocfg.line_outs;
1656 toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
1657
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001658 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001659}
1660
Harald Welte69e52a82008-09-09 15:57:32 +08001661static void via_gpio_control(struct hda_codec *codec)
1662{
1663 unsigned int gpio_data;
1664 unsigned int vol_counter;
1665 unsigned int vol;
1666 unsigned int master_vol;
1667
1668 struct via_spec *spec = codec->spec;
1669
1670 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1671 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1672
1673 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1674 0xF84, 0) & 0x3F0000) >> 16;
1675
1676 vol = vol_counter & 0x1F;
1677 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1678 AC_VERB_GET_AMP_GAIN_MUTE,
1679 AC_AMP_GET_INPUT);
1680
1681 if (gpio_data == 0x02) {
1682 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001683 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1684 AC_VERB_SET_PIN_WIDGET_CONTROL,
1685 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001686 if (vol_counter & 0x20) {
1687 /* decrease volume */
1688 if (vol > master_vol)
1689 vol = master_vol;
1690 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1691 0, HDA_AMP_VOLMASK,
1692 master_vol-vol);
1693 } else {
1694 /* increase volume */
1695 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1696 HDA_AMP_VOLMASK,
1697 ((master_vol+vol) > 0x2A) ? 0x2A :
1698 (master_vol+vol));
1699 }
1700 } else if (!(gpio_data & 0x02)) {
1701 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001702 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1703 AC_VERB_SET_PIN_WIDGET_CONTROL,
1704 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001705 }
1706}
1707
1708/* unsolicited event for jack sensing */
1709static void via_unsol_event(struct hda_codec *codec,
1710 unsigned int res)
1711{
Takashi Iwai1835a0f2011-10-27 22:12:46 +02001712 snd_hda_jack_set_dirty_all(codec); /* FIXME: to be more fine-grained */
1713
Harald Welte69e52a82008-09-09 15:57:32 +08001714 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001715
Lydia Wanga34df192009-10-10 19:08:01 +08001716 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001717 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001718
1719 res &= ~VIA_JACK_EVENT;
1720
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001721 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001722 via_hp_automute(codec);
1723 else if (res == VIA_GPIO_EVENT)
1724 via_gpio_control(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001725}
1726
Takashi Iwai2a439522011-07-26 09:52:50 +02001727#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001728static int via_suspend(struct hda_codec *codec, pm_message_t state)
1729{
1730 struct via_spec *spec = codec->spec;
1731 vt1708_stop_hp_work(spec);
1732 return 0;
1733}
1734#endif
1735
Takashi Iwaicb53c622007-08-10 17:21:45 +02001736#ifdef CONFIG_SND_HDA_POWER_SAVE
1737static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1738{
1739 struct via_spec *spec = codec->spec;
1740 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1741}
1742#endif
1743
Joseph Chanc577b8a2006-11-29 15:29:40 +01001744/*
1745 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001746
1747static int via_init(struct hda_codec *codec);
1748
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001749static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001750 .build_controls = via_build_controls,
1751 .build_pcms = via_build_pcms,
1752 .init = via_init,
1753 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001754 .unsol_event = via_unsol_event,
Takashi Iwai2a439522011-07-26 09:52:50 +02001755#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001756 .suspend = via_suspend,
1757#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001758#ifdef CONFIG_SND_HDA_POWER_SAVE
1759 .check_power_status = via_check_power_status,
1760#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001761};
1762
Takashi Iwai4a796162011-06-17 17:53:38 +02001763static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001764{
Takashi Iwai4a796162011-06-17 17:53:38 +02001765 struct via_spec *spec = codec->spec;
1766 int i;
1767
1768 for (i = 0; i < spec->multiout.num_dacs; i++) {
1769 if (spec->multiout.dac_nids[i] == dac)
1770 return false;
1771 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001772 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001773 return false;
1774 return true;
1775}
1776
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001777static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001778 hda_nid_t target_dac, int with_aa_mix,
1779 struct nid_path *path, int depth)
Takashi Iwai4a796162011-06-17 17:53:38 +02001780{
Takashi Iwai3214b962011-07-18 12:49:25 +02001781 struct via_spec *spec = codec->spec;
Takashi Iwai4a796162011-06-17 17:53:38 +02001782 hda_nid_t conn[8];
1783 int i, nums;
1784
Takashi Iwai3214b962011-07-18 12:49:25 +02001785 if (nid == spec->aa_mix_nid) {
1786 if (!with_aa_mix)
1787 return false;
1788 with_aa_mix = 2; /* mark aa-mix is included */
1789 }
1790
Takashi Iwai4a796162011-06-17 17:53:38 +02001791 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1792 for (i = 0; i < nums; i++) {
1793 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1794 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001795 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1796 /* aa-mix is requested but not included? */
1797 if (!(spec->aa_mix_nid && with_aa_mix == 1))
1798 goto found;
1799 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001800 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001801 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001802 return false;
1803 for (i = 0; i < nums; i++) {
1804 unsigned int type;
1805 type = get_wcaps_type(get_wcaps(codec, conn[i]));
Takashi Iwai3214b962011-07-18 12:49:25 +02001806 if (type == AC_WID_AUD_OUT)
Takashi Iwai4a796162011-06-17 17:53:38 +02001807 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001808 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai3214b962011-07-18 12:49:25 +02001809 with_aa_mix, path, depth + 1))
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001810 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001811 }
1812 return false;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001813
1814 found:
1815 path->path[path->depth] = conn[i];
1816 path->idx[path->depth] = i;
1817 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1818 path->multi[path->depth] = 1;
1819 path->depth++;
1820 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001821}
1822
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001823static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001824 hda_nid_t target_dac, int with_aa_mix,
1825 struct nid_path *path)
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001826{
Takashi Iwai3214b962011-07-18 12:49:25 +02001827 if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001828 path->path[path->depth] = nid;
1829 path->depth++;
Takashi Iwai3214b962011-07-18 12:49:25 +02001830 snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
1831 path->depth, path->path[0], path->path[1],
1832 path->path[2], path->path[3], path->path[4]);
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001833 return true;
1834 }
1835 return false;
1836}
1837
Takashi Iwai4a796162011-06-17 17:53:38 +02001838static int via_auto_fill_dac_nids(struct hda_codec *codec)
1839{
1840 struct via_spec *spec = codec->spec;
1841 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001842 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001843 hda_nid_t nid;
1844
Joseph Chanc577b8a2006-11-29 15:29:40 +01001845 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001846 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001847 for (i = 0; i < cfg->line_outs; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001848 hda_nid_t dac = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001849 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001850 if (!nid)
1851 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001852 if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
1853 dac = spec->out_path[i].path[0];
1854 if (!i && parse_output_path(codec, nid, dac, 1,
1855 &spec->out_mix_path))
1856 dac = spec->out_mix_path.path[0];
1857 if (dac) {
1858 spec->private_dac_nids[i] = dac;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001859 dac_num++;
1860 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001861 }
Takashi Iwai3214b962011-07-18 12:49:25 +02001862 if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
1863 spec->out_path[0] = spec->out_mix_path;
1864 spec->out_mix_path.depth = 0;
1865 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001866 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001867 return 0;
1868}
1869
Takashi Iwai4a796162011-06-17 17:53:38 +02001870static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001871 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001872{
Takashi Iwai4a796162011-06-17 17:53:38 +02001873 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001874 char name[32];
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001875 hda_nid_t dac, pin, sel, nid;
1876 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001877
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001878 dac = check_dac ? path->path[0] : 0;
1879 pin = path->path[path->depth - 1];
1880 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001881
Takashi Iwai8df2a312011-06-21 11:48:29 +02001882 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001883 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001884 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001885 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001886 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1887 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001888 else
1889 nid = 0;
1890 if (nid) {
1891 sprintf(name, "%s Playback Volume", pfx);
1892 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001893 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001894 if (err < 0)
1895 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001896 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001897 }
1898
Takashi Iwai8df2a312011-06-21 11:48:29 +02001899 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001900 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001901 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001902 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001903 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1904 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001905 else
1906 nid = 0;
1907 if (nid) {
1908 sprintf(name, "%s Playback Switch", pfx);
1909 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1910 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1911 if (err < 0)
1912 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001913 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001914 }
1915 return 0;
1916}
1917
Takashi Iwaif4a78282011-06-17 18:46:48 +02001918static void mangle_smart51(struct hda_codec *codec)
1919{
1920 struct via_spec *spec = codec->spec;
1921 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001922 struct auto_pin_cfg_item *ins = cfg->inputs;
1923 int i, j, nums, attr;
1924 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001925
Takashi Iwai0f98c242011-06-21 12:51:33 +02001926 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1927 nums = 0;
1928 for (i = 0; i < cfg->num_inputs; i++) {
1929 unsigned int def;
1930 if (ins[i].type > AUTO_PIN_LINE_IN)
1931 continue;
1932 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1933 if (snd_hda_get_input_pin_attr(def) != attr)
1934 continue;
1935 for (j = 0; j < nums; j++)
1936 if (ins[pins[j]].type < ins[i].type) {
1937 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001938 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001939 break;
1940 }
1941 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001942 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001943 }
1944 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001945 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001946 for (i = 0; i < nums; i++) {
1947 hda_nid_t pin = ins[pins[i]].pin;
1948 spec->smart51_pins[spec->smart51_nums++] = pin;
1949 cfg->line_out_pins[cfg->line_outs++] = pin;
1950 if (cfg->line_outs == 3)
1951 break;
1952 }
1953 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001954 }
1955}
1956
Takashi Iwai020066d2011-07-21 13:45:56 +02001957static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src)
1958{
1959 dst->vol_ctl = src->vol_ctl;
1960 dst->mute_ctl = src->mute_ctl;
1961}
1962
Takashi Iwai4a796162011-06-17 17:53:38 +02001963/* add playback controls from the parsed DAC table */
1964static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1965{
1966 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001967 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai3214b962011-07-18 12:49:25 +02001968 struct nid_path *path;
Takashi Iwaiea734962011-01-17 11:29:34 +01001969 static const char * const chname[4] = {
1970 "Front", "Surround", "C/LFE", "Side"
1971 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001972 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001973 int old_line_outs;
1974
1975 /* check smart51 */
1976 old_line_outs = cfg->line_outs;
1977 if (cfg->line_outs == 1)
1978 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001979
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001980 err = via_auto_fill_dac_nids(codec);
1981 if (err < 0)
1982 return err;
1983
Lydia Wang5c9a5612011-07-08 14:03:43 +08001984 if (spec->multiout.num_dacs < 3) {
1985 spec->smart51_nums = 0;
1986 cfg->line_outs = old_line_outs;
1987 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001988 for (i = 0; i < cfg->line_outs; i++) {
1989 hda_nid_t pin, dac;
1990 pin = cfg->line_out_pins[i];
1991 dac = spec->multiout.dac_nids[i];
1992 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001993 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001994 path = spec->out_path + i;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001995 if (i == HDA_CLFE) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001996 err = create_ch_ctls(codec, "Center", 1, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001997 if (err < 0)
1998 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02001999 err = create_ch_ctls(codec, "LFE", 2, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002000 if (err < 0)
2001 return err;
2002 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02002003 const char *pfx = chname[i];
2004 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
2005 cfg->line_outs == 1)
2006 pfx = "Speaker";
Takashi Iwai3214b962011-07-18 12:49:25 +02002007 err = create_ch_ctls(codec, pfx, 3, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002008 if (err < 0)
2009 return err;
2010 }
Takashi Iwai020066d2011-07-21 13:45:56 +02002011 if (path != spec->out_path + i)
2012 copy_path_mixer_ctls(&spec->out_path[i], path);
2013 if (path == spec->out_path && spec->out_mix_path.depth)
2014 copy_path_mixer_ctls(&spec->out_mix_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002015 }
2016
Takashi Iwai4a796162011-06-17 17:53:38 +02002017 idx = get_connection_index(codec, spec->aa_mix_nid,
2018 spec->multiout.dac_nids[0]);
2019 if (idx >= 0) {
2020 /* add control to mixer */
Takashi Iwai3214b962011-07-18 12:49:25 +02002021 const char *name;
2022 name = spec->out_mix_path.depth ?
2023 "PCM Loopback Playback Volume" : "PCM Playback Volume";
2024 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002025 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2026 idx, HDA_INPUT));
2027 if (err < 0)
2028 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002029 name = spec->out_mix_path.depth ?
2030 "PCM Loopback Playback Switch" : "PCM Playback Switch";
2031 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002032 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2033 idx, HDA_INPUT));
2034 if (err < 0)
2035 return err;
2036 }
2037
Takashi Iwaif4a78282011-06-17 18:46:48 +02002038 cfg->line_outs = old_line_outs;
2039
Joseph Chanc577b8a2006-11-29 15:29:40 +01002040 return 0;
2041}
2042
Takashi Iwai4a796162011-06-17 17:53:38 +02002043static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002044{
Takashi Iwai4a796162011-06-17 17:53:38 +02002045 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02002046 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002047 bool check_dac;
Takashi Iwai3214b962011-07-18 12:49:25 +02002048 int i, err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002049
2050 if (!pin)
2051 return 0;
2052
Takashi Iwai3214b962011-07-18 12:49:25 +02002053 if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
2054 for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
2055 if (i < spec->multiout.num_dacs &&
2056 parse_output_path(codec, pin,
2057 spec->multiout.dac_nids[i], 0,
2058 &spec->hp_indep_path)) {
2059 spec->hp_indep_shared = i;
2060 break;
2061 }
2062 }
Takashi Iwai25250502011-06-30 17:24:47 +02002063 }
Takashi Iwai3214b962011-07-18 12:49:25 +02002064 if (spec->hp_indep_path.depth) {
2065 spec->hp_dac_nid = spec->hp_indep_path.path[0];
2066 if (!spec->hp_indep_shared)
2067 spec->hp_path = spec->hp_indep_path;
2068 }
2069 /* optionally check front-path w/o AA-mix */
2070 if (!spec->hp_path.depth)
2071 parse_output_path(codec, pin,
2072 spec->multiout.dac_nids[HDA_FRONT], 0,
2073 &spec->hp_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002074
Takashi Iwaiece8d042011-06-19 16:24:21 +02002075 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai3214b962011-07-18 12:49:25 +02002076 1, &spec->hp_mix_path) && !spec->hp_path.depth)
Takashi Iwaiece8d042011-06-19 16:24:21 +02002077 return 0;
2078
Takashi Iwai3214b962011-07-18 12:49:25 +02002079 if (spec->hp_path.depth) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +02002080 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002081 check_dac = true;
2082 } else {
Takashi Iwai3214b962011-07-18 12:49:25 +02002083 path = &spec->hp_mix_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002084 check_dac = false;
2085 }
2086 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002087 if (err < 0)
2088 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002089 if (check_dac)
2090 copy_path_mixer_ctls(&spec->hp_mix_path, path);
2091 else
2092 copy_path_mixer_ctls(&spec->hp_path, path);
2093 if (spec->hp_indep_path.depth)
2094 copy_path_mixer_ctls(&spec->hp_indep_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002095 return 0;
2096}
2097
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002098static int via_auto_create_speaker_ctls(struct hda_codec *codec)
2099{
2100 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +02002101 struct nid_path *path;
2102 bool check_dac;
Wang Shaoyan81c0a782011-08-05 18:51:29 +08002103 hda_nid_t pin, dac = 0;
Takashi Iwai3214b962011-07-18 12:49:25 +02002104 int err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002105
2106 pin = spec->autocfg.speaker_pins[0];
2107 if (!spec->autocfg.speaker_outs || !pin)
2108 return 0;
2109
Takashi Iwai3214b962011-07-18 12:49:25 +02002110 if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02002111 dac = spec->speaker_path.path[0];
Takashi Iwai3214b962011-07-18 12:49:25 +02002112 if (!dac)
2113 parse_output_path(codec, pin,
2114 spec->multiout.dac_nids[HDA_FRONT], 0,
2115 &spec->speaker_path);
2116 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
2117 1, &spec->speaker_mix_path) && !dac)
2118 return 0;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002119
Takashi Iwai3214b962011-07-18 12:49:25 +02002120 /* no AA-path for front? */
2121 if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
2122 dac = 0;
2123
2124 spec->speaker_dac_nid = dac;
2125 spec->multiout.extra_out_nid[0] = dac;
2126 if (dac) {
2127 path = &spec->speaker_path;
2128 check_dac = true;
2129 } else {
2130 path = &spec->speaker_mix_path;
2131 check_dac = false;
2132 }
2133 err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
2134 if (err < 0)
2135 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002136 if (check_dac)
2137 copy_path_mixer_ctls(&spec->speaker_mix_path, path);
2138 else
2139 copy_path_mixer_ctls(&spec->speaker_path, path);
Takashi Iwai3214b962011-07-18 12:49:25 +02002140 return 0;
2141}
2142
2143#define via_aamix_ctl_info via_pin_power_ctl_info
2144
2145static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol,
2146 struct snd_ctl_elem_value *ucontrol)
2147{
2148 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2149 struct via_spec *spec = codec->spec;
2150 ucontrol->value.enumerated.item[0] = spec->aamix_mode;
2151 return 0;
2152}
2153
2154static void update_aamix_paths(struct hda_codec *codec, int do_mix,
2155 struct nid_path *nomix, struct nid_path *mix)
2156{
2157 if (do_mix) {
2158 activate_output_path(codec, nomix, false, false);
2159 activate_output_path(codec, mix, true, false);
2160 } else {
2161 activate_output_path(codec, mix, false, false);
2162 activate_output_path(codec, nomix, true, false);
2163 }
2164}
2165
2166static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol,
2167 struct snd_ctl_elem_value *ucontrol)
2168{
2169 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2170 struct via_spec *spec = codec->spec;
2171 unsigned int val = ucontrol->value.enumerated.item[0];
2172
2173 if (val == spec->aamix_mode)
2174 return 0;
2175 spec->aamix_mode = val;
2176 /* update front path */
2177 update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
2178 /* update HP path */
2179 if (!spec->hp_independent_mode) {
2180 update_aamix_paths(codec, val, &spec->hp_path,
2181 &spec->hp_mix_path);
2182 }
2183 /* update speaker path */
2184 update_aamix_paths(codec, val, &spec->speaker_path,
2185 &spec->speaker_mix_path);
2186 return 1;
2187}
2188
2189static const struct snd_kcontrol_new via_aamix_ctl_enum = {
2190 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2191 .name = "Loopback Mixing",
2192 .info = via_aamix_ctl_info,
2193 .get = via_aamix_ctl_get,
2194 .put = via_aamix_ctl_put,
2195};
2196
2197static int via_auto_create_loopback_switch(struct hda_codec *codec)
2198{
2199 struct via_spec *spec = codec->spec;
2200
2201 if (!spec->aa_mix_nid || !spec->out_mix_path.depth)
2202 return 0; /* no loopback switching available */
2203 if (!via_clone_control(spec, &via_aamix_ctl_enum))
2204 return -ENOMEM;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002205 return 0;
2206}
2207
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002208/* look for ADCs */
2209static int via_fill_adcs(struct hda_codec *codec)
2210{
2211 struct via_spec *spec = codec->spec;
2212 hda_nid_t nid = codec->start_nid;
2213 int i;
2214
2215 for (i = 0; i < codec->num_nodes; i++, nid++) {
2216 unsigned int wcaps = get_wcaps(codec, nid);
2217 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2218 continue;
2219 if (wcaps & AC_WCAP_DIGITAL)
2220 continue;
2221 if (!(wcaps & AC_WCAP_CONN_LIST))
2222 continue;
2223 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2224 return -ENOMEM;
2225 spec->adc_nids[spec->num_adc_nids++] = nid;
2226 }
2227 return 0;
2228}
2229
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002230/* input-src control */
2231static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2232 struct snd_ctl_elem_info *uinfo)
2233{
2234 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2235 struct via_spec *spec = codec->spec;
2236
2237 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2238 uinfo->count = 1;
2239 uinfo->value.enumerated.items = spec->num_inputs;
2240 if (uinfo->value.enumerated.item >= spec->num_inputs)
2241 uinfo->value.enumerated.item = spec->num_inputs - 1;
2242 strcpy(uinfo->value.enumerated.name,
2243 spec->inputs[uinfo->value.enumerated.item].label);
2244 return 0;
2245}
2246
2247static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2248 struct snd_ctl_elem_value *ucontrol)
2249{
2250 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2251 struct via_spec *spec = codec->spec;
2252 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2253
2254 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2255 return 0;
2256}
2257
2258static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2259 struct snd_ctl_elem_value *ucontrol)
2260{
2261 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2262 struct via_spec *spec = codec->spec;
2263 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2264 hda_nid_t mux;
2265 int cur;
2266
2267 cur = ucontrol->value.enumerated.item[0];
2268 if (cur < 0 || cur >= spec->num_inputs)
2269 return -EINVAL;
2270 if (spec->cur_mux[idx] == cur)
2271 return 0;
2272 spec->cur_mux[idx] = cur;
2273 if (spec->dyn_adc_switch) {
2274 int adc_idx = spec->inputs[cur].adc_idx;
2275 mux = spec->mux_nids[adc_idx];
2276 via_dyn_adc_pcm_resetup(codec, cur);
2277 } else {
2278 mux = spec->mux_nids[idx];
2279 if (snd_BUG_ON(!mux))
2280 return -EINVAL;
2281 }
2282
2283 if (mux) {
2284 /* switch to D0 beofre change index */
2285 if (snd_hda_codec_read(codec, mux, 0,
2286 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2287 snd_hda_codec_write(codec, mux, 0,
2288 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2289 snd_hda_codec_write(codec, mux, 0,
2290 AC_VERB_SET_CONNECT_SEL,
2291 spec->inputs[cur].mux_idx);
2292 }
2293
2294 /* update jack power state */
2295 set_widgets_power_state(codec);
2296 return 0;
2297}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002298
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002299static const struct snd_kcontrol_new via_input_src_ctl = {
2300 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2301 /* The multiple "Capture Source" controls confuse alsamixer
2302 * So call somewhat different..
2303 */
2304 /* .name = "Capture Source", */
2305 .name = "Input Source",
2306 .info = via_mux_enum_info,
2307 .get = via_mux_enum_get,
2308 .put = via_mux_enum_put,
2309};
2310
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002311static int create_input_src_ctls(struct hda_codec *codec, int count)
2312{
2313 struct via_spec *spec = codec->spec;
2314 struct snd_kcontrol_new *knew;
2315
2316 if (spec->num_inputs <= 1 || !count)
2317 return 0; /* no need for single src */
2318
2319 knew = via_clone_control(spec, &via_input_src_ctl);
2320 if (!knew)
2321 return -ENOMEM;
2322 knew->count = count;
2323 return 0;
2324}
2325
2326/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002327static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2328{
2329 struct hda_amp_list *list;
2330
2331 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2332 return;
2333 list = spec->loopback_list + spec->num_loopbacks;
2334 list->nid = mix;
2335 list->dir = HDA_INPUT;
2336 list->idx = idx;
2337 spec->num_loopbacks++;
2338 spec->loopback.amplist = spec->loopback_list;
2339}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002340
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002341static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002342 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002343{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002344 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002345}
2346
2347/* add the input-route to the given pin */
2348static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002349{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002350 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002351 int c, idx;
2352
2353 spec->inputs[spec->num_inputs].adc_idx = -1;
2354 spec->inputs[spec->num_inputs].pin = pin;
2355 for (c = 0; c < spec->num_adc_nids; c++) {
2356 if (spec->mux_nids[c]) {
2357 idx = get_connection_index(codec, spec->mux_nids[c],
2358 pin);
2359 if (idx < 0)
2360 continue;
2361 spec->inputs[spec->num_inputs].mux_idx = idx;
2362 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002363 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002364 continue;
2365 }
2366 spec->inputs[spec->num_inputs].adc_idx = c;
2367 /* Can primary ADC satisfy all inputs? */
2368 if (!spec->dyn_adc_switch &&
2369 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2370 snd_printd(KERN_INFO
2371 "via: dynamic ADC switching enabled\n");
2372 spec->dyn_adc_switch = 1;
2373 }
2374 return true;
2375 }
2376 return false;
2377}
2378
2379static int get_mux_nids(struct hda_codec *codec);
2380
2381/* parse input-routes; fill ADCs, MUXs and input-src entries */
2382static int parse_analog_inputs(struct hda_codec *codec)
2383{
2384 struct via_spec *spec = codec->spec;
2385 const struct auto_pin_cfg *cfg = &spec->autocfg;
2386 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002387
2388 err = via_fill_adcs(codec);
2389 if (err < 0)
2390 return err;
2391 err = get_mux_nids(codec);
2392 if (err < 0)
2393 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002394
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002395 /* fill all input-routes */
2396 for (i = 0; i < cfg->num_inputs; i++) {
2397 if (add_input_route(codec, cfg->inputs[i].pin))
2398 spec->inputs[spec->num_inputs++].label =
2399 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002400 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002401
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002402 /* check for internal loopback recording */
2403 if (spec->aa_mix_nid &&
2404 add_input_route(codec, spec->aa_mix_nid))
2405 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2406
2407 return 0;
2408}
2409
2410/* create analog-loopback volume/switch controls */
2411static int create_loopback_ctls(struct hda_codec *codec)
2412{
2413 struct via_spec *spec = codec->spec;
2414 const struct auto_pin_cfg *cfg = &spec->autocfg;
2415 const char *prev_label = NULL;
2416 int type_idx = 0;
2417 int i, j, err, idx;
2418
2419 if (!spec->aa_mix_nid)
2420 return 0;
2421
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002422 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002423 hda_nid_t pin = cfg->inputs[i].pin;
2424 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2425
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002426 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002427 type_idx++;
2428 else
2429 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002430 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002431 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2432 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002433 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002434 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002435 if (err < 0)
2436 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002437 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002438 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002439
2440 /* remember the label for smart51 control */
2441 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002442 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002443 spec->smart51_idxs[j] = idx;
2444 spec->smart51_labels[j] = label;
2445 break;
2446 }
2447 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002448 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002449 return 0;
2450}
2451
2452/* create mic-boost controls (if present) */
2453static int create_mic_boost_ctls(struct hda_codec *codec)
2454{
2455 struct via_spec *spec = codec->spec;
2456 const struct auto_pin_cfg *cfg = &spec->autocfg;
2457 int i, err;
2458
2459 for (i = 0; i < cfg->num_inputs; i++) {
2460 hda_nid_t pin = cfg->inputs[i].pin;
2461 unsigned int caps;
2462 const char *label;
2463 char name[32];
2464
2465 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2466 continue;
2467 caps = query_amp_caps(codec, pin, HDA_INPUT);
2468 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2469 continue;
2470 label = hda_get_autocfg_input_label(codec, cfg, i);
2471 snprintf(name, sizeof(name), "%s Boost Volume", label);
2472 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2473 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2474 if (err < 0)
2475 return err;
2476 }
2477 return 0;
2478}
2479
2480/* create capture and input-src controls for multiple streams */
2481static int create_multi_adc_ctls(struct hda_codec *codec)
2482{
2483 struct via_spec *spec = codec->spec;
2484 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002485
2486 /* create capture mixer elements */
2487 for (i = 0; i < spec->num_adc_nids; i++) {
2488 hda_nid_t adc = spec->adc_nids[i];
2489 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2490 "Capture Volume", i,
2491 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2492 HDA_INPUT));
2493 if (err < 0)
2494 return err;
2495 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2496 "Capture Switch", i,
2497 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2498 HDA_INPUT));
2499 if (err < 0)
2500 return err;
2501 }
2502
2503 /* input-source control */
2504 for (i = 0; i < spec->num_adc_nids; i++)
2505 if (!spec->mux_nids[i])
2506 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002507 err = create_input_src_ctls(codec, i);
2508 if (err < 0)
2509 return err;
2510 return 0;
2511}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002512
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002513/* bind capture volume/switch */
2514static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2515 HDA_BIND_VOL("Capture Volume", 0);
2516static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2517 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002518
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002519static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2520 struct hda_ctl_ops *ops)
2521{
2522 struct hda_bind_ctls *ctl;
2523 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002524
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002525 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2526 if (!ctl)
2527 return -ENOMEM;
2528 ctl->ops = ops;
2529 for (i = 0; i < spec->num_adc_nids; i++)
2530 ctl->values[i] =
2531 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2532 *ctl_ret = ctl;
2533 return 0;
2534}
2535
2536/* create capture and input-src controls for dynamic ADC-switch case */
2537static int create_dyn_adc_ctls(struct hda_codec *codec)
2538{
2539 struct via_spec *spec = codec->spec;
2540 struct snd_kcontrol_new *knew;
2541 int err;
2542
2543 /* set up the bind capture ctls */
2544 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2545 if (err < 0)
2546 return err;
2547 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2548 if (err < 0)
2549 return err;
2550
2551 /* create capture mixer elements */
2552 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2553 if (!knew)
2554 return -ENOMEM;
2555 knew->private_value = (long)spec->bind_cap_vol;
2556
2557 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2558 if (!knew)
2559 return -ENOMEM;
2560 knew->private_value = (long)spec->bind_cap_sw;
2561
2562 /* input-source control */
2563 err = create_input_src_ctls(codec, 1);
2564 if (err < 0)
2565 return err;
2566 return 0;
2567}
2568
2569/* parse and create capture-related stuff */
2570static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2571{
2572 struct via_spec *spec = codec->spec;
2573 int err;
2574
2575 err = parse_analog_inputs(codec);
2576 if (err < 0)
2577 return err;
2578 if (spec->dyn_adc_switch)
2579 err = create_dyn_adc_ctls(codec);
2580 else
2581 err = create_multi_adc_ctls(codec);
2582 if (err < 0)
2583 return err;
2584 err = create_loopback_ctls(codec);
2585 if (err < 0)
2586 return err;
2587 err = create_mic_boost_ctls(codec);
2588 if (err < 0)
2589 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002590 return 0;
2591}
2592
Harald Welte76d9b0d2008-09-09 15:50:37 +08002593static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2594{
2595 unsigned int def_conf;
2596 unsigned char seqassoc;
2597
Takashi Iwai2f334f92009-02-20 14:37:42 +01002598 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002599 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2600 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002601 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2602 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2603 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2604 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002605 }
2606
2607 return;
2608}
2609
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002610static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002611 struct snd_ctl_elem_value *ucontrol)
2612{
2613 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2614 struct via_spec *spec = codec->spec;
2615
2616 if (spec->codec_type != VT1708)
2617 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002618 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002619 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002620 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002621 return 0;
2622}
2623
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002624static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002625 struct snd_ctl_elem_value *ucontrol)
2626{
2627 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2628 struct via_spec *spec = codec->spec;
2629 int change;
2630
2631 if (spec->codec_type != VT1708)
2632 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002633 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002634 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002635 == !spec->vt1708_jack_detect;
2636 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002637 mute_aa_path(codec, 1);
2638 notify_aa_path_ctls(codec);
2639 }
2640 return change;
2641}
2642
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002643static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2644 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2645 .name = "Jack Detect",
2646 .count = 1,
2647 .info = snd_ctl_boolean_mono_info,
2648 .get = vt1708_jack_detect_get,
2649 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002650};
2651
Takashi Iwai12daef62011-06-18 17:45:49 +02002652static void fill_dig_outs(struct hda_codec *codec);
2653static void fill_dig_in(struct hda_codec *codec);
2654
2655static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002656{
2657 struct via_spec *spec = codec->spec;
2658 int err;
2659
2660 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2661 if (err < 0)
2662 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002663 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002664 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002665
Takashi Iwai4a796162011-06-17 17:53:38 +02002666 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002667 if (err < 0)
2668 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002669 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002670 if (err < 0)
2671 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002672 err = via_auto_create_speaker_ctls(codec);
2673 if (err < 0)
2674 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002675 err = via_auto_create_loopback_switch(codec);
2676 if (err < 0)
2677 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002678 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002679 if (err < 0)
2680 return err;
2681
2682 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2683
Takashi Iwai12daef62011-06-18 17:45:49 +02002684 fill_dig_outs(codec);
2685 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002686
Takashi Iwai603c4012008-07-30 15:01:44 +02002687 if (spec->kctls.list)
2688 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002689
Joseph Chanc577b8a2006-11-29 15:29:40 +01002690
Takashi Iwai3214b962011-07-18 12:49:25 +02002691 if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002692 err = via_hp_build(codec);
2693 if (err < 0)
2694 return err;
2695 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002696
Takashi Iwaif4a78282011-06-17 18:46:48 +02002697 err = via_smart51_build(codec);
2698 if (err < 0)
2699 return err;
2700
Takashi Iwai5d417622011-06-20 11:32:27 +02002701 /* assign slave outs */
2702 if (spec->slave_dig_outs[0])
2703 codec->slave_dig_outs = spec->slave_dig_outs;
2704
Joseph Chanc577b8a2006-11-29 15:29:40 +01002705 return 1;
2706}
2707
Takashi Iwai5d417622011-06-20 11:32:27 +02002708static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002709{
Lydia Wang25eaba22009-10-10 19:08:43 +08002710 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002711 if (spec->multiout.dig_out_nid)
2712 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2713 if (spec->slave_dig_outs[0])
2714 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2715}
Lydia Wang25eaba22009-10-10 19:08:43 +08002716
Takashi Iwai5d417622011-06-20 11:32:27 +02002717static void via_auto_init_dig_in(struct hda_codec *codec)
2718{
2719 struct via_spec *spec = codec->spec;
2720 if (!spec->dig_in_nid)
2721 return;
2722 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2723 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2724}
2725
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002726/* initialize the unsolicited events */
2727static void via_auto_init_unsol_event(struct hda_codec *codec)
2728{
2729 struct via_spec *spec = codec->spec;
2730 struct auto_pin_cfg *cfg = &spec->autocfg;
2731 unsigned int ev;
2732 int i;
2733
2734 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002735 snd_hda_jack_detect_enable(codec, cfg->hp_pins[0],
2736 VIA_HP_EVENT | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002737
2738 if (cfg->speaker_pins[0])
2739 ev = VIA_LINE_EVENT;
2740 else
2741 ev = 0;
2742 for (i = 0; i < cfg->line_outs; i++) {
2743 if (cfg->line_out_pins[i] &&
2744 is_jack_detectable(codec, cfg->line_out_pins[i]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002745 snd_hda_jack_detect_enable(codec, cfg->line_out_pins[i],
2746 ev | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002747 }
2748
2749 for (i = 0; i < cfg->num_inputs; i++) {
2750 if (is_jack_detectable(codec, cfg->inputs[i].pin))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002751 snd_hda_jack_detect_enable(codec, cfg->inputs[i].pin,
2752 VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002753 }
2754}
2755
Takashi Iwai5d417622011-06-20 11:32:27 +02002756static int via_init(struct hda_codec *codec)
2757{
2758 struct via_spec *spec = codec->spec;
2759 int i;
2760
2761 for (i = 0; i < spec->num_iverbs; i++)
2762 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2763
Joseph Chanc577b8a2006-11-29 15:29:40 +01002764 via_auto_init_multi_out(codec);
2765 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002766 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002767 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002768 via_auto_init_dig_outs(codec);
2769 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002770
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002771 via_auto_init_unsol_event(codec);
2772
2773 via_hp_automute(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002774
Joseph Chanc577b8a2006-11-29 15:29:40 +01002775 return 0;
2776}
2777
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002778static void vt1708_update_hp_jack_state(struct work_struct *work)
2779{
2780 struct via_spec *spec = container_of(work, struct via_spec,
2781 vt1708_hp_work.work);
2782 if (spec->codec_type != VT1708)
2783 return;
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002784 snd_hda_jack_set_dirty_all(spec->codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002785 /* if jack state toggled */
2786 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002787 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002788 spec->vt1708_hp_present ^= 1;
2789 via_hp_automute(spec->codec);
2790 }
2791 vt1708_start_hp_work(spec);
2792}
2793
Takashi Iwai337b9d02009-07-07 18:18:59 +02002794static int get_mux_nids(struct hda_codec *codec)
2795{
2796 struct via_spec *spec = codec->spec;
2797 hda_nid_t nid, conn[8];
2798 unsigned int type;
2799 int i, n;
2800
2801 for (i = 0; i < spec->num_adc_nids; i++) {
2802 nid = spec->adc_nids[i];
2803 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002804 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002805 if (type == AC_WID_PIN)
2806 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002807 n = snd_hda_get_connections(codec, nid, conn,
2808 ARRAY_SIZE(conn));
2809 if (n <= 0)
2810 break;
2811 if (n > 1) {
2812 spec->mux_nids[i] = nid;
2813 break;
2814 }
2815 nid = conn[0];
2816 }
2817 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002818 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002819}
2820
Joseph Chanc577b8a2006-11-29 15:29:40 +01002821static int patch_vt1708(struct hda_codec *codec)
2822{
2823 struct via_spec *spec;
2824 int err;
2825
2826 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002827 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002828 if (spec == NULL)
2829 return -ENOMEM;
2830
Takashi Iwai620e2b22011-06-17 17:19:19 +02002831 spec->aa_mix_nid = 0x17;
2832
Takashi Iwai12daef62011-06-18 17:45:49 +02002833 /* Add HP and CD pin config connect bit re-config action */
2834 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2835 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2836
Joseph Chanc577b8a2006-11-29 15:29:40 +01002837 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002838 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002839 if (err < 0) {
2840 via_free(codec);
2841 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002842 }
2843
Takashi Iwai12daef62011-06-18 17:45:49 +02002844 /* add jack detect on/off control */
2845 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2846 return -ENOMEM;
2847
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002848 /* disable 32bit format on VT1708 */
2849 if (codec->vendor_id == 0x11061708)
2850 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002851
Lydia Wange322a362011-06-29 13:52:02 +08002852 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2853
Joseph Chanc577b8a2006-11-29 15:29:40 +01002854 codec->patch_ops = via_patch_ops;
2855
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002856 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002857 return 0;
2858}
2859
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002860static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002861{
2862 struct via_spec *spec;
2863 int err;
2864
2865 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002866 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002867 if (spec == NULL)
2868 return -ENOMEM;
2869
Takashi Iwai620e2b22011-06-17 17:19:19 +02002870 spec->aa_mix_nid = 0x18;
2871
Takashi Iwai12daef62011-06-18 17:45:49 +02002872 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002873 if (err < 0) {
2874 via_free(codec);
2875 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002876 }
2877
Joseph Chanc577b8a2006-11-29 15:29:40 +01002878 codec->patch_ops = via_patch_ops;
2879
Josepch Chanf7278fd2007-12-13 16:40:40 +01002880 return 0;
2881}
2882
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002883static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2884{
2885 struct via_spec *spec = codec->spec;
2886 int imux_is_smixer;
2887 unsigned int parm;
2888 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002889 if ((spec->codec_type != VT1708B_4CH) &&
2890 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002891 is_8ch = 1;
2892
2893 /* SW0 (17h) = stereo mixer */
2894 imux_is_smixer =
2895 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2896 == ((spec->codec_type == VT1708S) ? 5 : 0));
2897 /* inputs */
2898 /* PW 1/2/5 (1ah/1bh/1eh) */
2899 parm = AC_PWRST_D3;
2900 set_pin_power_state(codec, 0x1a, &parm);
2901 set_pin_power_state(codec, 0x1b, &parm);
2902 set_pin_power_state(codec, 0x1e, &parm);
2903 if (imux_is_smixer)
2904 parm = AC_PWRST_D0;
2905 /* SW0 (17h), AIW 0/1 (13h/14h) */
2906 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2907 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2908 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2909
2910 /* outputs */
2911 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2912 parm = AC_PWRST_D3;
2913 set_pin_power_state(codec, 0x19, &parm);
2914 if (spec->smart51_enabled)
2915 set_pin_power_state(codec, 0x1b, &parm);
2916 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2917 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2918
2919 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2920 if (is_8ch) {
2921 parm = AC_PWRST_D3;
2922 set_pin_power_state(codec, 0x22, &parm);
2923 if (spec->smart51_enabled)
2924 set_pin_power_state(codec, 0x1a, &parm);
2925 snd_hda_codec_write(codec, 0x26, 0,
2926 AC_VERB_SET_POWER_STATE, parm);
2927 snd_hda_codec_write(codec, 0x24, 0,
2928 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002929 } else if (codec->vendor_id == 0x11064397) {
2930 /* PW7(23h), SW2(27h), AOW2(25h) */
2931 parm = AC_PWRST_D3;
2932 set_pin_power_state(codec, 0x23, &parm);
2933 if (spec->smart51_enabled)
2934 set_pin_power_state(codec, 0x1a, &parm);
2935 snd_hda_codec_write(codec, 0x27, 0,
2936 AC_VERB_SET_POWER_STATE, parm);
2937 snd_hda_codec_write(codec, 0x25, 0,
2938 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002939 }
2940
2941 /* PW 3/4/7 (1ch/1dh/23h) */
2942 parm = AC_PWRST_D3;
2943 /* force to D0 for internal Speaker */
2944 set_pin_power_state(codec, 0x1c, &parm);
2945 set_pin_power_state(codec, 0x1d, &parm);
2946 if (is_8ch)
2947 set_pin_power_state(codec, 0x23, &parm);
2948
2949 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2950 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2951 imux_is_smixer ? AC_PWRST_D0 : parm);
2952 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2953 if (is_8ch) {
2954 snd_hda_codec_write(codec, 0x25, 0,
2955 AC_VERB_SET_POWER_STATE, parm);
2956 snd_hda_codec_write(codec, 0x27, 0,
2957 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002958 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2959 snd_hda_codec_write(codec, 0x25, 0,
2960 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002961}
2962
Lydia Wang518bf3b2009-10-10 19:07:29 +08002963static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002964static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002965{
2966 struct via_spec *spec;
2967 int err;
2968
Lydia Wang518bf3b2009-10-10 19:07:29 +08002969 if (get_codec_type(codec) == VT1708BCE)
2970 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002971
Josepch Chanf7278fd2007-12-13 16:40:40 +01002972 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002973 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002974 if (spec == NULL)
2975 return -ENOMEM;
2976
Takashi Iwai620e2b22011-06-17 17:19:19 +02002977 spec->aa_mix_nid = 0x16;
2978
Josepch Chanf7278fd2007-12-13 16:40:40 +01002979 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002980 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002981 if (err < 0) {
2982 via_free(codec);
2983 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002984 }
2985
Josepch Chanf7278fd2007-12-13 16:40:40 +01002986 codec->patch_ops = via_patch_ops;
2987
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002988 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2989
Josepch Chanf7278fd2007-12-13 16:40:40 +01002990 return 0;
2991}
2992
Harald Welted949cac2008-09-09 15:56:01 +08002993/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002994static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002995 /* Enable Mic Boost Volume backdoor */
2996 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002997 /* don't bybass mixer */
2998 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002999 { }
3000};
3001
Takashi Iwai9da29272009-05-07 16:31:14 +02003002/* fill out digital output widgets; one for master and one for slave outputs */
3003static void fill_dig_outs(struct hda_codec *codec)
3004{
3005 struct via_spec *spec = codec->spec;
3006 int i;
3007
3008 for (i = 0; i < spec->autocfg.dig_outs; i++) {
3009 hda_nid_t nid;
3010 int conn;
3011
3012 nid = spec->autocfg.dig_out_pins[i];
3013 if (!nid)
3014 continue;
3015 conn = snd_hda_get_connections(codec, nid, &nid, 1);
3016 if (conn < 1)
3017 continue;
3018 if (!spec->multiout.dig_out_nid)
3019 spec->multiout.dig_out_nid = nid;
3020 else {
3021 spec->slave_dig_outs[0] = nid;
3022 break; /* at most two dig outs */
3023 }
3024 }
3025}
3026
Takashi Iwai12daef62011-06-18 17:45:49 +02003027static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08003028{
3029 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02003030 hda_nid_t dig_nid;
3031 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08003032
Takashi Iwai12daef62011-06-18 17:45:49 +02003033 if (!spec->autocfg.dig_in_pin)
3034 return;
Harald Welted949cac2008-09-09 15:56:01 +08003035
Takashi Iwai12daef62011-06-18 17:45:49 +02003036 dig_nid = codec->start_nid;
3037 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
3038 unsigned int wcaps = get_wcaps(codec, dig_nid);
3039 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
3040 continue;
3041 if (!(wcaps & AC_WCAP_DIGITAL))
3042 continue;
3043 if (!(wcaps & AC_WCAP_CONN_LIST))
3044 continue;
3045 err = get_connection_index(codec, dig_nid,
3046 spec->autocfg.dig_in_pin);
3047 if (err >= 0) {
3048 spec->dig_in_nid = dig_nid;
3049 break;
3050 }
3051 }
Harald Welted949cac2008-09-09 15:56:01 +08003052}
3053
Lydia Wang6369bcf2009-10-10 19:08:31 +08003054static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
3055 int offset, int num_steps, int step_size)
3056{
3057 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
3058 (offset << AC_AMPCAP_OFFSET_SHIFT) |
3059 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
3060 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
3061 (0 << AC_AMPCAP_MUTE_SHIFT));
3062}
3063
Harald Welted949cac2008-09-09 15:56:01 +08003064static int patch_vt1708S(struct hda_codec *codec)
3065{
3066 struct via_spec *spec;
3067 int err;
3068
3069 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003070 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003071 if (spec == NULL)
3072 return -ENOMEM;
3073
Takashi Iwai620e2b22011-06-17 17:19:19 +02003074 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003075 override_mic_boost(codec, 0x1a, 0, 3, 40);
3076 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003077
Harald Welted949cac2008-09-09 15:56:01 +08003078 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003079 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003080 if (err < 0) {
3081 via_free(codec);
3082 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003083 }
3084
Takashi Iwai096a8852011-06-20 12:09:02 +02003085 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003086
Harald Welted949cac2008-09-09 15:56:01 +08003087 codec->patch_ops = via_patch_ops;
3088
Lydia Wang518bf3b2009-10-10 19:07:29 +08003089 /* correct names for VT1708BCE */
3090 if (get_codec_type(codec) == VT1708BCE) {
3091 kfree(codec->chip_name);
3092 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3093 snprintf(codec->bus->card->mixername,
3094 sizeof(codec->bus->card->mixername),
3095 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08003096 }
Lydia Wangbc92df72011-03-23 17:56:05 +08003097 /* correct names for VT1705 */
3098 if (codec->vendor_id == 0x11064397) {
3099 kfree(codec->chip_name);
3100 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3101 snprintf(codec->bus->card->mixername,
3102 sizeof(codec->bus->card->mixername),
3103 "%s %s", codec->vendor_name, codec->chip_name);
3104 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003105 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08003106 return 0;
3107}
3108
3109/* Patch for VT1702 */
3110
Takashi Iwai096a8852011-06-20 12:09:02 +02003111static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003112 /* mixer enable */
3113 {0x1, 0xF88, 0x3},
3114 /* GPIO 0~2 */
3115 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003116 { }
3117};
3118
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003119static void set_widgets_power_state_vt1702(struct hda_codec *codec)
3120{
3121 int imux_is_smixer =
3122 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3123 unsigned int parm;
3124 /* inputs */
3125 /* PW 1/2/5 (14h/15h/18h) */
3126 parm = AC_PWRST_D3;
3127 set_pin_power_state(codec, 0x14, &parm);
3128 set_pin_power_state(codec, 0x15, &parm);
3129 set_pin_power_state(codec, 0x18, &parm);
3130 if (imux_is_smixer)
3131 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
3132 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
3133 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3134 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
3135 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3136 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
3137
3138 /* outputs */
3139 /* PW 3/4 (16h/17h) */
3140 parm = AC_PWRST_D3;
3141 set_pin_power_state(codec, 0x17, &parm);
3142 set_pin_power_state(codec, 0x16, &parm);
3143 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
3144 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
3145 imux_is_smixer ? AC_PWRST_D0 : parm);
3146 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3147 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3148}
3149
Harald Welted949cac2008-09-09 15:56:01 +08003150static int patch_vt1702(struct hda_codec *codec)
3151{
3152 struct via_spec *spec;
3153 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003154
3155 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003156 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003157 if (spec == NULL)
3158 return -ENOMEM;
3159
Takashi Iwai620e2b22011-06-17 17:19:19 +02003160 spec->aa_mix_nid = 0x1a;
3161
Takashi Iwai12daef62011-06-18 17:45:49 +02003162 /* limit AA path volume to 0 dB */
3163 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3164 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3165 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3166 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3167 (1 << AC_AMPCAP_MUTE_SHIFT));
3168
Harald Welted949cac2008-09-09 15:56:01 +08003169 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003170 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003171 if (err < 0) {
3172 via_free(codec);
3173 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003174 }
3175
Takashi Iwai096a8852011-06-20 12:09:02 +02003176 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003177
Harald Welted949cac2008-09-09 15:56:01 +08003178 codec->patch_ops = via_patch_ops;
3179
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003180 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08003181 return 0;
3182}
3183
Lydia Wangeb7188c2009-10-10 19:08:34 +08003184/* Patch for VT1718S */
3185
Takashi Iwai096a8852011-06-20 12:09:02 +02003186static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08003187 /* Enable MW0 adjust Gain 5 */
3188 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003189 /* Enable Boost Volume backdoor */
3190 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02003191
Lydia Wangeb7188c2009-10-10 19:08:34 +08003192 { }
3193};
3194
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003195static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
3196{
3197 struct via_spec *spec = codec->spec;
3198 int imux_is_smixer;
3199 unsigned int parm;
3200 /* MUX6 (1eh) = stereo mixer */
3201 imux_is_smixer =
3202 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3203 /* inputs */
3204 /* PW 5/6/7 (29h/2ah/2bh) */
3205 parm = AC_PWRST_D3;
3206 set_pin_power_state(codec, 0x29, &parm);
3207 set_pin_power_state(codec, 0x2a, &parm);
3208 set_pin_power_state(codec, 0x2b, &parm);
3209 if (imux_is_smixer)
3210 parm = AC_PWRST_D0;
3211 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
3212 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3213 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3214 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3215 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3216
3217 /* outputs */
3218 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
3219 parm = AC_PWRST_D3;
3220 set_pin_power_state(codec, 0x27, &parm);
3221 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
3222 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
3223
3224 /* PW2 (26h), AOW2 (ah) */
3225 parm = AC_PWRST_D3;
3226 set_pin_power_state(codec, 0x26, &parm);
3227 if (spec->smart51_enabled)
3228 set_pin_power_state(codec, 0x2b, &parm);
3229 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
3230
3231 /* PW0 (24h), AOW0 (8h) */
3232 parm = AC_PWRST_D3;
3233 set_pin_power_state(codec, 0x24, &parm);
3234 if (!spec->hp_independent_mode) /* check for redirected HP */
3235 set_pin_power_state(codec, 0x28, &parm);
3236 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3237 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
3238 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
3239 imux_is_smixer ? AC_PWRST_D0 : parm);
3240
3241 /* PW1 (25h), AOW1 (9h) */
3242 parm = AC_PWRST_D3;
3243 set_pin_power_state(codec, 0x25, &parm);
3244 if (spec->smart51_enabled)
3245 set_pin_power_state(codec, 0x2a, &parm);
3246 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
3247
3248 if (spec->hp_independent_mode) {
3249 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3250 parm = AC_PWRST_D3;
3251 set_pin_power_state(codec, 0x28, &parm);
3252 snd_hda_codec_write(codec, 0x1b, 0,
3253 AC_VERB_SET_POWER_STATE, parm);
3254 snd_hda_codec_write(codec, 0x34, 0,
3255 AC_VERB_SET_POWER_STATE, parm);
3256 snd_hda_codec_write(codec, 0xc, 0,
3257 AC_VERB_SET_POWER_STATE, parm);
3258 }
3259}
3260
Takashi Iwai30b45032011-07-11 17:05:04 +02003261/* Add a connection to the primary DAC from AA-mixer for some codecs
3262 * This isn't listed from the raw info, but the chip has a secret connection.
3263 */
3264static int add_secret_dac_path(struct hda_codec *codec)
3265{
3266 struct via_spec *spec = codec->spec;
3267 int i, nums;
3268 hda_nid_t conn[8];
3269 hda_nid_t nid;
3270
3271 if (!spec->aa_mix_nid)
3272 return 0;
3273 nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
3274 ARRAY_SIZE(conn) - 1);
3275 for (i = 0; i < nums; i++) {
3276 if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
3277 return 0;
3278 }
3279
3280 /* find the primary DAC and add to the connection list */
3281 nid = codec->start_nid;
3282 for (i = 0; i < codec->num_nodes; i++, nid++) {
3283 unsigned int caps = get_wcaps(codec, nid);
3284 if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
3285 !(caps & AC_WCAP_DIGITAL)) {
3286 conn[nums++] = nid;
3287 return snd_hda_override_conn_list(codec,
3288 spec->aa_mix_nid,
3289 nums, conn);
3290 }
3291 }
3292 return 0;
3293}
3294
3295
Lydia Wangeb7188c2009-10-10 19:08:34 +08003296static int patch_vt1718S(struct hda_codec *codec)
3297{
3298 struct via_spec *spec;
3299 int err;
3300
3301 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003302 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003303 if (spec == NULL)
3304 return -ENOMEM;
3305
Takashi Iwai620e2b22011-06-17 17:19:19 +02003306 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003307 override_mic_boost(codec, 0x2b, 0, 3, 40);
3308 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003309 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003310
Lydia Wangeb7188c2009-10-10 19:08:34 +08003311 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003312 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003313 if (err < 0) {
3314 via_free(codec);
3315 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003316 }
3317
Takashi Iwai096a8852011-06-20 12:09:02 +02003318 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003319
Lydia Wangeb7188c2009-10-10 19:08:34 +08003320 codec->patch_ops = via_patch_ops;
3321
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003322 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3323
Lydia Wangeb7188c2009-10-10 19:08:34 +08003324 return 0;
3325}
Lydia Wangf3db4232009-10-10 19:08:41 +08003326
3327/* Patch for VT1716S */
3328
3329static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3330 struct snd_ctl_elem_info *uinfo)
3331{
3332 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3333 uinfo->count = 1;
3334 uinfo->value.integer.min = 0;
3335 uinfo->value.integer.max = 1;
3336 return 0;
3337}
3338
3339static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3340 struct snd_ctl_elem_value *ucontrol)
3341{
3342 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3343 int index = 0;
3344
3345 index = snd_hda_codec_read(codec, 0x26, 0,
3346 AC_VERB_GET_CONNECT_SEL, 0);
3347 if (index != -1)
3348 *ucontrol->value.integer.value = index;
3349
3350 return 0;
3351}
3352
3353static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3354 struct snd_ctl_elem_value *ucontrol)
3355{
3356 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3357 struct via_spec *spec = codec->spec;
3358 int index = *ucontrol->value.integer.value;
3359
3360 snd_hda_codec_write(codec, 0x26, 0,
3361 AC_VERB_SET_CONNECT_SEL, index);
3362 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003363 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003364 return 1;
3365}
3366
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003367static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003368 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3369 {
3370 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3371 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003372 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003373 .count = 1,
3374 .info = vt1716s_dmic_info,
3375 .get = vt1716s_dmic_get,
3376 .put = vt1716s_dmic_put,
3377 },
3378 {} /* end */
3379};
3380
3381
3382/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003383static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003384 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3385 { } /* end */
3386};
3387
Takashi Iwai096a8852011-06-20 12:09:02 +02003388static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003389 /* Enable Boost Volume backdoor */
3390 {0x1, 0xf8a, 0x80},
3391 /* don't bybass mixer */
3392 {0x1, 0xf88, 0xc0},
3393 /* Enable mono output */
3394 {0x1, 0xf90, 0x08},
3395 { }
3396};
3397
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003398static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3399{
3400 struct via_spec *spec = codec->spec;
3401 int imux_is_smixer;
3402 unsigned int parm;
3403 unsigned int mono_out, present;
3404 /* SW0 (17h) = stereo mixer */
3405 imux_is_smixer =
3406 (snd_hda_codec_read(codec, 0x17, 0,
3407 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3408 /* inputs */
3409 /* PW 1/2/5 (1ah/1bh/1eh) */
3410 parm = AC_PWRST_D3;
3411 set_pin_power_state(codec, 0x1a, &parm);
3412 set_pin_power_state(codec, 0x1b, &parm);
3413 set_pin_power_state(codec, 0x1e, &parm);
3414 if (imux_is_smixer)
3415 parm = AC_PWRST_D0;
3416 /* SW0 (17h), AIW0(13h) */
3417 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3418 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3419
3420 parm = AC_PWRST_D3;
3421 set_pin_power_state(codec, 0x1e, &parm);
3422 /* PW11 (22h) */
3423 if (spec->dmic_enabled)
3424 set_pin_power_state(codec, 0x22, &parm);
3425 else
3426 snd_hda_codec_write(codec, 0x22, 0,
3427 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3428
3429 /* SW2(26h), AIW1(14h) */
3430 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3431 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3432
3433 /* outputs */
3434 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3435 parm = AC_PWRST_D3;
3436 set_pin_power_state(codec, 0x19, &parm);
3437 /* Smart 5.1 PW2(1bh) */
3438 if (spec->smart51_enabled)
3439 set_pin_power_state(codec, 0x1b, &parm);
3440 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3441 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3442
3443 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3444 parm = AC_PWRST_D3;
3445 set_pin_power_state(codec, 0x23, &parm);
3446 /* Smart 5.1 PW1(1ah) */
3447 if (spec->smart51_enabled)
3448 set_pin_power_state(codec, 0x1a, &parm);
3449 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3450
3451 /* Smart 5.1 PW5(1eh) */
3452 if (spec->smart51_enabled)
3453 set_pin_power_state(codec, 0x1e, &parm);
3454 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3455
3456 /* Mono out */
3457 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3458 present = snd_hda_jack_detect(codec, 0x1c);
3459
3460 if (present)
3461 mono_out = 0;
3462 else {
3463 present = snd_hda_jack_detect(codec, 0x1d);
3464 if (!spec->hp_independent_mode && present)
3465 mono_out = 0;
3466 else
3467 mono_out = 1;
3468 }
3469 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3470 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3471 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3472 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3473
3474 /* PW 3/4 (1ch/1dh) */
3475 parm = AC_PWRST_D3;
3476 set_pin_power_state(codec, 0x1c, &parm);
3477 set_pin_power_state(codec, 0x1d, &parm);
3478 /* HP Independent Mode, power on AOW3 */
3479 if (spec->hp_independent_mode)
3480 snd_hda_codec_write(codec, 0x25, 0,
3481 AC_VERB_SET_POWER_STATE, parm);
3482
3483 /* force to D0 for internal Speaker */
3484 /* MW0 (16h), AOW0 (10h) */
3485 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3486 imux_is_smixer ? AC_PWRST_D0 : parm);
3487 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3488 mono_out ? AC_PWRST_D0 : parm);
3489}
3490
Lydia Wangf3db4232009-10-10 19:08:41 +08003491static int patch_vt1716S(struct hda_codec *codec)
3492{
3493 struct via_spec *spec;
3494 int err;
3495
3496 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003497 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003498 if (spec == NULL)
3499 return -ENOMEM;
3500
Takashi Iwai620e2b22011-06-17 17:19:19 +02003501 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003502 override_mic_boost(codec, 0x1a, 0, 3, 40);
3503 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003504
Lydia Wangf3db4232009-10-10 19:08:41 +08003505 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003506 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003507 if (err < 0) {
3508 via_free(codec);
3509 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003510 }
3511
Takashi Iwai096a8852011-06-20 12:09:02 +02003512 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003513
Lydia Wangf3db4232009-10-10 19:08:41 +08003514 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3515 spec->num_mixers++;
3516
3517 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3518
3519 codec->patch_ops = via_patch_ops;
3520
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003521 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003522 return 0;
3523}
Lydia Wang25eaba22009-10-10 19:08:43 +08003524
3525/* for vt2002P */
3526
Takashi Iwai096a8852011-06-20 12:09:02 +02003527static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003528 /* Class-D speaker related verbs */
3529 {0x1, 0xfe0, 0x4},
3530 {0x1, 0xfe9, 0x80},
3531 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003532 /* Enable Boost Volume backdoor */
3533 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003534 /* Enable AOW0 to MW9 */
3535 {0x1, 0xfb8, 0x88},
3536 { }
3537};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003538
Takashi Iwai096a8852011-06-20 12:09:02 +02003539static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003540 /* Enable Boost Volume backdoor */
3541 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003542 /* Enable AOW0 to MW9 */
3543 {0x1, 0xfb8, 0x88},
3544 { }
3545};
Lydia Wang25eaba22009-10-10 19:08:43 +08003546
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003547static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3548{
3549 struct via_spec *spec = codec->spec;
3550 int imux_is_smixer;
3551 unsigned int parm;
3552 unsigned int present;
3553 /* MUX9 (1eh) = stereo mixer */
3554 imux_is_smixer =
3555 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3556 /* inputs */
3557 /* PW 5/6/7 (29h/2ah/2bh) */
3558 parm = AC_PWRST_D3;
3559 set_pin_power_state(codec, 0x29, &parm);
3560 set_pin_power_state(codec, 0x2a, &parm);
3561 set_pin_power_state(codec, 0x2b, &parm);
3562 parm = AC_PWRST_D0;
3563 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3564 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3565 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3566 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3567 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3568
3569 /* outputs */
3570 /* AOW0 (8h)*/
3571 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3572
Lydia Wang118909562011-03-23 17:57:34 +08003573 if (spec->codec_type == VT1802) {
3574 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3575 parm = AC_PWRST_D3;
3576 set_pin_power_state(codec, 0x28, &parm);
3577 snd_hda_codec_write(codec, 0x18, 0,
3578 AC_VERB_SET_POWER_STATE, parm);
3579 snd_hda_codec_write(codec, 0x38, 0,
3580 AC_VERB_SET_POWER_STATE, parm);
3581 } else {
3582 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3583 parm = AC_PWRST_D3;
3584 set_pin_power_state(codec, 0x26, &parm);
3585 snd_hda_codec_write(codec, 0x1c, 0,
3586 AC_VERB_SET_POWER_STATE, parm);
3587 snd_hda_codec_write(codec, 0x37, 0,
3588 AC_VERB_SET_POWER_STATE, parm);
3589 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003590
Lydia Wang118909562011-03-23 17:57:34 +08003591 if (spec->codec_type == VT1802) {
3592 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3593 parm = AC_PWRST_D3;
3594 set_pin_power_state(codec, 0x25, &parm);
3595 snd_hda_codec_write(codec, 0x15, 0,
3596 AC_VERB_SET_POWER_STATE, parm);
3597 snd_hda_codec_write(codec, 0x35, 0,
3598 AC_VERB_SET_POWER_STATE, parm);
3599 } else {
3600 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3601 parm = AC_PWRST_D3;
3602 set_pin_power_state(codec, 0x25, &parm);
3603 snd_hda_codec_write(codec, 0x19, 0,
3604 AC_VERB_SET_POWER_STATE, parm);
3605 snd_hda_codec_write(codec, 0x35, 0,
3606 AC_VERB_SET_POWER_STATE, parm);
3607 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003608
3609 if (spec->hp_independent_mode)
3610 snd_hda_codec_write(codec, 0x9, 0,
3611 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3612
3613 /* Class-D */
3614 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3615 present = snd_hda_jack_detect(codec, 0x25);
3616
3617 parm = AC_PWRST_D3;
3618 set_pin_power_state(codec, 0x24, &parm);
3619 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003620 if (spec->codec_type == VT1802)
3621 snd_hda_codec_write(codec, 0x14, 0,
3622 AC_VERB_SET_POWER_STATE, parm);
3623 else
3624 snd_hda_codec_write(codec, 0x18, 0,
3625 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003626 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3627
3628 /* Mono Out */
3629 present = snd_hda_jack_detect(codec, 0x26);
3630
3631 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003632 if (spec->codec_type == VT1802) {
3633 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3634 snd_hda_codec_write(codec, 0x33, 0,
3635 AC_VERB_SET_POWER_STATE, parm);
3636 snd_hda_codec_write(codec, 0x1c, 0,
3637 AC_VERB_SET_POWER_STATE, parm);
3638 snd_hda_codec_write(codec, 0x3c, 0,
3639 AC_VERB_SET_POWER_STATE, parm);
3640 } else {
3641 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3642 snd_hda_codec_write(codec, 0x31, 0,
3643 AC_VERB_SET_POWER_STATE, parm);
3644 snd_hda_codec_write(codec, 0x17, 0,
3645 AC_VERB_SET_POWER_STATE, parm);
3646 snd_hda_codec_write(codec, 0x3b, 0,
3647 AC_VERB_SET_POWER_STATE, parm);
3648 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003649 /* MW9 (21h) */
3650 if (imux_is_smixer || !is_aa_path_mute(codec))
3651 snd_hda_codec_write(codec, 0x21, 0,
3652 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3653 else
3654 snd_hda_codec_write(codec, 0x21, 0,
3655 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3656}
Lydia Wang25eaba22009-10-10 19:08:43 +08003657
3658/* patch for vt2002P */
3659static int patch_vt2002P(struct hda_codec *codec)
3660{
3661 struct via_spec *spec;
3662 int err;
3663
3664 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003665 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003666 if (spec == NULL)
3667 return -ENOMEM;
3668
Takashi Iwai620e2b22011-06-17 17:19:19 +02003669 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003670 override_mic_boost(codec, 0x2b, 0, 3, 40);
3671 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003672 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003673
Lydia Wang25eaba22009-10-10 19:08:43 +08003674 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003675 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003676 if (err < 0) {
3677 via_free(codec);
3678 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003679 }
3680
Lydia Wang118909562011-03-23 17:57:34 +08003681 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003682 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003683 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003684 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003685
Lydia Wang25eaba22009-10-10 19:08:43 +08003686 codec->patch_ops = via_patch_ops;
3687
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003688 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003689 return 0;
3690}
Lydia Wangab6734e2009-10-10 19:08:46 +08003691
3692/* for vt1812 */
3693
Takashi Iwai096a8852011-06-20 12:09:02 +02003694static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003695 /* Enable Boost Volume backdoor */
3696 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003697 /* Enable AOW0 to MW9 */
3698 {0x1, 0xfb8, 0xa8},
3699 { }
3700};
3701
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003702static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3703{
3704 struct via_spec *spec = codec->spec;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003705 unsigned int parm;
3706 unsigned int present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003707 /* inputs */
3708 /* PW 5/6/7 (29h/2ah/2bh) */
3709 parm = AC_PWRST_D3;
3710 set_pin_power_state(codec, 0x29, &parm);
3711 set_pin_power_state(codec, 0x2a, &parm);
3712 set_pin_power_state(codec, 0x2b, &parm);
3713 parm = AC_PWRST_D0;
3714 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3715 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3716 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3717 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3718 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3719
3720 /* outputs */
3721 /* AOW0 (8h)*/
3722 snd_hda_codec_write(codec, 0x8, 0,
3723 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3724
3725 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3726 parm = AC_PWRST_D3;
3727 set_pin_power_state(codec, 0x28, &parm);
3728 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3729 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3730
3731 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3732 parm = AC_PWRST_D3;
3733 set_pin_power_state(codec, 0x25, &parm);
3734 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3735 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3736 if (spec->hp_independent_mode)
3737 snd_hda_codec_write(codec, 0x9, 0,
3738 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3739
3740 /* Internal Speaker */
3741 /* PW0 (24h), MW0(14h), MUX0(34h) */
3742 present = snd_hda_jack_detect(codec, 0x25);
3743
3744 parm = AC_PWRST_D3;
3745 set_pin_power_state(codec, 0x24, &parm);
3746 if (present) {
3747 snd_hda_codec_write(codec, 0x14, 0,
3748 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3749 snd_hda_codec_write(codec, 0x34, 0,
3750 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3751 } else {
3752 snd_hda_codec_write(codec, 0x14, 0,
3753 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3754 snd_hda_codec_write(codec, 0x34, 0,
3755 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3756 }
3757
3758
3759 /* Mono Out */
3760 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3761 present = snd_hda_jack_detect(codec, 0x28);
3762
3763 parm = AC_PWRST_D3;
3764 set_pin_power_state(codec, 0x31, &parm);
3765 if (present) {
3766 snd_hda_codec_write(codec, 0x1c, 0,
3767 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3768 snd_hda_codec_write(codec, 0x3c, 0,
3769 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3770 snd_hda_codec_write(codec, 0x3e, 0,
3771 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3772 } else {
3773 snd_hda_codec_write(codec, 0x1c, 0,
3774 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3775 snd_hda_codec_write(codec, 0x3c, 0,
3776 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3777 snd_hda_codec_write(codec, 0x3e, 0,
3778 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3779 }
3780
3781 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3782 parm = AC_PWRST_D3;
3783 set_pin_power_state(codec, 0x33, &parm);
3784 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3785 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3786
3787}
Lydia Wangab6734e2009-10-10 19:08:46 +08003788
3789/* patch for vt1812 */
3790static int patch_vt1812(struct hda_codec *codec)
3791{
3792 struct via_spec *spec;
3793 int err;
3794
3795 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003796 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003797 if (spec == NULL)
3798 return -ENOMEM;
3799
Takashi Iwai620e2b22011-06-17 17:19:19 +02003800 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003801 override_mic_boost(codec, 0x2b, 0, 3, 40);
3802 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003803 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003804
Lydia Wangab6734e2009-10-10 19:08:46 +08003805 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003806 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003807 if (err < 0) {
3808 via_free(codec);
3809 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003810 }
3811
Takashi Iwai096a8852011-06-20 12:09:02 +02003812 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003813
Lydia Wangab6734e2009-10-10 19:08:46 +08003814 codec->patch_ops = via_patch_ops;
3815
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003816 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003817 return 0;
3818}
3819
Joseph Chanc577b8a2006-11-29 15:29:40 +01003820/*
3821 * patch entries
3822 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003823static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003824 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3825 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3826 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3827 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3828 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003829 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003830 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003831 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003832 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003833 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003834 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003835 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003836 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003837 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003838 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003839 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003840 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003841 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003842 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003843 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003844 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003845 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003846 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003847 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003848 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003849 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003850 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003851 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003852 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003853 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003854 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003855 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003856 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003857 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003858 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003859 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003860 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003861 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003862 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003863 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003864 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003865 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003866 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003867 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003868 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003869 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003870 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003871 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003872 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003873 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003874 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003875 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003876 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003877 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003878 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003879 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003880 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003881 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003882 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003883 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003884 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003885 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003886 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003887 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003888 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003889 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003890 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003891 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003892 { .id = 0x11060428, .name = "VT1718S",
3893 .patch = patch_vt1718S},
3894 { .id = 0x11064428, .name = "VT1718S",
3895 .patch = patch_vt1718S},
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08003896 { .id = 0x11060441, .name = "VT2020",
3897 .patch = patch_vt1718S},
3898 { .id = 0x11064441, .name = "VT1828S",
3899 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003900 { .id = 0x11060433, .name = "VT1716S",
3901 .patch = patch_vt1716S},
3902 { .id = 0x1106a721, .name = "VT1716S",
3903 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003904 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3905 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003906 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003907 { .id = 0x11060440, .name = "VT1818S",
3908 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003909 { .id = 0x11060446, .name = "VT1802",
3910 .patch = patch_vt2002P},
3911 { .id = 0x11068446, .name = "VT1802",
3912 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003913 {} /* terminator */
3914};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003915
3916MODULE_ALIAS("snd-hda-codec-id:1106*");
3917
3918static struct hda_codec_preset_list via_list = {
3919 .preset = snd_hda_preset_via,
3920 .owner = THIS_MODULE,
3921};
3922
3923MODULE_LICENSE("GPL");
3924MODULE_DESCRIPTION("VIA HD-audio codec");
3925
3926static int __init patch_via_init(void)
3927{
3928 return snd_hda_add_codec_preset(&via_list);
3929}
3930
3931static void __exit patch_via_exit(void)
3932{
3933 snd_hda_delete_codec_preset(&via_list);
3934}
3935
3936module_init(patch_via_init)
3937module_exit(patch_via_exit)