blob: 852939658ddbd8f4f37be12a75328a34880d7903 [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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Wangbb3c6bfc2009-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-06-21 15:57:44 +0200541 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200542 return;
Takashi Iwai09a9ad692011-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 Iwai09a9ad692011-06-21 15:57:44 +0200576 if (spec->hp_independent_mode) {
Takashi Iwai09a9ad692011-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 Iwai09a9ad692011-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 */
Takashi Iwai01a61e12011-10-28 00:03:22 +02001503
1504 err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
1505 if (err < 0)
1506 return err;
1507
Joseph Chanc577b8a2006-11-29 15:29:40 +01001508 return 0;
1509}
1510
1511static int via_build_pcms(struct hda_codec *codec)
1512{
1513 struct via_spec *spec = codec->spec;
1514 struct hda_pcm *info = spec->pcm_rec;
1515
Takashi Iwaia5973102011-09-28 16:43:36 +02001516 codec->num_pcms = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001517 codec->pcm_info = info;
1518
Takashi Iwaia5973102011-09-28 16:43:36 +02001519 if (spec->multiout.num_dacs || spec->num_adc_nids) {
1520 snprintf(spec->stream_name_analog,
1521 sizeof(spec->stream_name_analog),
1522 "%s Analog", codec->chip_name);
1523 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001524
Takashi Iwaia5973102011-09-28 16:43:36 +02001525 if (spec->multiout.num_dacs) {
1526 if (!spec->stream_analog_playback)
1527 spec->stream_analog_playback =
1528 &via_pcm_analog_playback;
1529 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1530 *spec->stream_analog_playback;
1531 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1532 spec->multiout.dac_nids[0];
1533 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1534 spec->multiout.max_channels;
1535 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001536
Takashi Iwaia5973102011-09-28 16:43:36 +02001537 if (!spec->stream_analog_capture) {
1538 if (spec->dyn_adc_switch)
1539 spec->stream_analog_capture =
1540 &via_pcm_dyn_adc_analog_capture;
1541 else
1542 spec->stream_analog_capture =
1543 &via_pcm_analog_capture;
1544 }
1545 if (spec->num_adc_nids) {
1546 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1547 *spec->stream_analog_capture;
1548 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1549 spec->adc_nids[0];
1550 if (!spec->dyn_adc_switch)
1551 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1552 spec->num_adc_nids;
1553 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001554 codec->num_pcms++;
1555 info++;
Takashi Iwaia5973102011-09-28 16:43:36 +02001556 }
1557
1558 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
Takashi Iwai82673bc2011-06-17 16:24:21 +02001559 snprintf(spec->stream_name_digital,
1560 sizeof(spec->stream_name_digital),
1561 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001562 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001563 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001564 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001565 if (!spec->stream_digital_playback)
1566 spec->stream_digital_playback =
1567 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001568 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001569 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001570 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1571 spec->multiout.dig_out_nid;
1572 }
1573 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001574 if (!spec->stream_digital_capture)
1575 spec->stream_digital_capture =
1576 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001577 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001578 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001579 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1580 spec->dig_in_nid;
1581 }
Takashi Iwaia5973102011-09-28 16:43:36 +02001582 codec->num_pcms++;
1583 info++;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001584 }
1585
Takashi Iwaiece8d042011-06-19 16:24:21 +02001586 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001587 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1588 "%s HP", codec->chip_name);
1589 info->name = spec->stream_name_hp;
1590 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1591 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001592 spec->hp_dac_nid;
Takashi Iwaia5973102011-09-28 16:43:36 +02001593 codec->num_pcms++;
1594 info++;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001595 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001596 return 0;
1597}
1598
1599static void via_free(struct hda_codec *codec)
1600{
1601 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001602
1603 if (!spec)
1604 return;
1605
Takashi Iwai603c4012008-07-30 15:01:44 +02001606 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001607 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001608 kfree(spec->bind_cap_vol);
1609 kfree(spec->bind_cap_sw);
1610 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001611}
1612
Takashi Iwai64be2852011-06-17 16:51:39 +02001613/* mute/unmute outputs */
1614static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1615 hda_nid_t *pins, bool mute)
1616{
1617 int i;
Takashi Iwai94994732011-07-11 11:36:44 +02001618 for (i = 0; i < num_pins; i++) {
1619 unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
1620 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1621 if (parm & AC_PINCTL_IN_EN)
1622 continue;
1623 if (mute)
1624 parm &= ~AC_PINCTL_OUT_EN;
1625 else
1626 parm |= AC_PINCTL_OUT_EN;
Takashi Iwai64be2852011-06-17 16:51:39 +02001627 snd_hda_codec_write(codec, pins[i], 0,
Takashi Iwai94994732011-07-11 11:36:44 +02001628 AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
1629 }
Takashi Iwai64be2852011-06-17 16:51:39 +02001630}
1631
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001632/* mute internal speaker if line-out is plugged */
1633static void via_line_automute(struct hda_codec *codec, int present)
1634{
1635 struct via_spec *spec = codec->spec;
1636
1637 if (!spec->autocfg.speaker_outs)
1638 return;
1639 if (!present)
1640 present = snd_hda_jack_detect(codec,
1641 spec->autocfg.line_out_pins[0]);
1642 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1643 spec->autocfg.speaker_pins,
1644 present);
1645}
1646
Harald Welte69e52a82008-09-09 15:57:32 +08001647/* mute internal speaker if HP is plugged */
1648static void via_hp_automute(struct hda_codec *codec)
1649{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001650 int present = 0;
Takashi Iwai6e969d92011-07-11 11:28:13 +02001651 int nums;
Harald Welte69e52a82008-09-09 15:57:32 +08001652 struct via_spec *spec = codec->spec;
1653
Takashi Iwai6e969d92011-07-11 11:28:13 +02001654 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0])
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001655 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai6e969d92011-07-11 11:28:13 +02001656
1657 if (spec->smart51_enabled)
1658 nums = spec->autocfg.line_outs + spec->smart51_nums;
1659 else
1660 nums = spec->autocfg.line_outs;
1661 toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
1662
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001663 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001664}
1665
Harald Welte69e52a82008-09-09 15:57:32 +08001666static void via_gpio_control(struct hda_codec *codec)
1667{
1668 unsigned int gpio_data;
1669 unsigned int vol_counter;
1670 unsigned int vol;
1671 unsigned int master_vol;
1672
1673 struct via_spec *spec = codec->spec;
1674
1675 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1676 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1677
1678 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1679 0xF84, 0) & 0x3F0000) >> 16;
1680
1681 vol = vol_counter & 0x1F;
1682 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1683 AC_VERB_GET_AMP_GAIN_MUTE,
1684 AC_AMP_GET_INPUT);
1685
1686 if (gpio_data == 0x02) {
1687 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001688 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1689 AC_VERB_SET_PIN_WIDGET_CONTROL,
1690 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001691 if (vol_counter & 0x20) {
1692 /* decrease volume */
1693 if (vol > master_vol)
1694 vol = master_vol;
1695 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1696 0, HDA_AMP_VOLMASK,
1697 master_vol-vol);
1698 } else {
1699 /* increase volume */
1700 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1701 HDA_AMP_VOLMASK,
1702 ((master_vol+vol) > 0x2A) ? 0x2A :
1703 (master_vol+vol));
1704 }
1705 } else if (!(gpio_data & 0x02)) {
1706 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001707 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1708 AC_VERB_SET_PIN_WIDGET_CONTROL,
1709 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001710 }
1711}
1712
1713/* unsolicited event for jack sensing */
1714static void via_unsol_event(struct hda_codec *codec,
1715 unsigned int res)
1716{
Takashi Iwai1835a0f2011-10-27 22:12:46 +02001717 snd_hda_jack_set_dirty_all(codec); /* FIXME: to be more fine-grained */
1718
Harald Welte69e52a82008-09-09 15:57:32 +08001719 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001720
Lydia Wanga34df192009-10-10 19:08:01 +08001721 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001722 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001723
1724 res &= ~VIA_JACK_EVENT;
1725
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001726 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001727 via_hp_automute(codec);
1728 else if (res == VIA_GPIO_EVENT)
1729 via_gpio_control(codec);
Takashi Iwai01a61e12011-10-28 00:03:22 +02001730 snd_hda_jack_report_sync(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001731}
1732
Takashi Iwai2a439522011-07-26 09:52:50 +02001733#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001734static int via_suspend(struct hda_codec *codec, pm_message_t state)
1735{
1736 struct via_spec *spec = codec->spec;
1737 vt1708_stop_hp_work(spec);
1738 return 0;
1739}
1740#endif
1741
Takashi Iwaicb53c622007-08-10 17:21:45 +02001742#ifdef CONFIG_SND_HDA_POWER_SAVE
1743static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1744{
1745 struct via_spec *spec = codec->spec;
1746 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1747}
1748#endif
1749
Joseph Chanc577b8a2006-11-29 15:29:40 +01001750/*
1751 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001752
1753static int via_init(struct hda_codec *codec);
1754
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001755static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001756 .build_controls = via_build_controls,
1757 .build_pcms = via_build_pcms,
1758 .init = via_init,
1759 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001760 .unsol_event = via_unsol_event,
Takashi Iwai2a439522011-07-26 09:52:50 +02001761#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001762 .suspend = via_suspend,
1763#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001764#ifdef CONFIG_SND_HDA_POWER_SAVE
1765 .check_power_status = via_check_power_status,
1766#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001767};
1768
Takashi Iwai4a796162011-06-17 17:53:38 +02001769static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001770{
Takashi Iwai4a796162011-06-17 17:53:38 +02001771 struct via_spec *spec = codec->spec;
1772 int i;
1773
1774 for (i = 0; i < spec->multiout.num_dacs; i++) {
1775 if (spec->multiout.dac_nids[i] == dac)
1776 return false;
1777 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001778 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001779 return false;
1780 return true;
1781}
1782
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001783static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001784 hda_nid_t target_dac, int with_aa_mix,
1785 struct nid_path *path, int depth)
Takashi Iwai4a796162011-06-17 17:53:38 +02001786{
Takashi Iwai3214b962011-07-18 12:49:25 +02001787 struct via_spec *spec = codec->spec;
Takashi Iwai4a796162011-06-17 17:53:38 +02001788 hda_nid_t conn[8];
1789 int i, nums;
1790
Takashi Iwai3214b962011-07-18 12:49:25 +02001791 if (nid == spec->aa_mix_nid) {
1792 if (!with_aa_mix)
1793 return false;
1794 with_aa_mix = 2; /* mark aa-mix is included */
1795 }
1796
Takashi Iwai4a796162011-06-17 17:53:38 +02001797 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1798 for (i = 0; i < nums; i++) {
1799 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1800 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001801 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1802 /* aa-mix is requested but not included? */
1803 if (!(spec->aa_mix_nid && with_aa_mix == 1))
1804 goto found;
1805 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001806 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001807 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001808 return false;
1809 for (i = 0; i < nums; i++) {
1810 unsigned int type;
1811 type = get_wcaps_type(get_wcaps(codec, conn[i]));
Takashi Iwai3214b962011-07-18 12:49:25 +02001812 if (type == AC_WID_AUD_OUT)
Takashi Iwai4a796162011-06-17 17:53:38 +02001813 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001814 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai3214b962011-07-18 12:49:25 +02001815 with_aa_mix, path, depth + 1))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001816 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001817 }
1818 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001819
1820 found:
1821 path->path[path->depth] = conn[i];
1822 path->idx[path->depth] = i;
1823 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1824 path->multi[path->depth] = 1;
1825 path->depth++;
1826 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001827}
1828
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001829static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001830 hda_nid_t target_dac, int with_aa_mix,
1831 struct nid_path *path)
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001832{
Takashi Iwai3214b962011-07-18 12:49:25 +02001833 if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001834 path->path[path->depth] = nid;
1835 path->depth++;
Takashi Iwai3214b962011-07-18 12:49:25 +02001836 snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
1837 path->depth, path->path[0], path->path[1],
1838 path->path[2], path->path[3], path->path[4]);
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001839 return true;
1840 }
1841 return false;
1842}
1843
Takashi Iwai4a796162011-06-17 17:53:38 +02001844static int via_auto_fill_dac_nids(struct hda_codec *codec)
1845{
1846 struct via_spec *spec = codec->spec;
1847 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001848 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001849 hda_nid_t nid;
1850
Joseph Chanc577b8a2006-11-29 15:29:40 +01001851 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001852 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001853 for (i = 0; i < cfg->line_outs; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001854 hda_nid_t dac = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001855 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001856 if (!nid)
1857 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001858 if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
1859 dac = spec->out_path[i].path[0];
1860 if (!i && parse_output_path(codec, nid, dac, 1,
1861 &spec->out_mix_path))
1862 dac = spec->out_mix_path.path[0];
1863 if (dac) {
1864 spec->private_dac_nids[i] = dac;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001865 dac_num++;
1866 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001867 }
Takashi Iwai3214b962011-07-18 12:49:25 +02001868 if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
1869 spec->out_path[0] = spec->out_mix_path;
1870 spec->out_mix_path.depth = 0;
1871 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001872 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001873 return 0;
1874}
1875
Takashi Iwai4a796162011-06-17 17:53:38 +02001876static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001877 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001878{
Takashi Iwai4a796162011-06-17 17:53:38 +02001879 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001880 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001881 hda_nid_t dac, pin, sel, nid;
1882 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001883
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001884 dac = check_dac ? path->path[0] : 0;
1885 pin = path->path[path->depth - 1];
1886 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001887
Takashi Iwai8df2a312011-06-21 11:48:29 +02001888 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001889 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001890 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001891 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001892 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1893 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001894 else
1895 nid = 0;
1896 if (nid) {
1897 sprintf(name, "%s Playback Volume", pfx);
1898 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001899 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001900 if (err < 0)
1901 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001902 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001903 }
1904
Takashi Iwai8df2a312011-06-21 11:48:29 +02001905 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001906 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001907 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001908 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001909 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1910 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001911 else
1912 nid = 0;
1913 if (nid) {
1914 sprintf(name, "%s Playback Switch", pfx);
1915 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1916 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1917 if (err < 0)
1918 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001919 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001920 }
1921 return 0;
1922}
1923
Takashi Iwaif4a78282011-06-17 18:46:48 +02001924static void mangle_smart51(struct hda_codec *codec)
1925{
1926 struct via_spec *spec = codec->spec;
1927 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001928 struct auto_pin_cfg_item *ins = cfg->inputs;
1929 int i, j, nums, attr;
1930 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001931
Takashi Iwai0f98c242011-06-21 12:51:33 +02001932 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1933 nums = 0;
1934 for (i = 0; i < cfg->num_inputs; i++) {
1935 unsigned int def;
1936 if (ins[i].type > AUTO_PIN_LINE_IN)
1937 continue;
1938 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1939 if (snd_hda_get_input_pin_attr(def) != attr)
1940 continue;
1941 for (j = 0; j < nums; j++)
1942 if (ins[pins[j]].type < ins[i].type) {
1943 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001944 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001945 break;
1946 }
1947 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001948 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001949 }
1950 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001951 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001952 for (i = 0; i < nums; i++) {
1953 hda_nid_t pin = ins[pins[i]].pin;
1954 spec->smart51_pins[spec->smart51_nums++] = pin;
1955 cfg->line_out_pins[cfg->line_outs++] = pin;
1956 if (cfg->line_outs == 3)
1957 break;
1958 }
1959 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001960 }
1961}
1962
Takashi Iwai020066d2011-07-21 13:45:56 +02001963static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src)
1964{
1965 dst->vol_ctl = src->vol_ctl;
1966 dst->mute_ctl = src->mute_ctl;
1967}
1968
Takashi Iwai4a796162011-06-17 17:53:38 +02001969/* add playback controls from the parsed DAC table */
1970static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1971{
1972 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001973 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai3214b962011-07-18 12:49:25 +02001974 struct nid_path *path;
Takashi Iwaiea734962011-01-17 11:29:34 +01001975 static const char * const chname[4] = {
1976 "Front", "Surround", "C/LFE", "Side"
1977 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001978 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001979 int old_line_outs;
1980
1981 /* check smart51 */
1982 old_line_outs = cfg->line_outs;
1983 if (cfg->line_outs == 1)
1984 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001985
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001986 err = via_auto_fill_dac_nids(codec);
1987 if (err < 0)
1988 return err;
1989
Lydia Wang5c9a5612011-07-08 14:03:43 +08001990 if (spec->multiout.num_dacs < 3) {
1991 spec->smart51_nums = 0;
1992 cfg->line_outs = old_line_outs;
1993 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001994 for (i = 0; i < cfg->line_outs; i++) {
1995 hda_nid_t pin, dac;
1996 pin = cfg->line_out_pins[i];
1997 dac = spec->multiout.dac_nids[i];
1998 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001999 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02002000 path = spec->out_path + i;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02002001 if (i == HDA_CLFE) {
Takashi Iwai3214b962011-07-18 12:49:25 +02002002 err = create_ch_ctls(codec, "Center", 1, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002003 if (err < 0)
2004 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002005 err = create_ch_ctls(codec, "LFE", 2, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002006 if (err < 0)
2007 return err;
2008 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02002009 const char *pfx = chname[i];
2010 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
2011 cfg->line_outs == 1)
2012 pfx = "Speaker";
Takashi Iwai3214b962011-07-18 12:49:25 +02002013 err = create_ch_ctls(codec, pfx, 3, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002014 if (err < 0)
2015 return err;
2016 }
Takashi Iwai020066d2011-07-21 13:45:56 +02002017 if (path != spec->out_path + i)
2018 copy_path_mixer_ctls(&spec->out_path[i], path);
2019 if (path == spec->out_path && spec->out_mix_path.depth)
2020 copy_path_mixer_ctls(&spec->out_mix_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002021 }
2022
Takashi Iwai4a796162011-06-17 17:53:38 +02002023 idx = get_connection_index(codec, spec->aa_mix_nid,
2024 spec->multiout.dac_nids[0]);
2025 if (idx >= 0) {
2026 /* add control to mixer */
Takashi Iwai3214b962011-07-18 12:49:25 +02002027 const char *name;
2028 name = spec->out_mix_path.depth ?
2029 "PCM Loopback Playback Volume" : "PCM Playback Volume";
2030 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002031 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2032 idx, HDA_INPUT));
2033 if (err < 0)
2034 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002035 name = spec->out_mix_path.depth ?
2036 "PCM Loopback Playback Switch" : "PCM Playback Switch";
2037 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002038 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2039 idx, HDA_INPUT));
2040 if (err < 0)
2041 return err;
2042 }
2043
Takashi Iwaif4a78282011-06-17 18:46:48 +02002044 cfg->line_outs = old_line_outs;
2045
Joseph Chanc577b8a2006-11-29 15:29:40 +01002046 return 0;
2047}
2048
Takashi Iwai4a796162011-06-17 17:53:38 +02002049static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002050{
Takashi Iwai4a796162011-06-17 17:53:38 +02002051 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002052 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002053 bool check_dac;
Takashi Iwai3214b962011-07-18 12:49:25 +02002054 int i, err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002055
2056 if (!pin)
2057 return 0;
2058
Takashi Iwai3214b962011-07-18 12:49:25 +02002059 if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
2060 for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
2061 if (i < spec->multiout.num_dacs &&
2062 parse_output_path(codec, pin,
2063 spec->multiout.dac_nids[i], 0,
2064 &spec->hp_indep_path)) {
2065 spec->hp_indep_shared = i;
2066 break;
2067 }
2068 }
Takashi Iwai25250502011-06-30 17:24:47 +02002069 }
Takashi Iwai3214b962011-07-18 12:49:25 +02002070 if (spec->hp_indep_path.depth) {
2071 spec->hp_dac_nid = spec->hp_indep_path.path[0];
2072 if (!spec->hp_indep_shared)
2073 spec->hp_path = spec->hp_indep_path;
2074 }
2075 /* optionally check front-path w/o AA-mix */
2076 if (!spec->hp_path.depth)
2077 parse_output_path(codec, pin,
2078 spec->multiout.dac_nids[HDA_FRONT], 0,
2079 &spec->hp_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002080
Takashi Iwaiece8d042011-06-19 16:24:21 +02002081 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai3214b962011-07-18 12:49:25 +02002082 1, &spec->hp_mix_path) && !spec->hp_path.depth)
Takashi Iwaiece8d042011-06-19 16:24:21 +02002083 return 0;
2084
Takashi Iwai3214b962011-07-18 12:49:25 +02002085 if (spec->hp_path.depth) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002086 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002087 check_dac = true;
2088 } else {
Takashi Iwai3214b962011-07-18 12:49:25 +02002089 path = &spec->hp_mix_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002090 check_dac = false;
2091 }
2092 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002093 if (err < 0)
2094 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002095 if (check_dac)
2096 copy_path_mixer_ctls(&spec->hp_mix_path, path);
2097 else
2098 copy_path_mixer_ctls(&spec->hp_path, path);
2099 if (spec->hp_indep_path.depth)
2100 copy_path_mixer_ctls(&spec->hp_indep_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002101 return 0;
2102}
2103
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002104static int via_auto_create_speaker_ctls(struct hda_codec *codec)
2105{
2106 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +02002107 struct nid_path *path;
2108 bool check_dac;
Wang Shaoyan81c0a782011-08-05 18:51:29 +08002109 hda_nid_t pin, dac = 0;
Takashi Iwai3214b962011-07-18 12:49:25 +02002110 int err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002111
2112 pin = spec->autocfg.speaker_pins[0];
2113 if (!spec->autocfg.speaker_outs || !pin)
2114 return 0;
2115
Takashi Iwai3214b962011-07-18 12:49:25 +02002116 if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02002117 dac = spec->speaker_path.path[0];
Takashi Iwai3214b962011-07-18 12:49:25 +02002118 if (!dac)
2119 parse_output_path(codec, pin,
2120 spec->multiout.dac_nids[HDA_FRONT], 0,
2121 &spec->speaker_path);
2122 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
2123 1, &spec->speaker_mix_path) && !dac)
2124 return 0;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002125
Takashi Iwai3214b962011-07-18 12:49:25 +02002126 /* no AA-path for front? */
2127 if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
2128 dac = 0;
2129
2130 spec->speaker_dac_nid = dac;
2131 spec->multiout.extra_out_nid[0] = dac;
2132 if (dac) {
2133 path = &spec->speaker_path;
2134 check_dac = true;
2135 } else {
2136 path = &spec->speaker_mix_path;
2137 check_dac = false;
2138 }
2139 err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
2140 if (err < 0)
2141 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002142 if (check_dac)
2143 copy_path_mixer_ctls(&spec->speaker_mix_path, path);
2144 else
2145 copy_path_mixer_ctls(&spec->speaker_path, path);
Takashi Iwai3214b962011-07-18 12:49:25 +02002146 return 0;
2147}
2148
2149#define via_aamix_ctl_info via_pin_power_ctl_info
2150
2151static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol,
2152 struct snd_ctl_elem_value *ucontrol)
2153{
2154 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2155 struct via_spec *spec = codec->spec;
2156 ucontrol->value.enumerated.item[0] = spec->aamix_mode;
2157 return 0;
2158}
2159
2160static void update_aamix_paths(struct hda_codec *codec, int do_mix,
2161 struct nid_path *nomix, struct nid_path *mix)
2162{
2163 if (do_mix) {
2164 activate_output_path(codec, nomix, false, false);
2165 activate_output_path(codec, mix, true, false);
2166 } else {
2167 activate_output_path(codec, mix, false, false);
2168 activate_output_path(codec, nomix, true, false);
2169 }
2170}
2171
2172static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol,
2173 struct snd_ctl_elem_value *ucontrol)
2174{
2175 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2176 struct via_spec *spec = codec->spec;
2177 unsigned int val = ucontrol->value.enumerated.item[0];
2178
2179 if (val == spec->aamix_mode)
2180 return 0;
2181 spec->aamix_mode = val;
2182 /* update front path */
2183 update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
2184 /* update HP path */
2185 if (!spec->hp_independent_mode) {
2186 update_aamix_paths(codec, val, &spec->hp_path,
2187 &spec->hp_mix_path);
2188 }
2189 /* update speaker path */
2190 update_aamix_paths(codec, val, &spec->speaker_path,
2191 &spec->speaker_mix_path);
2192 return 1;
2193}
2194
2195static const struct snd_kcontrol_new via_aamix_ctl_enum = {
2196 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2197 .name = "Loopback Mixing",
2198 .info = via_aamix_ctl_info,
2199 .get = via_aamix_ctl_get,
2200 .put = via_aamix_ctl_put,
2201};
2202
2203static int via_auto_create_loopback_switch(struct hda_codec *codec)
2204{
2205 struct via_spec *spec = codec->spec;
2206
2207 if (!spec->aa_mix_nid || !spec->out_mix_path.depth)
2208 return 0; /* no loopback switching available */
2209 if (!via_clone_control(spec, &via_aamix_ctl_enum))
2210 return -ENOMEM;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002211 return 0;
2212}
2213
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002214/* look for ADCs */
2215static int via_fill_adcs(struct hda_codec *codec)
2216{
2217 struct via_spec *spec = codec->spec;
2218 hda_nid_t nid = codec->start_nid;
2219 int i;
2220
2221 for (i = 0; i < codec->num_nodes; i++, nid++) {
2222 unsigned int wcaps = get_wcaps(codec, nid);
2223 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2224 continue;
2225 if (wcaps & AC_WCAP_DIGITAL)
2226 continue;
2227 if (!(wcaps & AC_WCAP_CONN_LIST))
2228 continue;
2229 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2230 return -ENOMEM;
2231 spec->adc_nids[spec->num_adc_nids++] = nid;
2232 }
2233 return 0;
2234}
2235
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002236/* input-src control */
2237static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2238 struct snd_ctl_elem_info *uinfo)
2239{
2240 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2241 struct via_spec *spec = codec->spec;
2242
2243 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2244 uinfo->count = 1;
2245 uinfo->value.enumerated.items = spec->num_inputs;
2246 if (uinfo->value.enumerated.item >= spec->num_inputs)
2247 uinfo->value.enumerated.item = spec->num_inputs - 1;
2248 strcpy(uinfo->value.enumerated.name,
2249 spec->inputs[uinfo->value.enumerated.item].label);
2250 return 0;
2251}
2252
2253static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2254 struct snd_ctl_elem_value *ucontrol)
2255{
2256 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2257 struct via_spec *spec = codec->spec;
2258 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2259
2260 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2261 return 0;
2262}
2263
2264static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2265 struct snd_ctl_elem_value *ucontrol)
2266{
2267 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2268 struct via_spec *spec = codec->spec;
2269 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2270 hda_nid_t mux;
2271 int cur;
2272
2273 cur = ucontrol->value.enumerated.item[0];
2274 if (cur < 0 || cur >= spec->num_inputs)
2275 return -EINVAL;
2276 if (spec->cur_mux[idx] == cur)
2277 return 0;
2278 spec->cur_mux[idx] = cur;
2279 if (spec->dyn_adc_switch) {
2280 int adc_idx = spec->inputs[cur].adc_idx;
2281 mux = spec->mux_nids[adc_idx];
2282 via_dyn_adc_pcm_resetup(codec, cur);
2283 } else {
2284 mux = spec->mux_nids[idx];
2285 if (snd_BUG_ON(!mux))
2286 return -EINVAL;
2287 }
2288
2289 if (mux) {
2290 /* switch to D0 beofre change index */
2291 if (snd_hda_codec_read(codec, mux, 0,
2292 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2293 snd_hda_codec_write(codec, mux, 0,
2294 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2295 snd_hda_codec_write(codec, mux, 0,
2296 AC_VERB_SET_CONNECT_SEL,
2297 spec->inputs[cur].mux_idx);
2298 }
2299
2300 /* update jack power state */
2301 set_widgets_power_state(codec);
2302 return 0;
2303}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002304
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002305static const struct snd_kcontrol_new via_input_src_ctl = {
2306 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2307 /* The multiple "Capture Source" controls confuse alsamixer
2308 * So call somewhat different..
2309 */
2310 /* .name = "Capture Source", */
2311 .name = "Input Source",
2312 .info = via_mux_enum_info,
2313 .get = via_mux_enum_get,
2314 .put = via_mux_enum_put,
2315};
2316
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002317static int create_input_src_ctls(struct hda_codec *codec, int count)
2318{
2319 struct via_spec *spec = codec->spec;
2320 struct snd_kcontrol_new *knew;
2321
2322 if (spec->num_inputs <= 1 || !count)
2323 return 0; /* no need for single src */
2324
2325 knew = via_clone_control(spec, &via_input_src_ctl);
2326 if (!knew)
2327 return -ENOMEM;
2328 knew->count = count;
2329 return 0;
2330}
2331
2332/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002333static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2334{
2335 struct hda_amp_list *list;
2336
2337 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2338 return;
2339 list = spec->loopback_list + spec->num_loopbacks;
2340 list->nid = mix;
2341 list->dir = HDA_INPUT;
2342 list->idx = idx;
2343 spec->num_loopbacks++;
2344 spec->loopback.amplist = spec->loopback_list;
2345}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002346
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002347static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002348 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002349{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002350 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002351}
2352
2353/* add the input-route to the given pin */
2354static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002355{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002356 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002357 int c, idx;
2358
2359 spec->inputs[spec->num_inputs].adc_idx = -1;
2360 spec->inputs[spec->num_inputs].pin = pin;
2361 for (c = 0; c < spec->num_adc_nids; c++) {
2362 if (spec->mux_nids[c]) {
2363 idx = get_connection_index(codec, spec->mux_nids[c],
2364 pin);
2365 if (idx < 0)
2366 continue;
2367 spec->inputs[spec->num_inputs].mux_idx = idx;
2368 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002369 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002370 continue;
2371 }
2372 spec->inputs[spec->num_inputs].adc_idx = c;
2373 /* Can primary ADC satisfy all inputs? */
2374 if (!spec->dyn_adc_switch &&
2375 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2376 snd_printd(KERN_INFO
2377 "via: dynamic ADC switching enabled\n");
2378 spec->dyn_adc_switch = 1;
2379 }
2380 return true;
2381 }
2382 return false;
2383}
2384
2385static int get_mux_nids(struct hda_codec *codec);
2386
2387/* parse input-routes; fill ADCs, MUXs and input-src entries */
2388static int parse_analog_inputs(struct hda_codec *codec)
2389{
2390 struct via_spec *spec = codec->spec;
2391 const struct auto_pin_cfg *cfg = &spec->autocfg;
2392 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002393
2394 err = via_fill_adcs(codec);
2395 if (err < 0)
2396 return err;
2397 err = get_mux_nids(codec);
2398 if (err < 0)
2399 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002400
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002401 /* fill all input-routes */
2402 for (i = 0; i < cfg->num_inputs; i++) {
2403 if (add_input_route(codec, cfg->inputs[i].pin))
2404 spec->inputs[spec->num_inputs++].label =
2405 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002406 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002407
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002408 /* check for internal loopback recording */
2409 if (spec->aa_mix_nid &&
2410 add_input_route(codec, spec->aa_mix_nid))
2411 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2412
2413 return 0;
2414}
2415
2416/* create analog-loopback volume/switch controls */
2417static int create_loopback_ctls(struct hda_codec *codec)
2418{
2419 struct via_spec *spec = codec->spec;
2420 const struct auto_pin_cfg *cfg = &spec->autocfg;
2421 const char *prev_label = NULL;
2422 int type_idx = 0;
2423 int i, j, err, idx;
2424
2425 if (!spec->aa_mix_nid)
2426 return 0;
2427
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002428 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002429 hda_nid_t pin = cfg->inputs[i].pin;
2430 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2431
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002432 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002433 type_idx++;
2434 else
2435 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002436 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002437 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2438 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002439 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002440 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002441 if (err < 0)
2442 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002443 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002444 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002445
2446 /* remember the label for smart51 control */
2447 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002448 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002449 spec->smart51_idxs[j] = idx;
2450 spec->smart51_labels[j] = label;
2451 break;
2452 }
2453 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002454 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002455 return 0;
2456}
2457
2458/* create mic-boost controls (if present) */
2459static int create_mic_boost_ctls(struct hda_codec *codec)
2460{
2461 struct via_spec *spec = codec->spec;
2462 const struct auto_pin_cfg *cfg = &spec->autocfg;
2463 int i, err;
2464
2465 for (i = 0; i < cfg->num_inputs; i++) {
2466 hda_nid_t pin = cfg->inputs[i].pin;
2467 unsigned int caps;
2468 const char *label;
2469 char name[32];
2470
2471 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2472 continue;
2473 caps = query_amp_caps(codec, pin, HDA_INPUT);
2474 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2475 continue;
2476 label = hda_get_autocfg_input_label(codec, cfg, i);
2477 snprintf(name, sizeof(name), "%s Boost Volume", label);
2478 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2479 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2480 if (err < 0)
2481 return err;
2482 }
2483 return 0;
2484}
2485
2486/* create capture and input-src controls for multiple streams */
2487static int create_multi_adc_ctls(struct hda_codec *codec)
2488{
2489 struct via_spec *spec = codec->spec;
2490 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002491
2492 /* create capture mixer elements */
2493 for (i = 0; i < spec->num_adc_nids; i++) {
2494 hda_nid_t adc = spec->adc_nids[i];
2495 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2496 "Capture Volume", i,
2497 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2498 HDA_INPUT));
2499 if (err < 0)
2500 return err;
2501 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2502 "Capture Switch", i,
2503 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2504 HDA_INPUT));
2505 if (err < 0)
2506 return err;
2507 }
2508
2509 /* input-source control */
2510 for (i = 0; i < spec->num_adc_nids; i++)
2511 if (!spec->mux_nids[i])
2512 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002513 err = create_input_src_ctls(codec, i);
2514 if (err < 0)
2515 return err;
2516 return 0;
2517}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002518
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002519/* bind capture volume/switch */
2520static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2521 HDA_BIND_VOL("Capture Volume", 0);
2522static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2523 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002524
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002525static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2526 struct hda_ctl_ops *ops)
2527{
2528 struct hda_bind_ctls *ctl;
2529 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002530
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002531 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2532 if (!ctl)
2533 return -ENOMEM;
2534 ctl->ops = ops;
2535 for (i = 0; i < spec->num_adc_nids; i++)
2536 ctl->values[i] =
2537 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2538 *ctl_ret = ctl;
2539 return 0;
2540}
2541
2542/* create capture and input-src controls for dynamic ADC-switch case */
2543static int create_dyn_adc_ctls(struct hda_codec *codec)
2544{
2545 struct via_spec *spec = codec->spec;
2546 struct snd_kcontrol_new *knew;
2547 int err;
2548
2549 /* set up the bind capture ctls */
2550 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2551 if (err < 0)
2552 return err;
2553 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2554 if (err < 0)
2555 return err;
2556
2557 /* create capture mixer elements */
2558 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2559 if (!knew)
2560 return -ENOMEM;
2561 knew->private_value = (long)spec->bind_cap_vol;
2562
2563 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2564 if (!knew)
2565 return -ENOMEM;
2566 knew->private_value = (long)spec->bind_cap_sw;
2567
2568 /* input-source control */
2569 err = create_input_src_ctls(codec, 1);
2570 if (err < 0)
2571 return err;
2572 return 0;
2573}
2574
2575/* parse and create capture-related stuff */
2576static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2577{
2578 struct via_spec *spec = codec->spec;
2579 int err;
2580
2581 err = parse_analog_inputs(codec);
2582 if (err < 0)
2583 return err;
2584 if (spec->dyn_adc_switch)
2585 err = create_dyn_adc_ctls(codec);
2586 else
2587 err = create_multi_adc_ctls(codec);
2588 if (err < 0)
2589 return err;
2590 err = create_loopback_ctls(codec);
2591 if (err < 0)
2592 return err;
2593 err = create_mic_boost_ctls(codec);
2594 if (err < 0)
2595 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002596 return 0;
2597}
2598
Harald Welte76d9b0d2008-09-09 15:50:37 +08002599static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2600{
2601 unsigned int def_conf;
2602 unsigned char seqassoc;
2603
Takashi Iwai2f334f92009-02-20 14:37:42 +01002604 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002605 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2606 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002607 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2608 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2609 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2610 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002611 }
2612
2613 return;
2614}
2615
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002616static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002617 struct snd_ctl_elem_value *ucontrol)
2618{
2619 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2620 struct via_spec *spec = codec->spec;
2621
2622 if (spec->codec_type != VT1708)
2623 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002624 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002625 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002626 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002627 return 0;
2628}
2629
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002630static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002631 struct snd_ctl_elem_value *ucontrol)
2632{
2633 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2634 struct via_spec *spec = codec->spec;
2635 int change;
2636
2637 if (spec->codec_type != VT1708)
2638 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002639 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002640 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002641 == !spec->vt1708_jack_detect;
2642 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002643 mute_aa_path(codec, 1);
2644 notify_aa_path_ctls(codec);
2645 }
2646 return change;
2647}
2648
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002649static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2650 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2651 .name = "Jack Detect",
2652 .count = 1,
2653 .info = snd_ctl_boolean_mono_info,
2654 .get = vt1708_jack_detect_get,
2655 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002656};
2657
Takashi Iwai12daef62011-06-18 17:45:49 +02002658static void fill_dig_outs(struct hda_codec *codec);
2659static void fill_dig_in(struct hda_codec *codec);
2660
2661static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002662{
2663 struct via_spec *spec = codec->spec;
2664 int err;
2665
2666 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2667 if (err < 0)
2668 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002669 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002670 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002671
Takashi Iwai4a796162011-06-17 17:53:38 +02002672 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002673 if (err < 0)
2674 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002675 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002676 if (err < 0)
2677 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002678 err = via_auto_create_speaker_ctls(codec);
2679 if (err < 0)
2680 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002681 err = via_auto_create_loopback_switch(codec);
2682 if (err < 0)
2683 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002684 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002685 if (err < 0)
2686 return err;
2687
2688 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2689
Takashi Iwai12daef62011-06-18 17:45:49 +02002690 fill_dig_outs(codec);
2691 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002692
Takashi Iwai603c4012008-07-30 15:01:44 +02002693 if (spec->kctls.list)
2694 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002695
Joseph Chanc577b8a2006-11-29 15:29:40 +01002696
Takashi Iwai3214b962011-07-18 12:49:25 +02002697 if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002698 err = via_hp_build(codec);
2699 if (err < 0)
2700 return err;
2701 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002702
Takashi Iwaif4a78282011-06-17 18:46:48 +02002703 err = via_smart51_build(codec);
2704 if (err < 0)
2705 return err;
2706
Takashi Iwai5d417622011-06-20 11:32:27 +02002707 /* assign slave outs */
2708 if (spec->slave_dig_outs[0])
2709 codec->slave_dig_outs = spec->slave_dig_outs;
2710
Joseph Chanc577b8a2006-11-29 15:29:40 +01002711 return 1;
2712}
2713
Takashi Iwai5d417622011-06-20 11:32:27 +02002714static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002715{
Lydia Wang25eaba22009-10-10 19:08:43 +08002716 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002717 if (spec->multiout.dig_out_nid)
2718 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2719 if (spec->slave_dig_outs[0])
2720 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2721}
Lydia Wang25eaba22009-10-10 19:08:43 +08002722
Takashi Iwai5d417622011-06-20 11:32:27 +02002723static void via_auto_init_dig_in(struct hda_codec *codec)
2724{
2725 struct via_spec *spec = codec->spec;
2726 if (!spec->dig_in_nid)
2727 return;
2728 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2729 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2730}
2731
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002732/* initialize the unsolicited events */
2733static void via_auto_init_unsol_event(struct hda_codec *codec)
2734{
2735 struct via_spec *spec = codec->spec;
2736 struct auto_pin_cfg *cfg = &spec->autocfg;
2737 unsigned int ev;
2738 int i;
2739
2740 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002741 snd_hda_jack_detect_enable(codec, cfg->hp_pins[0],
2742 VIA_HP_EVENT | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002743
2744 if (cfg->speaker_pins[0])
2745 ev = VIA_LINE_EVENT;
2746 else
2747 ev = 0;
2748 for (i = 0; i < cfg->line_outs; i++) {
2749 if (cfg->line_out_pins[i] &&
2750 is_jack_detectable(codec, cfg->line_out_pins[i]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002751 snd_hda_jack_detect_enable(codec, cfg->line_out_pins[i],
2752 ev | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002753 }
2754
2755 for (i = 0; i < cfg->num_inputs; i++) {
2756 if (is_jack_detectable(codec, cfg->inputs[i].pin))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002757 snd_hda_jack_detect_enable(codec, cfg->inputs[i].pin,
2758 VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002759 }
2760}
2761
Takashi Iwai5d417622011-06-20 11:32:27 +02002762static int via_init(struct hda_codec *codec)
2763{
2764 struct via_spec *spec = codec->spec;
2765 int i;
2766
2767 for (i = 0; i < spec->num_iverbs; i++)
2768 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2769
Joseph Chanc577b8a2006-11-29 15:29:40 +01002770 via_auto_init_multi_out(codec);
2771 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002772 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002773 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002774 via_auto_init_dig_outs(codec);
2775 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002776
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002777 via_auto_init_unsol_event(codec);
2778
2779 via_hp_automute(codec);
Takashi Iwai01a61e12011-10-28 00:03:22 +02002780 snd_hda_jack_report_sync(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002781
Joseph Chanc577b8a2006-11-29 15:29:40 +01002782 return 0;
2783}
2784
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002785static void vt1708_update_hp_jack_state(struct work_struct *work)
2786{
2787 struct via_spec *spec = container_of(work, struct via_spec,
2788 vt1708_hp_work.work);
2789 if (spec->codec_type != VT1708)
2790 return;
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002791 snd_hda_jack_set_dirty_all(spec->codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002792 /* if jack state toggled */
2793 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002794 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002795 spec->vt1708_hp_present ^= 1;
2796 via_hp_automute(spec->codec);
2797 }
2798 vt1708_start_hp_work(spec);
2799}
2800
Takashi Iwai337b9d02009-07-07 18:18:59 +02002801static int get_mux_nids(struct hda_codec *codec)
2802{
2803 struct via_spec *spec = codec->spec;
2804 hda_nid_t nid, conn[8];
2805 unsigned int type;
2806 int i, n;
2807
2808 for (i = 0; i < spec->num_adc_nids; i++) {
2809 nid = spec->adc_nids[i];
2810 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002811 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002812 if (type == AC_WID_PIN)
2813 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002814 n = snd_hda_get_connections(codec, nid, conn,
2815 ARRAY_SIZE(conn));
2816 if (n <= 0)
2817 break;
2818 if (n > 1) {
2819 spec->mux_nids[i] = nid;
2820 break;
2821 }
2822 nid = conn[0];
2823 }
2824 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002825 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002826}
2827
Joseph Chanc577b8a2006-11-29 15:29:40 +01002828static int patch_vt1708(struct hda_codec *codec)
2829{
2830 struct via_spec *spec;
2831 int err;
2832
2833 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002834 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002835 if (spec == NULL)
2836 return -ENOMEM;
2837
Takashi Iwai620e2b22011-06-17 17:19:19 +02002838 spec->aa_mix_nid = 0x17;
2839
Takashi Iwai12daef62011-06-18 17:45:49 +02002840 /* Add HP and CD pin config connect bit re-config action */
2841 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2842 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2843
Joseph Chanc577b8a2006-11-29 15:29:40 +01002844 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002845 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002846 if (err < 0) {
2847 via_free(codec);
2848 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002849 }
2850
Takashi Iwai12daef62011-06-18 17:45:49 +02002851 /* add jack detect on/off control */
2852 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2853 return -ENOMEM;
2854
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002855 /* disable 32bit format on VT1708 */
2856 if (codec->vendor_id == 0x11061708)
2857 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002858
Lydia Wange322a362011-06-29 13:52:02 +08002859 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2860
Joseph Chanc577b8a2006-11-29 15:29:40 +01002861 codec->patch_ops = via_patch_ops;
2862
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002863 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002864 return 0;
2865}
2866
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002867static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002868{
2869 struct via_spec *spec;
2870 int err;
2871
2872 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002873 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002874 if (spec == NULL)
2875 return -ENOMEM;
2876
Takashi Iwai620e2b22011-06-17 17:19:19 +02002877 spec->aa_mix_nid = 0x18;
2878
Takashi Iwai12daef62011-06-18 17:45:49 +02002879 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002880 if (err < 0) {
2881 via_free(codec);
2882 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002883 }
2884
Joseph Chanc577b8a2006-11-29 15:29:40 +01002885 codec->patch_ops = via_patch_ops;
2886
Josepch Chanf7278fd2007-12-13 16:40:40 +01002887 return 0;
2888}
2889
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002890static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2891{
2892 struct via_spec *spec = codec->spec;
2893 int imux_is_smixer;
2894 unsigned int parm;
2895 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002896 if ((spec->codec_type != VT1708B_4CH) &&
2897 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002898 is_8ch = 1;
2899
2900 /* SW0 (17h) = stereo mixer */
2901 imux_is_smixer =
2902 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2903 == ((spec->codec_type == VT1708S) ? 5 : 0));
2904 /* inputs */
2905 /* PW 1/2/5 (1ah/1bh/1eh) */
2906 parm = AC_PWRST_D3;
2907 set_pin_power_state(codec, 0x1a, &parm);
2908 set_pin_power_state(codec, 0x1b, &parm);
2909 set_pin_power_state(codec, 0x1e, &parm);
2910 if (imux_is_smixer)
2911 parm = AC_PWRST_D0;
2912 /* SW0 (17h), AIW 0/1 (13h/14h) */
2913 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2914 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2915 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2916
2917 /* outputs */
2918 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2919 parm = AC_PWRST_D3;
2920 set_pin_power_state(codec, 0x19, &parm);
2921 if (spec->smart51_enabled)
2922 set_pin_power_state(codec, 0x1b, &parm);
2923 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2924 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2925
2926 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2927 if (is_8ch) {
2928 parm = AC_PWRST_D3;
2929 set_pin_power_state(codec, 0x22, &parm);
2930 if (spec->smart51_enabled)
2931 set_pin_power_state(codec, 0x1a, &parm);
2932 snd_hda_codec_write(codec, 0x26, 0,
2933 AC_VERB_SET_POWER_STATE, parm);
2934 snd_hda_codec_write(codec, 0x24, 0,
2935 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002936 } else if (codec->vendor_id == 0x11064397) {
2937 /* PW7(23h), SW2(27h), AOW2(25h) */
2938 parm = AC_PWRST_D3;
2939 set_pin_power_state(codec, 0x23, &parm);
2940 if (spec->smart51_enabled)
2941 set_pin_power_state(codec, 0x1a, &parm);
2942 snd_hda_codec_write(codec, 0x27, 0,
2943 AC_VERB_SET_POWER_STATE, parm);
2944 snd_hda_codec_write(codec, 0x25, 0,
2945 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002946 }
2947
2948 /* PW 3/4/7 (1ch/1dh/23h) */
2949 parm = AC_PWRST_D3;
2950 /* force to D0 for internal Speaker */
2951 set_pin_power_state(codec, 0x1c, &parm);
2952 set_pin_power_state(codec, 0x1d, &parm);
2953 if (is_8ch)
2954 set_pin_power_state(codec, 0x23, &parm);
2955
2956 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2957 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2958 imux_is_smixer ? AC_PWRST_D0 : parm);
2959 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2960 if (is_8ch) {
2961 snd_hda_codec_write(codec, 0x25, 0,
2962 AC_VERB_SET_POWER_STATE, parm);
2963 snd_hda_codec_write(codec, 0x27, 0,
2964 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002965 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2966 snd_hda_codec_write(codec, 0x25, 0,
2967 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002968}
2969
Lydia Wang518bf3b2009-10-10 19:07:29 +08002970static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002971static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002972{
2973 struct via_spec *spec;
2974 int err;
2975
Lydia Wang518bf3b2009-10-10 19:07:29 +08002976 if (get_codec_type(codec) == VT1708BCE)
2977 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002978
Josepch Chanf7278fd2007-12-13 16:40:40 +01002979 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002980 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002981 if (spec == NULL)
2982 return -ENOMEM;
2983
Takashi Iwai620e2b22011-06-17 17:19:19 +02002984 spec->aa_mix_nid = 0x16;
2985
Josepch Chanf7278fd2007-12-13 16:40:40 +01002986 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002987 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002988 if (err < 0) {
2989 via_free(codec);
2990 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002991 }
2992
Josepch Chanf7278fd2007-12-13 16:40:40 +01002993 codec->patch_ops = via_patch_ops;
2994
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002995 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2996
Josepch Chanf7278fd2007-12-13 16:40:40 +01002997 return 0;
2998}
2999
Harald Welted949cac2008-09-09 15:56:01 +08003000/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02003001static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08003002 /* Enable Mic Boost Volume backdoor */
3003 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003004 /* don't bybass mixer */
3005 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08003006 { }
3007};
3008
Takashi Iwai9da29272009-05-07 16:31:14 +02003009/* fill out digital output widgets; one for master and one for slave outputs */
3010static void fill_dig_outs(struct hda_codec *codec)
3011{
3012 struct via_spec *spec = codec->spec;
3013 int i;
3014
3015 for (i = 0; i < spec->autocfg.dig_outs; i++) {
3016 hda_nid_t nid;
3017 int conn;
3018
3019 nid = spec->autocfg.dig_out_pins[i];
3020 if (!nid)
3021 continue;
3022 conn = snd_hda_get_connections(codec, nid, &nid, 1);
3023 if (conn < 1)
3024 continue;
3025 if (!spec->multiout.dig_out_nid)
3026 spec->multiout.dig_out_nid = nid;
3027 else {
3028 spec->slave_dig_outs[0] = nid;
3029 break; /* at most two dig outs */
3030 }
3031 }
3032}
3033
Takashi Iwai12daef62011-06-18 17:45:49 +02003034static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08003035{
3036 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02003037 hda_nid_t dig_nid;
3038 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08003039
Takashi Iwai12daef62011-06-18 17:45:49 +02003040 if (!spec->autocfg.dig_in_pin)
3041 return;
Harald Welted949cac2008-09-09 15:56:01 +08003042
Takashi Iwai12daef62011-06-18 17:45:49 +02003043 dig_nid = codec->start_nid;
3044 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
3045 unsigned int wcaps = get_wcaps(codec, dig_nid);
3046 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
3047 continue;
3048 if (!(wcaps & AC_WCAP_DIGITAL))
3049 continue;
3050 if (!(wcaps & AC_WCAP_CONN_LIST))
3051 continue;
3052 err = get_connection_index(codec, dig_nid,
3053 spec->autocfg.dig_in_pin);
3054 if (err >= 0) {
3055 spec->dig_in_nid = dig_nid;
3056 break;
3057 }
3058 }
Harald Welted949cac2008-09-09 15:56:01 +08003059}
3060
Lydia Wang6369bcf2009-10-10 19:08:31 +08003061static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
3062 int offset, int num_steps, int step_size)
3063{
3064 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
3065 (offset << AC_AMPCAP_OFFSET_SHIFT) |
3066 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
3067 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
3068 (0 << AC_AMPCAP_MUTE_SHIFT));
3069}
3070
Harald Welted949cac2008-09-09 15:56:01 +08003071static int patch_vt1708S(struct hda_codec *codec)
3072{
3073 struct via_spec *spec;
3074 int err;
3075
3076 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003077 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003078 if (spec == NULL)
3079 return -ENOMEM;
3080
Takashi Iwai620e2b22011-06-17 17:19:19 +02003081 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003082 override_mic_boost(codec, 0x1a, 0, 3, 40);
3083 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003084
Harald Welted949cac2008-09-09 15:56:01 +08003085 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003086 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003087 if (err < 0) {
3088 via_free(codec);
3089 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003090 }
3091
Takashi Iwai096a8852011-06-20 12:09:02 +02003092 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003093
Harald Welted949cac2008-09-09 15:56:01 +08003094 codec->patch_ops = via_patch_ops;
3095
Lydia Wang518bf3b2009-10-10 19:07:29 +08003096 /* correct names for VT1708BCE */
3097 if (get_codec_type(codec) == VT1708BCE) {
3098 kfree(codec->chip_name);
3099 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3100 snprintf(codec->bus->card->mixername,
3101 sizeof(codec->bus->card->mixername),
3102 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08003103 }
Lydia Wangbc92df72011-03-23 17:56:05 +08003104 /* correct names for VT1705 */
3105 if (codec->vendor_id == 0x11064397) {
3106 kfree(codec->chip_name);
3107 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3108 snprintf(codec->bus->card->mixername,
3109 sizeof(codec->bus->card->mixername),
3110 "%s %s", codec->vendor_name, codec->chip_name);
3111 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003112 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08003113 return 0;
3114}
3115
3116/* Patch for VT1702 */
3117
Takashi Iwai096a8852011-06-20 12:09:02 +02003118static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003119 /* mixer enable */
3120 {0x1, 0xF88, 0x3},
3121 /* GPIO 0~2 */
3122 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003123 { }
3124};
3125
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003126static void set_widgets_power_state_vt1702(struct hda_codec *codec)
3127{
3128 int imux_is_smixer =
3129 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3130 unsigned int parm;
3131 /* inputs */
3132 /* PW 1/2/5 (14h/15h/18h) */
3133 parm = AC_PWRST_D3;
3134 set_pin_power_state(codec, 0x14, &parm);
3135 set_pin_power_state(codec, 0x15, &parm);
3136 set_pin_power_state(codec, 0x18, &parm);
3137 if (imux_is_smixer)
3138 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
3139 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
3140 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3141 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
3142 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3143 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
3144
3145 /* outputs */
3146 /* PW 3/4 (16h/17h) */
3147 parm = AC_PWRST_D3;
3148 set_pin_power_state(codec, 0x17, &parm);
3149 set_pin_power_state(codec, 0x16, &parm);
3150 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
3151 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
3152 imux_is_smixer ? AC_PWRST_D0 : parm);
3153 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3154 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3155}
3156
Harald Welted949cac2008-09-09 15:56:01 +08003157static int patch_vt1702(struct hda_codec *codec)
3158{
3159 struct via_spec *spec;
3160 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003161
3162 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003163 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003164 if (spec == NULL)
3165 return -ENOMEM;
3166
Takashi Iwai620e2b22011-06-17 17:19:19 +02003167 spec->aa_mix_nid = 0x1a;
3168
Takashi Iwai12daef62011-06-18 17:45:49 +02003169 /* limit AA path volume to 0 dB */
3170 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3171 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3172 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3173 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3174 (1 << AC_AMPCAP_MUTE_SHIFT));
3175
Harald Welted949cac2008-09-09 15:56:01 +08003176 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003177 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003178 if (err < 0) {
3179 via_free(codec);
3180 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003181 }
3182
Takashi Iwai096a8852011-06-20 12:09:02 +02003183 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003184
Harald Welted949cac2008-09-09 15:56:01 +08003185 codec->patch_ops = via_patch_ops;
3186
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003187 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08003188 return 0;
3189}
3190
Lydia Wangeb7188c2009-10-10 19:08:34 +08003191/* Patch for VT1718S */
3192
Takashi Iwai096a8852011-06-20 12:09:02 +02003193static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08003194 /* Enable MW0 adjust Gain 5 */
3195 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003196 /* Enable Boost Volume backdoor */
3197 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02003198
Lydia Wangeb7188c2009-10-10 19:08:34 +08003199 { }
3200};
3201
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003202static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
3203{
3204 struct via_spec *spec = codec->spec;
3205 int imux_is_smixer;
3206 unsigned int parm;
3207 /* MUX6 (1eh) = stereo mixer */
3208 imux_is_smixer =
3209 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3210 /* inputs */
3211 /* PW 5/6/7 (29h/2ah/2bh) */
3212 parm = AC_PWRST_D3;
3213 set_pin_power_state(codec, 0x29, &parm);
3214 set_pin_power_state(codec, 0x2a, &parm);
3215 set_pin_power_state(codec, 0x2b, &parm);
3216 if (imux_is_smixer)
3217 parm = AC_PWRST_D0;
3218 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
3219 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3220 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3221 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3222 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3223
3224 /* outputs */
3225 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
3226 parm = AC_PWRST_D3;
3227 set_pin_power_state(codec, 0x27, &parm);
3228 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
3229 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
3230
3231 /* PW2 (26h), AOW2 (ah) */
3232 parm = AC_PWRST_D3;
3233 set_pin_power_state(codec, 0x26, &parm);
3234 if (spec->smart51_enabled)
3235 set_pin_power_state(codec, 0x2b, &parm);
3236 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
3237
3238 /* PW0 (24h), AOW0 (8h) */
3239 parm = AC_PWRST_D3;
3240 set_pin_power_state(codec, 0x24, &parm);
3241 if (!spec->hp_independent_mode) /* check for redirected HP */
3242 set_pin_power_state(codec, 0x28, &parm);
3243 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3244 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
3245 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
3246 imux_is_smixer ? AC_PWRST_D0 : parm);
3247
3248 /* PW1 (25h), AOW1 (9h) */
3249 parm = AC_PWRST_D3;
3250 set_pin_power_state(codec, 0x25, &parm);
3251 if (spec->smart51_enabled)
3252 set_pin_power_state(codec, 0x2a, &parm);
3253 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
3254
3255 if (spec->hp_independent_mode) {
3256 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3257 parm = AC_PWRST_D3;
3258 set_pin_power_state(codec, 0x28, &parm);
3259 snd_hda_codec_write(codec, 0x1b, 0,
3260 AC_VERB_SET_POWER_STATE, parm);
3261 snd_hda_codec_write(codec, 0x34, 0,
3262 AC_VERB_SET_POWER_STATE, parm);
3263 snd_hda_codec_write(codec, 0xc, 0,
3264 AC_VERB_SET_POWER_STATE, parm);
3265 }
3266}
3267
Takashi Iwai30b45032011-07-11 17:05:04 +02003268/* Add a connection to the primary DAC from AA-mixer for some codecs
3269 * This isn't listed from the raw info, but the chip has a secret connection.
3270 */
3271static int add_secret_dac_path(struct hda_codec *codec)
3272{
3273 struct via_spec *spec = codec->spec;
3274 int i, nums;
3275 hda_nid_t conn[8];
3276 hda_nid_t nid;
3277
3278 if (!spec->aa_mix_nid)
3279 return 0;
3280 nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
3281 ARRAY_SIZE(conn) - 1);
3282 for (i = 0; i < nums; i++) {
3283 if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
3284 return 0;
3285 }
3286
3287 /* find the primary DAC and add to the connection list */
3288 nid = codec->start_nid;
3289 for (i = 0; i < codec->num_nodes; i++, nid++) {
3290 unsigned int caps = get_wcaps(codec, nid);
3291 if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
3292 !(caps & AC_WCAP_DIGITAL)) {
3293 conn[nums++] = nid;
3294 return snd_hda_override_conn_list(codec,
3295 spec->aa_mix_nid,
3296 nums, conn);
3297 }
3298 }
3299 return 0;
3300}
3301
3302
Lydia Wangeb7188c2009-10-10 19:08:34 +08003303static int patch_vt1718S(struct hda_codec *codec)
3304{
3305 struct via_spec *spec;
3306 int err;
3307
3308 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003309 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003310 if (spec == NULL)
3311 return -ENOMEM;
3312
Takashi Iwai620e2b22011-06-17 17:19:19 +02003313 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003314 override_mic_boost(codec, 0x2b, 0, 3, 40);
3315 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003316 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003317
Lydia Wangeb7188c2009-10-10 19:08:34 +08003318 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003319 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003320 if (err < 0) {
3321 via_free(codec);
3322 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003323 }
3324
Takashi Iwai096a8852011-06-20 12:09:02 +02003325 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003326
Lydia Wangeb7188c2009-10-10 19:08:34 +08003327 codec->patch_ops = via_patch_ops;
3328
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003329 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3330
Lydia Wangeb7188c2009-10-10 19:08:34 +08003331 return 0;
3332}
Lydia Wangf3db4232009-10-10 19:08:41 +08003333
3334/* Patch for VT1716S */
3335
3336static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3337 struct snd_ctl_elem_info *uinfo)
3338{
3339 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3340 uinfo->count = 1;
3341 uinfo->value.integer.min = 0;
3342 uinfo->value.integer.max = 1;
3343 return 0;
3344}
3345
3346static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3347 struct snd_ctl_elem_value *ucontrol)
3348{
3349 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3350 int index = 0;
3351
3352 index = snd_hda_codec_read(codec, 0x26, 0,
3353 AC_VERB_GET_CONNECT_SEL, 0);
3354 if (index != -1)
3355 *ucontrol->value.integer.value = index;
3356
3357 return 0;
3358}
3359
3360static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3361 struct snd_ctl_elem_value *ucontrol)
3362{
3363 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3364 struct via_spec *spec = codec->spec;
3365 int index = *ucontrol->value.integer.value;
3366
3367 snd_hda_codec_write(codec, 0x26, 0,
3368 AC_VERB_SET_CONNECT_SEL, index);
3369 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003370 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003371 return 1;
3372}
3373
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003374static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003375 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3376 {
3377 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3378 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003379 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003380 .count = 1,
3381 .info = vt1716s_dmic_info,
3382 .get = vt1716s_dmic_get,
3383 .put = vt1716s_dmic_put,
3384 },
3385 {} /* end */
3386};
3387
3388
3389/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003390static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003391 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3392 { } /* end */
3393};
3394
Takashi Iwai096a8852011-06-20 12:09:02 +02003395static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003396 /* Enable Boost Volume backdoor */
3397 {0x1, 0xf8a, 0x80},
3398 /* don't bybass mixer */
3399 {0x1, 0xf88, 0xc0},
3400 /* Enable mono output */
3401 {0x1, 0xf90, 0x08},
3402 { }
3403};
3404
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003405static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3406{
3407 struct via_spec *spec = codec->spec;
3408 int imux_is_smixer;
3409 unsigned int parm;
3410 unsigned int mono_out, present;
3411 /* SW0 (17h) = stereo mixer */
3412 imux_is_smixer =
3413 (snd_hda_codec_read(codec, 0x17, 0,
3414 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3415 /* inputs */
3416 /* PW 1/2/5 (1ah/1bh/1eh) */
3417 parm = AC_PWRST_D3;
3418 set_pin_power_state(codec, 0x1a, &parm);
3419 set_pin_power_state(codec, 0x1b, &parm);
3420 set_pin_power_state(codec, 0x1e, &parm);
3421 if (imux_is_smixer)
3422 parm = AC_PWRST_D0;
3423 /* SW0 (17h), AIW0(13h) */
3424 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3425 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3426
3427 parm = AC_PWRST_D3;
3428 set_pin_power_state(codec, 0x1e, &parm);
3429 /* PW11 (22h) */
3430 if (spec->dmic_enabled)
3431 set_pin_power_state(codec, 0x22, &parm);
3432 else
3433 snd_hda_codec_write(codec, 0x22, 0,
3434 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3435
3436 /* SW2(26h), AIW1(14h) */
3437 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3438 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3439
3440 /* outputs */
3441 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3442 parm = AC_PWRST_D3;
3443 set_pin_power_state(codec, 0x19, &parm);
3444 /* Smart 5.1 PW2(1bh) */
3445 if (spec->smart51_enabled)
3446 set_pin_power_state(codec, 0x1b, &parm);
3447 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3448 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3449
3450 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3451 parm = AC_PWRST_D3;
3452 set_pin_power_state(codec, 0x23, &parm);
3453 /* Smart 5.1 PW1(1ah) */
3454 if (spec->smart51_enabled)
3455 set_pin_power_state(codec, 0x1a, &parm);
3456 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3457
3458 /* Smart 5.1 PW5(1eh) */
3459 if (spec->smart51_enabled)
3460 set_pin_power_state(codec, 0x1e, &parm);
3461 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3462
3463 /* Mono out */
3464 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3465 present = snd_hda_jack_detect(codec, 0x1c);
3466
3467 if (present)
3468 mono_out = 0;
3469 else {
3470 present = snd_hda_jack_detect(codec, 0x1d);
3471 if (!spec->hp_independent_mode && present)
3472 mono_out = 0;
3473 else
3474 mono_out = 1;
3475 }
3476 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3477 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3478 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3479 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3480
3481 /* PW 3/4 (1ch/1dh) */
3482 parm = AC_PWRST_D3;
3483 set_pin_power_state(codec, 0x1c, &parm);
3484 set_pin_power_state(codec, 0x1d, &parm);
3485 /* HP Independent Mode, power on AOW3 */
3486 if (spec->hp_independent_mode)
3487 snd_hda_codec_write(codec, 0x25, 0,
3488 AC_VERB_SET_POWER_STATE, parm);
3489
3490 /* force to D0 for internal Speaker */
3491 /* MW0 (16h), AOW0 (10h) */
3492 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3493 imux_is_smixer ? AC_PWRST_D0 : parm);
3494 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3495 mono_out ? AC_PWRST_D0 : parm);
3496}
3497
Lydia Wangf3db4232009-10-10 19:08:41 +08003498static int patch_vt1716S(struct hda_codec *codec)
3499{
3500 struct via_spec *spec;
3501 int err;
3502
3503 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003504 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003505 if (spec == NULL)
3506 return -ENOMEM;
3507
Takashi Iwai620e2b22011-06-17 17:19:19 +02003508 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003509 override_mic_boost(codec, 0x1a, 0, 3, 40);
3510 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003511
Lydia Wangf3db4232009-10-10 19:08:41 +08003512 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003513 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003514 if (err < 0) {
3515 via_free(codec);
3516 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003517 }
3518
Takashi Iwai096a8852011-06-20 12:09:02 +02003519 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003520
Lydia Wangf3db4232009-10-10 19:08:41 +08003521 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3522 spec->num_mixers++;
3523
3524 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3525
3526 codec->patch_ops = via_patch_ops;
3527
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003528 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003529 return 0;
3530}
Lydia Wang25eaba22009-10-10 19:08:43 +08003531
3532/* for vt2002P */
3533
Takashi Iwai096a8852011-06-20 12:09:02 +02003534static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003535 /* Class-D speaker related verbs */
3536 {0x1, 0xfe0, 0x4},
3537 {0x1, 0xfe9, 0x80},
3538 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003539 /* Enable Boost Volume backdoor */
3540 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003541 /* Enable AOW0 to MW9 */
3542 {0x1, 0xfb8, 0x88},
3543 { }
3544};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003545
Takashi Iwai096a8852011-06-20 12:09:02 +02003546static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003547 /* Enable Boost Volume backdoor */
3548 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003549 /* Enable AOW0 to MW9 */
3550 {0x1, 0xfb8, 0x88},
3551 { }
3552};
Lydia Wang25eaba22009-10-10 19:08:43 +08003553
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003554static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3555{
3556 struct via_spec *spec = codec->spec;
3557 int imux_is_smixer;
3558 unsigned int parm;
3559 unsigned int present;
3560 /* MUX9 (1eh) = stereo mixer */
3561 imux_is_smixer =
3562 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3563 /* inputs */
3564 /* PW 5/6/7 (29h/2ah/2bh) */
3565 parm = AC_PWRST_D3;
3566 set_pin_power_state(codec, 0x29, &parm);
3567 set_pin_power_state(codec, 0x2a, &parm);
3568 set_pin_power_state(codec, 0x2b, &parm);
3569 parm = AC_PWRST_D0;
3570 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3571 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3572 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3573 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3574 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3575
3576 /* outputs */
3577 /* AOW0 (8h)*/
3578 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3579
Lydia Wang118909562011-03-23 17:57:34 +08003580 if (spec->codec_type == VT1802) {
3581 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3582 parm = AC_PWRST_D3;
3583 set_pin_power_state(codec, 0x28, &parm);
3584 snd_hda_codec_write(codec, 0x18, 0,
3585 AC_VERB_SET_POWER_STATE, parm);
3586 snd_hda_codec_write(codec, 0x38, 0,
3587 AC_VERB_SET_POWER_STATE, parm);
3588 } else {
3589 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3590 parm = AC_PWRST_D3;
3591 set_pin_power_state(codec, 0x26, &parm);
3592 snd_hda_codec_write(codec, 0x1c, 0,
3593 AC_VERB_SET_POWER_STATE, parm);
3594 snd_hda_codec_write(codec, 0x37, 0,
3595 AC_VERB_SET_POWER_STATE, parm);
3596 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003597
Lydia Wang118909562011-03-23 17:57:34 +08003598 if (spec->codec_type == VT1802) {
3599 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3600 parm = AC_PWRST_D3;
3601 set_pin_power_state(codec, 0x25, &parm);
3602 snd_hda_codec_write(codec, 0x15, 0,
3603 AC_VERB_SET_POWER_STATE, parm);
3604 snd_hda_codec_write(codec, 0x35, 0,
3605 AC_VERB_SET_POWER_STATE, parm);
3606 } else {
3607 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3608 parm = AC_PWRST_D3;
3609 set_pin_power_state(codec, 0x25, &parm);
3610 snd_hda_codec_write(codec, 0x19, 0,
3611 AC_VERB_SET_POWER_STATE, parm);
3612 snd_hda_codec_write(codec, 0x35, 0,
3613 AC_VERB_SET_POWER_STATE, parm);
3614 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003615
3616 if (spec->hp_independent_mode)
3617 snd_hda_codec_write(codec, 0x9, 0,
3618 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3619
3620 /* Class-D */
3621 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3622 present = snd_hda_jack_detect(codec, 0x25);
3623
3624 parm = AC_PWRST_D3;
3625 set_pin_power_state(codec, 0x24, &parm);
3626 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003627 if (spec->codec_type == VT1802)
3628 snd_hda_codec_write(codec, 0x14, 0,
3629 AC_VERB_SET_POWER_STATE, parm);
3630 else
3631 snd_hda_codec_write(codec, 0x18, 0,
3632 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003633 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3634
3635 /* Mono Out */
3636 present = snd_hda_jack_detect(codec, 0x26);
3637
3638 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003639 if (spec->codec_type == VT1802) {
3640 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3641 snd_hda_codec_write(codec, 0x33, 0,
3642 AC_VERB_SET_POWER_STATE, parm);
3643 snd_hda_codec_write(codec, 0x1c, 0,
3644 AC_VERB_SET_POWER_STATE, parm);
3645 snd_hda_codec_write(codec, 0x3c, 0,
3646 AC_VERB_SET_POWER_STATE, parm);
3647 } else {
3648 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3649 snd_hda_codec_write(codec, 0x31, 0,
3650 AC_VERB_SET_POWER_STATE, parm);
3651 snd_hda_codec_write(codec, 0x17, 0,
3652 AC_VERB_SET_POWER_STATE, parm);
3653 snd_hda_codec_write(codec, 0x3b, 0,
3654 AC_VERB_SET_POWER_STATE, parm);
3655 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003656 /* MW9 (21h) */
3657 if (imux_is_smixer || !is_aa_path_mute(codec))
3658 snd_hda_codec_write(codec, 0x21, 0,
3659 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3660 else
3661 snd_hda_codec_write(codec, 0x21, 0,
3662 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3663}
Lydia Wang25eaba22009-10-10 19:08:43 +08003664
3665/* patch for vt2002P */
3666static int patch_vt2002P(struct hda_codec *codec)
3667{
3668 struct via_spec *spec;
3669 int err;
3670
3671 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003672 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003673 if (spec == NULL)
3674 return -ENOMEM;
3675
Takashi Iwai620e2b22011-06-17 17:19:19 +02003676 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003677 override_mic_boost(codec, 0x2b, 0, 3, 40);
3678 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003679 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003680
Lydia Wang25eaba22009-10-10 19:08:43 +08003681 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003682 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003683 if (err < 0) {
3684 via_free(codec);
3685 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003686 }
3687
Lydia Wang118909562011-03-23 17:57:34 +08003688 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003689 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003690 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003691 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003692
Lydia Wang25eaba22009-10-10 19:08:43 +08003693 codec->patch_ops = via_patch_ops;
3694
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003695 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003696 return 0;
3697}
Lydia Wangab6734e2009-10-10 19:08:46 +08003698
3699/* for vt1812 */
3700
Takashi Iwai096a8852011-06-20 12:09:02 +02003701static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003702 /* Enable Boost Volume backdoor */
3703 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003704 /* Enable AOW0 to MW9 */
3705 {0x1, 0xfb8, 0xa8},
3706 { }
3707};
3708
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003709static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3710{
3711 struct via_spec *spec = codec->spec;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003712 unsigned int parm;
3713 unsigned int present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003714 /* inputs */
3715 /* PW 5/6/7 (29h/2ah/2bh) */
3716 parm = AC_PWRST_D3;
3717 set_pin_power_state(codec, 0x29, &parm);
3718 set_pin_power_state(codec, 0x2a, &parm);
3719 set_pin_power_state(codec, 0x2b, &parm);
3720 parm = AC_PWRST_D0;
3721 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3722 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3723 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3724 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3725 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3726
3727 /* outputs */
3728 /* AOW0 (8h)*/
3729 snd_hda_codec_write(codec, 0x8, 0,
3730 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3731
3732 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3733 parm = AC_PWRST_D3;
3734 set_pin_power_state(codec, 0x28, &parm);
3735 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3736 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3737
3738 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3739 parm = AC_PWRST_D3;
3740 set_pin_power_state(codec, 0x25, &parm);
3741 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3742 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3743 if (spec->hp_independent_mode)
3744 snd_hda_codec_write(codec, 0x9, 0,
3745 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3746
3747 /* Internal Speaker */
3748 /* PW0 (24h), MW0(14h), MUX0(34h) */
3749 present = snd_hda_jack_detect(codec, 0x25);
3750
3751 parm = AC_PWRST_D3;
3752 set_pin_power_state(codec, 0x24, &parm);
3753 if (present) {
3754 snd_hda_codec_write(codec, 0x14, 0,
3755 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3756 snd_hda_codec_write(codec, 0x34, 0,
3757 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3758 } else {
3759 snd_hda_codec_write(codec, 0x14, 0,
3760 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3761 snd_hda_codec_write(codec, 0x34, 0,
3762 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3763 }
3764
3765
3766 /* Mono Out */
3767 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3768 present = snd_hda_jack_detect(codec, 0x28);
3769
3770 parm = AC_PWRST_D3;
3771 set_pin_power_state(codec, 0x31, &parm);
3772 if (present) {
3773 snd_hda_codec_write(codec, 0x1c, 0,
3774 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3775 snd_hda_codec_write(codec, 0x3c, 0,
3776 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3777 snd_hda_codec_write(codec, 0x3e, 0,
3778 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3779 } else {
3780 snd_hda_codec_write(codec, 0x1c, 0,
3781 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3782 snd_hda_codec_write(codec, 0x3c, 0,
3783 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3784 snd_hda_codec_write(codec, 0x3e, 0,
3785 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3786 }
3787
3788 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3789 parm = AC_PWRST_D3;
3790 set_pin_power_state(codec, 0x33, &parm);
3791 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3792 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3793
3794}
Lydia Wangab6734e2009-10-10 19:08:46 +08003795
3796/* patch for vt1812 */
3797static int patch_vt1812(struct hda_codec *codec)
3798{
3799 struct via_spec *spec;
3800 int err;
3801
3802 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003803 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003804 if (spec == NULL)
3805 return -ENOMEM;
3806
Takashi Iwai620e2b22011-06-17 17:19:19 +02003807 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003808 override_mic_boost(codec, 0x2b, 0, 3, 40);
3809 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003810 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003811
Lydia Wangab6734e2009-10-10 19:08:46 +08003812 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003813 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003814 if (err < 0) {
3815 via_free(codec);
3816 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003817 }
3818
Takashi Iwai096a8852011-06-20 12:09:02 +02003819 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003820
Lydia Wangab6734e2009-10-10 19:08:46 +08003821 codec->patch_ops = via_patch_ops;
3822
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003823 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003824 return 0;
3825}
3826
Joseph Chanc577b8a2006-11-29 15:29:40 +01003827/*
3828 * patch entries
3829 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003830static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003831 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3832 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3833 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3834 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3835 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003836 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003837 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003838 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003839 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003840 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003841 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003842 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003843 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003844 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003845 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003846 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003847 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003848 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003849 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003850 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003851 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003852 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003853 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003854 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003855 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003856 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003857 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003858 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003859 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003860 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003861 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003862 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003863 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003864 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003865 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003866 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003867 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003868 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003869 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003870 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003871 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003872 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003873 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003874 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003875 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003876 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003877 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003878 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003879 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003880 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003881 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003882 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003883 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003884 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003885 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003886 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003887 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003888 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003889 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003890 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003891 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003892 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003893 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003894 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003895 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003896 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003897 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003898 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003899 { .id = 0x11060428, .name = "VT1718S",
3900 .patch = patch_vt1718S},
3901 { .id = 0x11064428, .name = "VT1718S",
3902 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003903 { .id = 0x11060441, .name = "VT2020",
3904 .patch = patch_vt1718S},
3905 { .id = 0x11064441, .name = "VT1828S",
3906 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003907 { .id = 0x11060433, .name = "VT1716S",
3908 .patch = patch_vt1716S},
3909 { .id = 0x1106a721, .name = "VT1716S",
3910 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003911 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3912 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003913 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003914 { .id = 0x11060440, .name = "VT1818S",
3915 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003916 { .id = 0x11060446, .name = "VT1802",
3917 .patch = patch_vt2002P},
3918 { .id = 0x11068446, .name = "VT1802",
3919 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003920 {} /* terminator */
3921};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003922
3923MODULE_ALIAS("snd-hda-codec-id:1106*");
3924
3925static struct hda_codec_preset_list via_list = {
3926 .preset = snd_hda_preset_via,
3927 .owner = THIS_MODULE,
3928};
3929
3930MODULE_LICENSE("GPL");
3931MODULE_DESCRIPTION("VIA HD-audio codec");
3932
3933static int __init patch_via_init(void)
3934{
3935 return snd_hda_add_codec_preset(&via_list);
3936}
3937
3938static void __exit patch_via_exit(void)
3939{
3940 snd_hda_delete_codec_preset(&via_list);
3941}
3942
3943module_init(patch_via_init)
3944module_exit(patch_via_exit)