blob: b5137629f8e942a75a941e0e183cef98fb58db02 [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"
Joseph Chanc577b8a2006-11-29 15:29:40 +010057
Joseph Chanc577b8a2006-11-29 15:29:40 +010058/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080059#define VT1708_HP_PIN_NID 0x20
60#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010061
Harald Welted7426322008-09-15 22:43:23 +080062enum VIA_HDA_CODEC {
63 UNKNOWN = -1,
64 VT1708,
65 VT1709_10CH,
66 VT1709_6CH,
67 VT1708B_8CH,
68 VT1708B_4CH,
69 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080070 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080071 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080072 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080073 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080074 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080075 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080076 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080077 CODEC_TYPES,
78};
79
Lydia Wang118909562011-03-23 17:57:34 +080080#define VT2002P_COMPATIBLE(spec) \
81 ((spec)->codec_type == VT2002P ||\
82 (spec)->codec_type == VT1812 ||\
83 (spec)->codec_type == VT1802)
84
Takashi Iwai8e3679d2011-06-21 09:01:36 +020085#define MAX_NID_PATH_DEPTH 5
86
Takashi Iwai09a9ad692011-06-21 15:57:44 +020087/* output-path: DAC -> ... -> pin
88 * idx[] contains the source index number of the next widget;
89 * e.g. idx[0] is the index of the DAC selected by path[1] widget
90 * multi[] indicates whether it's a selector widget with multi-connectors
91 * (i.e. the connection selection is mandatory)
92 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
93 */
Takashi Iwai4a796162011-06-17 17:53:38 +020094struct nid_path {
95 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020096 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad692011-06-21 15:57:44 +020097 unsigned char idx[MAX_NID_PATH_DEPTH];
98 unsigned char multi[MAX_NID_PATH_DEPTH];
99 unsigned int vol_ctl;
100 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200101};
102
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200103/* input-path */
104struct via_input {
105 hda_nid_t pin; /* input-pin or aa-mix */
106 int adc_idx; /* ADC index to be used */
107 int mux_idx; /* MUX index (if any) */
108 const char *label; /* input-source label */
109};
110
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200111#define VIA_MAX_ADCS 3
112
Takashi Iwai3b607e32011-07-18 16:54:40 +0200113enum {
114 STREAM_MULTI_OUT = (1 << 0),
115 STREAM_INDEP_HP = (1 << 1),
116};
117
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800118struct via_spec {
119 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200120 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800121 unsigned int num_mixers;
122
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200123 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800124 unsigned int num_iverbs;
125
Takashi Iwai82673bc2011-06-17 16:24:21 +0200126 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200127 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200128 const struct hda_pcm_stream *stream_analog_playback;
129 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800130
Takashi Iwai82673bc2011-06-17 16:24:21 +0200131 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200132 const struct hda_pcm_stream *stream_digital_playback;
133 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800134
135 /* playback */
136 struct hda_multi_out multiout;
137 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200138 hda_nid_t hp_dac_nid;
Takashi Iwai3214b962011-07-18 12:49:25 +0200139 hda_nid_t speaker_dac_nid;
140 int hp_indep_shared; /* indep HP-DAC is shared with side ch */
Takashi Iwai3b607e32011-07-18 16:54:40 +0200141 int opened_streams; /* STREAM_* bits */
142 int active_streams; /* STREAM_* bits */
Takashi Iwai3214b962011-07-18 12:49:25 +0200143 int aamix_mode; /* loopback is enabled for output-path? */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800144
Takashi Iwai3214b962011-07-18 12:49:25 +0200145 /* Output-paths:
146 * There are different output-paths depending on the setup.
147 * out_path, hp_path and speaker_path are primary paths. If both
148 * direct DAC and aa-loopback routes are available, these contain
149 * the former paths. Meanwhile *_mix_path contain the paths with
150 * loopback mixer. (Since the loopback is only for front channel,
151 * no out_mix_path for surround channels.)
152 * The HP output has another path, hp_indep_path, which is used in
153 * the independent-HP mode.
154 */
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200155 struct nid_path out_path[HDA_SIDE + 1];
Takashi Iwai3214b962011-07-18 12:49:25 +0200156 struct nid_path out_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200157 struct nid_path hp_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200158 struct nid_path hp_mix_path;
159 struct nid_path hp_indep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200160 struct nid_path speaker_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200161 struct nid_path speaker_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200162
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800163 /* capture */
164 unsigned int num_adc_nids;
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200165 hda_nid_t adc_nids[VIA_MAX_ADCS];
166 hda_nid_t mux_nids[VIA_MAX_ADCS];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200167 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800168 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800169
170 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200171 bool dyn_adc_switch;
172 int num_inputs;
173 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200174 unsigned int cur_mux[VIA_MAX_ADCS];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800175
Takashi Iwai3b607e32011-07-18 16:54:40 +0200176 /* dynamic DAC switching */
177 unsigned int cur_dac_stream_tag;
178 unsigned int cur_dac_format;
179 unsigned int cur_hp_stream_tag;
180 unsigned int cur_hp_format;
181
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200182 /* dynamic ADC switching */
183 hda_nid_t cur_adc;
184 unsigned int cur_adc_stream_tag;
185 unsigned int cur_adc_format;
186
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800187 /* PCM information */
188 struct hda_pcm pcm_rec[3];
189
190 /* dynamic controls, init_verbs and input_mux */
191 struct auto_pin_cfg autocfg;
192 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800193 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
194
195 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800196 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800197 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200198 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800199 enum VIA_HDA_CODEC codec_type;
200
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200201 /* smart51 setup */
202 unsigned int smart51_nums;
203 hda_nid_t smart51_pins[2];
204 int smart51_idxs[2];
205 const char *smart51_labels[2];
206 unsigned int smart51_enabled;
207
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800208 /* work to check hp jack state */
209 struct hda_codec *codec;
210 struct delayed_work vt1708_hp_work;
Takashi Iwai187d3332011-11-24 16:33:09 +0100211 int hp_work_active;
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
Takashi Iwai187d3332011-11-24 16:33:09 +0100309#define hp_detect_with_aa(codec) \
310 (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
311 !is_aa_path_mute(codec))
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800312
313static void vt1708_stop_hp_work(struct via_spec *spec)
314{
315 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
316 return;
Takashi Iwai187d3332011-11-24 16:33:09 +0100317 if (spec->hp_work_active) {
318 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 1);
319 cancel_delayed_work_sync(&spec->vt1708_hp_work);
320 spec->hp_work_active = 0;
321 }
322}
323
324static void vt1708_update_hp_work(struct via_spec *spec)
325{
326 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800327 return;
Takashi Iwai187d3332011-11-24 16:33:09 +0100328 if (spec->vt1708_jack_detect &&
329 (spec->active_streams || hp_detect_with_aa(spec->codec))) {
330 if (!spec->hp_work_active) {
331 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 0);
332 schedule_delayed_work(&spec->vt1708_hp_work,
333 msecs_to_jiffies(100));
334 spec->hp_work_active = 1;
335 }
336 } else if (!hp_detect_with_aa(spec->codec))
337 vt1708_stop_hp_work(spec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800338}
Lydia Wangf5271102009-10-10 19:07:35 +0800339
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800340static void set_widgets_power_state(struct hda_codec *codec)
341{
342 struct via_spec *spec = codec->spec;
343 if (spec->set_widgets_power_state)
344 spec->set_widgets_power_state(codec);
345}
Lydia Wang25eaba22009-10-10 19:08:43 +0800346
Lydia Wangf5271102009-10-10 19:07:35 +0800347static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
348 struct snd_ctl_elem_value *ucontrol)
349{
350 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
351 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
352
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800353 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200354 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Takashi Iwai187d3332011-11-24 16:33:09 +0100355 vt1708_update_hp_work(codec->spec);
Lydia Wangf5271102009-10-10 19:07:35 +0800356 return change;
357}
358
359/* modify .put = snd_hda_mixer_amp_switch_put */
360#define ANALOG_INPUT_MUTE \
361 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
362 .name = NULL, \
363 .index = 0, \
364 .info = snd_hda_mixer_amp_switch_info, \
365 .get = snd_hda_mixer_amp_switch_get, \
366 .put = analog_input_switch_put, \
367 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
368
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200369static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100370 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
371 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800372 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100373};
374
Lydia Wangab6734e2009-10-10 19:08:46 +0800375
Joseph Chanc577b8a2006-11-29 15:29:40 +0100376/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200377static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
378 const struct snd_kcontrol_new *tmpl,
379 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100380{
381 struct snd_kcontrol_new *knew;
382
Takashi Iwai603c4012008-07-30 15:01:44 +0200383 snd_array_init(&spec->kctls, sizeof(*knew), 32);
384 knew = snd_array_new(&spec->kctls);
385 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200386 return NULL;
387 *knew = *tmpl;
388 if (!name)
389 name = tmpl->name;
390 if (name) {
391 knew->name = kstrdup(name, GFP_KERNEL);
392 if (!knew->name)
393 return NULL;
394 }
395 return knew;
396}
397
398static int __via_add_control(struct via_spec *spec, int type, const char *name,
399 int idx, unsigned long val)
400{
401 struct snd_kcontrol_new *knew;
402
403 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
404 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100405 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200406 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100407 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100408 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100409 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100410 return 0;
411}
412
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200413#define via_add_control(spec, type, name, val) \
414 __via_add_control(spec, type, name, 0, val)
415
Takashi Iwai291c9e32011-06-17 16:15:26 +0200416#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100417
Takashi Iwai603c4012008-07-30 15:01:44 +0200418static void via_free_kctls(struct hda_codec *codec)
419{
420 struct via_spec *spec = codec->spec;
421
422 if (spec->kctls.list) {
423 struct snd_kcontrol_new *kctl = spec->kctls.list;
424 int i;
425 for (i = 0; i < spec->kctls.used; i++)
426 kfree(kctl[i].name);
427 }
428 snd_array_free(&spec->kctls);
429}
430
Joseph Chanc577b8a2006-11-29 15:29:40 +0100431/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800432static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200433 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100434{
435 char name[32];
436 int err;
437
438 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200439 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100440 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
441 if (err < 0)
442 return err;
443 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200444 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100445 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
446 if (err < 0)
447 return err;
448 return 0;
449}
450
Takashi Iwai5d417622011-06-20 11:32:27 +0200451#define get_connection_index(codec, mux, nid) \
Takashi Iwai8d087c72011-06-28 12:45:47 +0200452 snd_hda_get_conn_index(codec, mux, nid, 0)
Takashi Iwai5d417622011-06-20 11:32:27 +0200453
Takashi Iwai8df2a312011-06-21 11:48:29 +0200454static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
455 unsigned int mask)
456{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200457 unsigned int caps;
458 if (!nid)
459 return false;
460 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200461 if (dir == HDA_INPUT)
462 caps &= AC_WCAP_IN_AMP;
463 else
464 caps &= AC_WCAP_OUT_AMP;
465 if (!caps)
466 return false;
467 if (query_amp_caps(codec, nid, dir) & mask)
468 return true;
469 return false;
470}
471
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200472#define have_mute(codec, nid, dir) \
473 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200474
Lydia Wangd69607b2011-07-08 14:02:52 +0800475/* enable/disable the output-route mixers */
476static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
Takashi Iwai3214b962011-07-18 12:49:25 +0200477 hda_nid_t mix_nid, int idx, bool enable)
Lydia Wangd69607b2011-07-08 14:02:52 +0800478{
479 int i, num, val;
Lydia Wangd69607b2011-07-08 14:02:52 +0800480
481 if (!path)
482 return;
483 num = snd_hda_get_conn_list(codec, mix_nid, NULL);
Lydia Wangd69607b2011-07-08 14:02:52 +0800484 for (i = 0; i < num; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +0200485 if (i == idx)
486 val = AMP_IN_UNMUTE(i);
487 else
488 val = AMP_IN_MUTE(i);
Lydia Wangd69607b2011-07-08 14:02:52 +0800489 snd_hda_codec_write(codec, mix_nid, 0,
490 AC_VERB_SET_AMP_GAIN_MUTE, val);
491 }
492}
493
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200494/* enable/disable the output-route */
495static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
496 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200497{
Lydia Wangd69607b2011-07-08 14:02:52 +0800498 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200499 int i;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200500 for (i = 0; i < path->depth; i++) {
501 hda_nid_t src, dst;
502 int idx = path->idx[i];
503 src = path->path[i];
504 if (i < path->depth - 1)
505 dst = path->path[i + 1];
506 else
507 dst = 0;
508 if (enable && path->multi[i])
509 snd_hda_codec_write(codec, dst, 0,
510 AC_VERB_SET_CONNECT_SEL, idx);
Takashi Iwai3214b962011-07-18 12:49:25 +0200511 if (!force && (dst == spec->aa_mix_nid))
Lydia Wange5e14682011-07-01 10:55:07 +0800512 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +0200513 if (have_mute(codec, dst, HDA_INPUT))
514 activate_output_mix(codec, path, dst, idx, enable);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200515 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
516 continue;
517 if (have_mute(codec, src, HDA_OUTPUT)) {
518 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
519 snd_hda_codec_write(codec, src, 0,
520 AC_VERB_SET_AMP_GAIN_MUTE, val);
521 }
522 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200523}
524
525/* set the given pin as output */
526static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
527 int pin_type)
528{
529 if (!pin)
530 return;
531 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
532 pin_type);
533 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
534 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200535 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100536}
537
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200538static void via_auto_init_output(struct hda_codec *codec,
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200539 struct nid_path *path, int pin_type)
Takashi Iwai5d417622011-06-20 11:32:27 +0200540{
Takashi Iwai5d417622011-06-20 11:32:27 +0200541 unsigned int caps;
Lydia Wangd69607b2011-07-08 14:02:52 +0800542 hda_nid_t pin;
Takashi Iwai5d417622011-06-20 11:32:27 +0200543
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200544 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200545 return;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200546 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200547
548 init_output_pin(codec, pin, pin_type);
549 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
550 if (caps & AC_AMPCAP_MUTE) {
551 unsigned int val;
552 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
553 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
554 AMP_OUT_MUTE | val);
555 }
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200556 activate_output_path(codec, path, true, true); /* force on */
Takashi Iwai5d417622011-06-20 11:32:27 +0200557}
558
Joseph Chanc577b8a2006-11-29 15:29:40 +0100559static void via_auto_init_multi_out(struct hda_codec *codec)
560{
561 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200562 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100563 int i;
564
Takashi Iwai3214b962011-07-18 12:49:25 +0200565 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) {
566 path = &spec->out_path[i];
567 if (!i && spec->aamix_mode && spec->out_mix_path.depth)
568 path = &spec->out_mix_path;
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200569 via_auto_init_output(codec, path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200570 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100571}
572
Takashi Iwai020066d2011-07-21 13:45:56 +0200573/* deactivate the inactive headphone-paths */
574static void deactivate_hp_paths(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100575{
576 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200577 int shared = spec->hp_indep_shared;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100578
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200579 if (spec->hp_independent_mode) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200580 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200581 activate_output_path(codec, &spec->hp_mix_path, false, false);
582 if (shared)
583 activate_output_path(codec, &spec->out_path[shared],
584 false, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200585 } else if (spec->aamix_mode || !spec->hp_path.depth) {
586 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200587 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200588 } else {
Takashi Iwai020066d2011-07-21 13:45:56 +0200589 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200590 activate_output_path(codec, &spec->hp_mix_path, false, false);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200591 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100592}
593
Takashi Iwai020066d2011-07-21 13:45:56 +0200594static void via_auto_init_hp_out(struct hda_codec *codec)
595{
596 struct via_spec *spec = codec->spec;
597
598 if (!spec->hp_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200599 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200600 return;
601 }
602 deactivate_hp_paths(codec);
603 if (spec->hp_independent_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200604 via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200605 else if (spec->aamix_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200606 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200607 else
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200608 via_auto_init_output(codec, &spec->hp_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200609}
610
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200611static void via_auto_init_speaker_out(struct hda_codec *codec)
612{
613 struct via_spec *spec = codec->spec;
614
Takashi Iwai3214b962011-07-18 12:49:25 +0200615 if (!spec->autocfg.speaker_outs)
616 return;
617 if (!spec->speaker_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200618 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200619 return;
620 }
621 if (!spec->aamix_mode) {
622 activate_output_path(codec, &spec->speaker_mix_path,
623 false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200624 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200625 } else {
626 activate_output_path(codec, &spec->speaker_path, false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200627 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200628 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200629}
630
Takashi Iwaif4a78282011-06-17 18:46:48 +0200631static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200632static void via_hp_automute(struct hda_codec *codec);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200633
Joseph Chanc577b8a2006-11-29 15:29:40 +0100634static void via_auto_init_analog_input(struct hda_codec *codec)
635{
636 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200637 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200638 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200639 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200640 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100641
Takashi Iwai096a8852011-06-20 12:09:02 +0200642 /* init ADCs */
643 for (i = 0; i < spec->num_adc_nids; i++) {
644 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
645 AC_VERB_SET_AMP_GAIN_MUTE,
646 AMP_IN_UNMUTE(0));
647 }
648
649 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200650 for (i = 0; i < cfg->num_inputs; i++) {
651 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200652 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200653 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100654 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200655 ctl = PIN_VREF50;
656 else
657 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100658 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200659 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100660 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200661
662 /* init input-src */
663 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200664 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
665 if (spec->mux_nids[adc_idx]) {
666 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
667 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
668 AC_VERB_SET_CONNECT_SEL,
669 mux_idx);
670 }
671 if (spec->dyn_adc_switch)
672 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200673 }
674
675 /* init aa-mixer */
676 if (!spec->aa_mix_nid)
677 return;
678 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
679 ARRAY_SIZE(conn));
680 for (i = 0; i < num_conns; i++) {
681 unsigned int caps = get_wcaps(codec, conn[i]);
682 if (get_wcaps_type(caps) == AC_WID_PIN)
683 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
684 AC_VERB_SET_AMP_GAIN_MUTE,
685 AMP_IN_MUTE(i));
686 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100687}
Lydia Wangf5271102009-10-10 19:07:35 +0800688
689static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
690 unsigned int *affected_parm)
691{
692 unsigned parm;
693 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
694 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
695 >> AC_DEFCFG_MISC_SHIFT
696 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800697 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200698 unsigned present = 0;
699
700 no_presence |= spec->no_pin_power_ctl;
701 if (!no_presence)
702 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200703 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800704 || ((no_presence || present)
705 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800706 *affected_parm = AC_PWRST_D0; /* if it's connected */
707 parm = AC_PWRST_D0;
708 } else
709 parm = AC_PWRST_D3;
710
711 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
712}
713
Takashi Iwai24088a52011-06-17 16:59:21 +0200714static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
715 struct snd_ctl_elem_info *uinfo)
716{
717 static const char * const texts[] = {
718 "Disabled", "Enabled"
719 };
720
721 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
722 uinfo->count = 1;
723 uinfo->value.enumerated.items = 2;
724 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
725 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
726 strcpy(uinfo->value.enumerated.name,
727 texts[uinfo->value.enumerated.item]);
728 return 0;
729}
730
731static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
732 struct snd_ctl_elem_value *ucontrol)
733{
734 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
735 struct via_spec *spec = codec->spec;
736 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
737 return 0;
738}
739
740static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
741 struct snd_ctl_elem_value *ucontrol)
742{
743 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
744 struct via_spec *spec = codec->spec;
745 unsigned int val = !ucontrol->value.enumerated.item[0];
746
747 if (val == spec->no_pin_power_ctl)
748 return 0;
749 spec->no_pin_power_ctl = val;
750 set_widgets_power_state(codec);
751 return 1;
752}
753
754static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
755 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
756 .name = "Dynamic Power-Control",
757 .info = via_pin_power_ctl_info,
758 .get = via_pin_power_ctl_get,
759 .put = via_pin_power_ctl_put,
760};
761
762
Harald Welte0aa62ae2008-09-09 15:58:27 +0800763static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
764 struct snd_ctl_elem_info *uinfo)
765{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200766 static const char * const texts[] = { "OFF", "ON" };
767
768 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
769 uinfo->count = 1;
770 uinfo->value.enumerated.items = 2;
771 if (uinfo->value.enumerated.item >= 2)
772 uinfo->value.enumerated.item = 1;
773 strcpy(uinfo->value.enumerated.name,
774 texts[uinfo->value.enumerated.item]);
775 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800776}
777
778static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
779 struct snd_ctl_elem_value *ucontrol)
780{
781 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800782 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800783
Takashi Iwaiece8d042011-06-19 16:24:21 +0200784 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800785 return 0;
786}
787
Takashi Iwai3b607e32011-07-18 16:54:40 +0200788/* adjust spec->multiout setup according to the current flags */
789static void setup_playback_multi_pcm(struct via_spec *spec)
790{
791 const struct auto_pin_cfg *cfg = &spec->autocfg;
792 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
793 spec->multiout.hp_nid = 0;
794 if (!spec->hp_independent_mode) {
795 if (!spec->hp_indep_shared)
796 spec->multiout.hp_nid = spec->hp_dac_nid;
797 } else {
798 if (spec->hp_indep_shared)
799 spec->multiout.num_dacs = cfg->line_outs - 1;
800 }
801}
802
803/* update DAC setups according to indep-HP switch;
804 * this function is called only when indep-HP is modified
805 */
806static void switch_indep_hp_dacs(struct hda_codec *codec)
807{
808 struct via_spec *spec = codec->spec;
809 int shared = spec->hp_indep_shared;
810 hda_nid_t shared_dac, hp_dac;
811
812 if (!spec->opened_streams)
813 return;
814
815 shared_dac = shared ? spec->multiout.dac_nids[shared] : 0;
816 hp_dac = spec->hp_dac_nid;
817 if (spec->hp_independent_mode) {
818 /* switch to indep-HP mode */
819 if (spec->active_streams & STREAM_MULTI_OUT) {
820 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
821 __snd_hda_codec_cleanup_stream(codec, shared_dac, 1);
822 }
823 if (spec->active_streams & STREAM_INDEP_HP)
824 snd_hda_codec_setup_stream(codec, hp_dac,
825 spec->cur_hp_stream_tag, 0,
826 spec->cur_hp_format);
827 } else {
828 /* back to HP or shared-DAC */
829 if (spec->active_streams & STREAM_INDEP_HP)
830 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
831 if (spec->active_streams & STREAM_MULTI_OUT) {
832 hda_nid_t dac;
833 int ch;
834 if (shared_dac) { /* reset mutli-ch DAC */
835 dac = shared_dac;
836 ch = shared * 2;
837 } else { /* reset HP DAC */
838 dac = hp_dac;
839 ch = 0;
840 }
841 snd_hda_codec_setup_stream(codec, dac,
842 spec->cur_dac_stream_tag, ch,
843 spec->cur_dac_format);
844 }
845 }
846 setup_playback_multi_pcm(spec);
847}
848
Harald Welte0aa62ae2008-09-09 15:58:27 +0800849static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
850 struct snd_ctl_elem_value *ucontrol)
851{
852 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
853 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200854 int cur, shared;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200855
Takashi Iwai3b607e32011-07-18 16:54:40 +0200856 mutex_lock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200857 cur = !!ucontrol->value.enumerated.item[0];
Takashi Iwai3b607e32011-07-18 16:54:40 +0200858 if (spec->hp_independent_mode == cur) {
859 mutex_unlock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200860 return 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200861 }
Takashi Iwai25250502011-06-30 17:24:47 +0200862 spec->hp_independent_mode = cur;
Takashi Iwai3214b962011-07-18 12:49:25 +0200863 shared = spec->hp_indep_shared;
Takashi Iwai020066d2011-07-21 13:45:56 +0200864 deactivate_hp_paths(codec);
865 if (cur)
866 activate_output_path(codec, &spec->hp_indep_path, true, false);
867 else {
Takashi Iwai3214b962011-07-18 12:49:25 +0200868 if (shared)
869 activate_output_path(codec, &spec->out_path[shared],
Takashi Iwai25250502011-06-30 17:24:47 +0200870 true, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200871 if (spec->aamix_mode || !spec->hp_path.depth)
872 activate_output_path(codec, &spec->hp_mix_path,
873 true, false);
874 else
875 activate_output_path(codec, &spec->hp_path,
876 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200877 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800878
Takashi Iwai3b607e32011-07-18 16:54:40 +0200879 switch_indep_hp_dacs(codec);
880 mutex_unlock(&spec->config_mutex);
881
Lydia Wangce0e5a92011-03-22 16:22:37 +0800882 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800883 set_widgets_power_state(codec);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200884 via_hp_automute(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200885 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800886}
887
Takashi Iwaiece8d042011-06-19 16:24:21 +0200888static const struct snd_kcontrol_new via_hp_mixer = {
889 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
890 .name = "Independent HP",
891 .info = via_independent_hp_info,
892 .get = via_independent_hp_get,
893 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800894};
895
Takashi Iwai3d83e572010-04-14 14:36:23 +0200896static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100897{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200898 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100899 struct snd_kcontrol_new *knew;
900 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100901
Takashi Iwaiece8d042011-06-19 16:24:21 +0200902 nid = spec->autocfg.hp_pins[0];
903 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200904 if (knew == NULL)
905 return -ENOMEM;
906
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100907 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100908
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100909 return 0;
910}
911
Lydia Wang1564b282009-10-10 19:07:52 +0800912static void notify_aa_path_ctls(struct hda_codec *codec)
913{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200914 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800915 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800916
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200917 for (i = 0; i < spec->smart51_nums; i++) {
918 struct snd_kcontrol *ctl;
919 struct snd_ctl_elem_id id;
920 memset(&id, 0, sizeof(id));
921 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
922 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800923 ctl = snd_hda_find_mixer_ctl(codec, id.name);
924 if (ctl)
925 snd_ctl_notify(codec->bus->card,
926 SNDRV_CTL_EVENT_MASK_VALUE,
927 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800928 }
929}
930
931static void mute_aa_path(struct hda_codec *codec, int mute)
932{
933 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200934 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800935 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200936
Lydia Wang1564b282009-10-10 19:07:52 +0800937 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200938 for (i = 0; i < spec->smart51_nums; i++) {
939 if (spec->smart51_idxs[i] < 0)
940 continue;
941 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
942 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800943 HDA_AMP_MUTE, val);
944 }
945}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200946
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200947static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
948{
949 struct via_spec *spec = codec->spec;
950 int i;
951
952 for (i = 0; i < spec->smart51_nums; i++)
953 if (spec->smart51_pins[i] == pin)
954 return true;
955 return false;
956}
957
Lydia Wang1564b282009-10-10 19:07:52 +0800958static int via_smart51_get(struct snd_kcontrol *kcontrol,
959 struct snd_ctl_elem_value *ucontrol)
960{
961 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
962 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800963
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200964 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800965 return 0;
966}
967
968static int via_smart51_put(struct snd_kcontrol *kcontrol,
969 struct snd_ctl_elem_value *ucontrol)
970{
971 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
972 struct via_spec *spec = codec->spec;
973 int out_in = *ucontrol->value.integer.value
974 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800975 int i;
976
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200977 for (i = 0; i < spec->smart51_nums; i++) {
978 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200979 unsigned int parm;
980
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200981 parm = snd_hda_codec_read(codec, nid, 0,
982 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
983 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
984 parm |= out_in;
985 snd_hda_codec_write(codec, nid, 0,
986 AC_VERB_SET_PIN_WIDGET_CONTROL,
987 parm);
988 if (out_in == AC_PINCTL_OUT_EN) {
989 mute_aa_path(codec, 1);
990 notify_aa_path_ctls(codec);
991 }
Lydia Wang1564b282009-10-10 19:07:52 +0800992 }
993 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800994 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800995 return 1;
996}
997
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200998static const struct snd_kcontrol_new via_smart51_mixer = {
999 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1000 .name = "Smart 5.1",
1001 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001002 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +02001003 .get = via_smart51_get,
1004 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +08001005};
1006
Takashi Iwaif4a78282011-06-17 18:46:48 +02001007static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001008{
Takashi Iwaif4a78282011-06-17 18:46:48 +02001009 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001010
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001011 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +08001012 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001013 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001014 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001015 return 0;
1016}
1017
Takashi Iwaiada509e2011-06-20 15:40:19 +02001018/* check AA path's mute status */
1019static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001020{
Lydia Wangf5271102009-10-10 19:07:35 +08001021 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001022 const struct hda_amp_list *p;
1023 int i, ch, v;
1024
1025 for (i = 0; i < spec->num_loopbacks; i++) {
1026 p = &spec->loopback_list[i];
1027 for (ch = 0; ch < 2; ch++) {
1028 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
1029 p->idx);
1030 if (!(v & HDA_AMP_MUTE) && v > 0)
1031 return false;
Lydia Wangf5271102009-10-10 19:07:35 +08001032 }
1033 }
Takashi Iwaiada509e2011-06-20 15:40:19 +02001034 return true;
Lydia Wangf5271102009-10-10 19:07:35 +08001035}
1036
1037/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +02001038static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001039{
1040 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001041 bool enable;
1042 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +08001043
Takashi Iwai3b607e32011-07-18 16:54:40 +02001044 enable = is_aa_path_mute(codec) && (spec->opened_streams != 0);
Lydia Wangf5271102009-10-10 19:07:35 +08001045
1046 /* decide low current mode's verb & parameter */
1047 switch (spec->codec_type) {
1048 case VT1708B_8CH:
1049 case VT1708B_4CH:
1050 verb = 0xf70;
1051 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1052 break;
1053 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001054 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001055 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001056 verb = 0xf73;
1057 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1058 break;
1059 case VT1702:
1060 verb = 0xf73;
1061 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1062 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001063 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001064 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +08001065 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +08001066 verb = 0xf93;
1067 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1068 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001069 default:
1070 return; /* other codecs are not supported */
1071 }
1072 /* send verb */
1073 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1074}
1075
Joseph Chanc577b8a2006-11-29 15:29:40 +01001076/*
1077 * generic initialization of ADC, input mixers and output mixers
1078 */
Takashi Iwai096a8852011-06-20 12:09:02 +02001079static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001080 /* power down jack detect function */
1081 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001082 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001083};
1084
Takashi Iwai3b607e32011-07-18 16:54:40 +02001085static void set_stream_open(struct hda_codec *codec, int bit, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001086{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001087 struct via_spec *spec = codec->spec;
1088
1089 if (active)
Takashi Iwai3b607e32011-07-18 16:54:40 +02001090 spec->opened_streams |= bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001091 else
Takashi Iwai3b607e32011-07-18 16:54:40 +02001092 spec->opened_streams &= ~bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001093 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001094}
1095
Takashi Iwaiece8d042011-06-19 16:24:21 +02001096static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001097 struct hda_codec *codec,
1098 struct snd_pcm_substream *substream)
1099{
1100 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001101 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001102 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001103
Takashi Iwai25250502011-06-30 17:24:47 +02001104 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
Takashi Iwai25250502011-06-30 17:24:47 +02001105 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001106 set_stream_open(codec, STREAM_MULTI_OUT, true);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001107 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1108 hinfo);
1109 if (err < 0) {
Takashi Iwai3b607e32011-07-18 16:54:40 +02001110 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001111 return err;
1112 }
1113 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001114}
1115
Takashi Iwaiece8d042011-06-19 16:24:21 +02001116static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001117 struct hda_codec *codec,
1118 struct snd_pcm_substream *substream)
1119{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001120 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001121 return 0;
1122}
1123
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001124static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1125 struct hda_codec *codec,
1126 struct snd_pcm_substream *substream)
1127{
1128 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001129
Takashi Iwaiece8d042011-06-19 16:24:21 +02001130 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001131 return -EINVAL;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001132 set_stream_open(codec, STREAM_INDEP_HP, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001133 return 0;
1134}
1135
1136static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1137 struct hda_codec *codec,
1138 struct snd_pcm_substream *substream)
1139{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001140 set_stream_open(codec, STREAM_INDEP_HP, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001141 return 0;
1142}
1143
1144static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1145 struct hda_codec *codec,
1146 unsigned int stream_tag,
1147 unsigned int format,
1148 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001149{
1150 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001151
Takashi Iwai3b607e32011-07-18 16:54:40 +02001152 mutex_lock(&spec->config_mutex);
1153 setup_playback_multi_pcm(spec);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001154 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1155 format, substream);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001156 /* remember for dynamic DAC switch with indep-HP */
1157 spec->active_streams |= STREAM_MULTI_OUT;
1158 spec->cur_dac_stream_tag = stream_tag;
1159 spec->cur_dac_format = format;
1160 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001161 vt1708_update_hp_work(spec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001162 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001163}
1164
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001165static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1166 struct hda_codec *codec,
1167 unsigned int stream_tag,
1168 unsigned int format,
1169 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001170{
1171 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001172
Takashi Iwai3b607e32011-07-18 16:54:40 +02001173 mutex_lock(&spec->config_mutex);
1174 if (spec->hp_independent_mode)
1175 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1176 stream_tag, 0, format);
1177 spec->active_streams |= STREAM_INDEP_HP;
1178 spec->cur_hp_stream_tag = stream_tag;
1179 spec->cur_hp_format = format;
1180 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001181 vt1708_update_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001182 return 0;
1183}
1184
1185static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1186 struct hda_codec *codec,
1187 struct snd_pcm_substream *substream)
1188{
1189 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001190
Takashi Iwai3b607e32011-07-18 16:54:40 +02001191 mutex_lock(&spec->config_mutex);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001192 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001193 spec->active_streams &= ~STREAM_MULTI_OUT;
1194 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001195 vt1708_update_hp_work(spec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001196 return 0;
1197}
1198
1199static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1200 struct hda_codec *codec,
1201 struct snd_pcm_substream *substream)
1202{
1203 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001204
Takashi Iwai3b607e32011-07-18 16:54:40 +02001205 mutex_lock(&spec->config_mutex);
1206 if (spec->hp_independent_mode)
1207 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
1208 spec->active_streams &= ~STREAM_INDEP_HP;
1209 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001210 vt1708_update_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001211 return 0;
1212}
1213
Joseph Chanc577b8a2006-11-29 15:29:40 +01001214/*
1215 * Digital out
1216 */
1217static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1218 struct hda_codec *codec,
1219 struct snd_pcm_substream *substream)
1220{
1221 struct via_spec *spec = codec->spec;
1222 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1223}
1224
1225static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1226 struct hda_codec *codec,
1227 struct snd_pcm_substream *substream)
1228{
1229 struct via_spec *spec = codec->spec;
1230 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1231}
1232
Harald Welte5691ec72008-09-15 22:42:26 +08001233static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001234 struct hda_codec *codec,
1235 unsigned int stream_tag,
1236 unsigned int format,
1237 struct snd_pcm_substream *substream)
1238{
1239 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001240 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1241 stream_tag, format, substream);
1242}
Harald Welte5691ec72008-09-15 22:42:26 +08001243
Takashi Iwai9da29272009-05-07 16:31:14 +02001244static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1245 struct hda_codec *codec,
1246 struct snd_pcm_substream *substream)
1247{
1248 struct via_spec *spec = codec->spec;
1249 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001250 return 0;
1251}
1252
Joseph Chanc577b8a2006-11-29 15:29:40 +01001253/*
1254 * Analog capture
1255 */
1256static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1257 struct hda_codec *codec,
1258 unsigned int stream_tag,
1259 unsigned int format,
1260 struct snd_pcm_substream *substream)
1261{
1262 struct via_spec *spec = codec->spec;
1263
1264 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1265 stream_tag, 0, format);
1266 return 0;
1267}
1268
1269static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1270 struct hda_codec *codec,
1271 struct snd_pcm_substream *substream)
1272{
1273 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001274 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001275 return 0;
1276}
1277
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001278/* analog capture with dynamic ADC switching */
1279static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1280 struct hda_codec *codec,
1281 unsigned int stream_tag,
1282 unsigned int format,
1283 struct snd_pcm_substream *substream)
1284{
1285 struct via_spec *spec = codec->spec;
1286 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1287
Takashi Iwai3b607e32011-07-18 16:54:40 +02001288 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001289 spec->cur_adc = spec->adc_nids[adc_idx];
1290 spec->cur_adc_stream_tag = stream_tag;
1291 spec->cur_adc_format = format;
1292 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001293 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001294 return 0;
1295}
1296
1297static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1298 struct hda_codec *codec,
1299 struct snd_pcm_substream *substream)
1300{
1301 struct via_spec *spec = codec->spec;
1302
Takashi Iwai3b607e32011-07-18 16:54:40 +02001303 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001304 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1305 spec->cur_adc = 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001306 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001307 return 0;
1308}
1309
1310/* re-setup the stream if running; called from input-src put */
1311static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1312{
1313 struct via_spec *spec = codec->spec;
1314 int adc_idx = spec->inputs[cur].adc_idx;
1315 hda_nid_t adc = spec->adc_nids[adc_idx];
Takashi Iwai3b607e32011-07-18 16:54:40 +02001316 bool ret = false;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001317
Takashi Iwai3b607e32011-07-18 16:54:40 +02001318 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001319 if (spec->cur_adc && spec->cur_adc != adc) {
1320 /* stream is running, let's swap the current ADC */
1321 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1322 spec->cur_adc = adc;
1323 snd_hda_codec_setup_stream(codec, adc,
1324 spec->cur_adc_stream_tag, 0,
1325 spec->cur_adc_format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001326 ret = true;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001327 }
Takashi Iwai3b607e32011-07-18 16:54:40 +02001328 mutex_unlock(&spec->config_mutex);
1329 return ret;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001330}
1331
Takashi Iwai9af74212011-06-18 16:17:45 +02001332static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001333 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001334 .channels_min = 2,
1335 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001336 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001337 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001338 .open = via_playback_multi_pcm_open,
1339 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001340 .prepare = via_playback_multi_pcm_prepare,
1341 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001342 },
1343};
1344
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001345static const struct hda_pcm_stream via_pcm_hp_playback = {
1346 .substreams = 1,
1347 .channels_min = 2,
1348 .channels_max = 2,
1349 /* NID is set in via_build_pcms */
1350 .ops = {
1351 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001352 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001353 .prepare = via_playback_hp_pcm_prepare,
1354 .cleanup = via_playback_hp_pcm_cleanup
1355 },
1356};
1357
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001358static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001359 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001360 .channels_min = 2,
1361 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001362 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001363 /* We got noisy outputs on the right channel on VT1708 when
1364 * 24bit samples are used. Until any workaround is found,
1365 * disable the 24bit format, so far.
1366 */
1367 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1368 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001369 .open = via_playback_multi_pcm_open,
1370 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001371 .prepare = via_playback_multi_pcm_prepare,
1372 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001373 },
1374};
1375
Takashi Iwai9af74212011-06-18 16:17:45 +02001376static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001377 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001378 .channels_min = 2,
1379 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001380 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001381 .ops = {
1382 .prepare = via_capture_pcm_prepare,
1383 .cleanup = via_capture_pcm_cleanup
1384 },
1385};
1386
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001387static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1388 .substreams = 1,
1389 .channels_min = 2,
1390 .channels_max = 2,
1391 /* NID is set in via_build_pcms */
1392 .ops = {
1393 .prepare = via_dyn_adc_capture_pcm_prepare,
1394 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1395 },
1396};
1397
Takashi Iwai9af74212011-06-18 16:17:45 +02001398static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001399 .substreams = 1,
1400 .channels_min = 2,
1401 .channels_max = 2,
1402 /* NID is set in via_build_pcms */
1403 .ops = {
1404 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001405 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001406 .prepare = via_dig_playback_pcm_prepare,
1407 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001408 },
1409};
1410
Takashi Iwai9af74212011-06-18 16:17:45 +02001411static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001412 .substreams = 1,
1413 .channels_min = 2,
1414 .channels_max = 2,
1415};
1416
Takashi Iwai370bafb2011-06-20 12:47:45 +02001417/*
1418 * slave controls for virtual master
1419 */
1420static const char * const via_slave_vols[] = {
1421 "Front Playback Volume",
1422 "Surround Playback Volume",
1423 "Center Playback Volume",
1424 "LFE Playback Volume",
1425 "Side Playback Volume",
1426 "Headphone Playback Volume",
1427 "Speaker Playback Volume",
1428 NULL,
1429};
1430
1431static const char * const via_slave_sws[] = {
1432 "Front Playback Switch",
1433 "Surround Playback Switch",
1434 "Center Playback Switch",
1435 "LFE Playback Switch",
1436 "Side Playback Switch",
1437 "Headphone Playback Switch",
1438 "Speaker Playback Switch",
1439 NULL,
1440};
1441
Joseph Chanc577b8a2006-11-29 15:29:40 +01001442static int via_build_controls(struct hda_codec *codec)
1443{
1444 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001445 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001446 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001447
Takashi Iwai24088a52011-06-17 16:59:21 +02001448 if (spec->set_widgets_power_state)
1449 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1450 return -ENOMEM;
1451
Joseph Chanc577b8a2006-11-29 15:29:40 +01001452 for (i = 0; i < spec->num_mixers; i++) {
1453 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1454 if (err < 0)
1455 return err;
1456 }
1457
1458 if (spec->multiout.dig_out_nid) {
1459 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001460 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001461 spec->multiout.dig_out_nid);
1462 if (err < 0)
1463 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001464 err = snd_hda_create_spdif_share_sw(codec,
1465 &spec->multiout);
1466 if (err < 0)
1467 return err;
1468 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001469 }
1470 if (spec->dig_in_nid) {
1471 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1472 if (err < 0)
1473 return err;
1474 }
Lydia Wang17314372009-10-10 19:07:37 +08001475
Takashi Iwai370bafb2011-06-20 12:47:45 +02001476 /* if we have no master control, let's create it */
1477 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1478 unsigned int vmaster_tlv[4];
1479 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1480 HDA_OUTPUT, vmaster_tlv);
1481 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1482 vmaster_tlv, via_slave_vols);
1483 if (err < 0)
1484 return err;
1485 }
1486 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1487 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1488 NULL, via_slave_sws);
1489 if (err < 0)
1490 return err;
1491 }
1492
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001493 /* assign Capture Source enums to NID */
1494 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1495 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001496 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001497 if (err < 0)
1498 return err;
1499 }
1500
Lydia Wang17314372009-10-10 19:07:37 +08001501 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001502 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001503 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001504
Takashi Iwai603c4012008-07-30 15:01:44 +02001505 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001506 return 0;
1507}
1508
1509static int via_build_pcms(struct hda_codec *codec)
1510{
1511 struct via_spec *spec = codec->spec;
1512 struct hda_pcm *info = spec->pcm_rec;
1513
Takashi Iwaia5973102011-09-28 16:43:36 +02001514 codec->num_pcms = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001515 codec->pcm_info = info;
1516
Takashi Iwaia5973102011-09-28 16:43:36 +02001517 if (spec->multiout.num_dacs || spec->num_adc_nids) {
1518 snprintf(spec->stream_name_analog,
1519 sizeof(spec->stream_name_analog),
1520 "%s Analog", codec->chip_name);
1521 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001522
Takashi Iwaia5973102011-09-28 16:43:36 +02001523 if (spec->multiout.num_dacs) {
1524 if (!spec->stream_analog_playback)
1525 spec->stream_analog_playback =
1526 &via_pcm_analog_playback;
1527 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1528 *spec->stream_analog_playback;
1529 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1530 spec->multiout.dac_nids[0];
1531 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1532 spec->multiout.max_channels;
1533 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001534
Takashi Iwaia5973102011-09-28 16:43:36 +02001535 if (!spec->stream_analog_capture) {
1536 if (spec->dyn_adc_switch)
1537 spec->stream_analog_capture =
1538 &via_pcm_dyn_adc_analog_capture;
1539 else
1540 spec->stream_analog_capture =
1541 &via_pcm_analog_capture;
1542 }
1543 if (spec->num_adc_nids) {
1544 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1545 *spec->stream_analog_capture;
1546 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1547 spec->adc_nids[0];
1548 if (!spec->dyn_adc_switch)
1549 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1550 spec->num_adc_nids;
1551 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001552 codec->num_pcms++;
1553 info++;
Takashi Iwaia5973102011-09-28 16:43:36 +02001554 }
1555
1556 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
Takashi Iwai82673bc2011-06-17 16:24:21 +02001557 snprintf(spec->stream_name_digital,
1558 sizeof(spec->stream_name_digital),
1559 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001560 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001561 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001562 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001563 if (!spec->stream_digital_playback)
1564 spec->stream_digital_playback =
1565 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001566 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001567 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001568 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1569 spec->multiout.dig_out_nid;
1570 }
1571 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001572 if (!spec->stream_digital_capture)
1573 spec->stream_digital_capture =
1574 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001575 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001576 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001577 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1578 spec->dig_in_nid;
1579 }
Takashi Iwaia5973102011-09-28 16:43:36 +02001580 codec->num_pcms++;
1581 info++;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001582 }
1583
Takashi Iwaiece8d042011-06-19 16:24:21 +02001584 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001585 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1586 "%s HP", codec->chip_name);
1587 info->name = spec->stream_name_hp;
1588 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1589 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001590 spec->hp_dac_nid;
Takashi Iwaia5973102011-09-28 16:43:36 +02001591 codec->num_pcms++;
1592 info++;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001593 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001594 return 0;
1595}
1596
1597static void via_free(struct hda_codec *codec)
1598{
1599 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001600
1601 if (!spec)
1602 return;
1603
Takashi Iwai603c4012008-07-30 15:01:44 +02001604 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001605 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001606 kfree(spec->bind_cap_vol);
1607 kfree(spec->bind_cap_sw);
1608 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001609}
1610
Takashi Iwai64be2852011-06-17 16:51:39 +02001611/* mute/unmute outputs */
1612static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1613 hda_nid_t *pins, bool mute)
1614{
1615 int i;
Takashi Iwai94994732011-07-11 11:36:44 +02001616 for (i = 0; i < num_pins; i++) {
1617 unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
1618 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1619 if (parm & AC_PINCTL_IN_EN)
1620 continue;
1621 if (mute)
1622 parm &= ~AC_PINCTL_OUT_EN;
1623 else
1624 parm |= AC_PINCTL_OUT_EN;
Takashi Iwai64be2852011-06-17 16:51:39 +02001625 snd_hda_codec_write(codec, pins[i], 0,
Takashi Iwai94994732011-07-11 11:36:44 +02001626 AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
1627 }
Takashi Iwai64be2852011-06-17 16:51:39 +02001628}
1629
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001630/* mute internal speaker if line-out is plugged */
1631static void via_line_automute(struct hda_codec *codec, int present)
1632{
1633 struct via_spec *spec = codec->spec;
1634
1635 if (!spec->autocfg.speaker_outs)
1636 return;
1637 if (!present)
1638 present = snd_hda_jack_detect(codec,
1639 spec->autocfg.line_out_pins[0]);
1640 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1641 spec->autocfg.speaker_pins,
1642 present);
1643}
1644
Harald Welte69e52a82008-09-09 15:57:32 +08001645/* mute internal speaker if HP is plugged */
1646static void via_hp_automute(struct hda_codec *codec)
1647{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001648 int present = 0;
Takashi Iwai6e969d92011-07-11 11:28:13 +02001649 int nums;
Harald Welte69e52a82008-09-09 15:57:32 +08001650 struct via_spec *spec = codec->spec;
1651
Takashi Iwai187d3332011-11-24 16:33:09 +01001652 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0] &&
1653 (spec->codec_type != VT1708 || spec->vt1708_jack_detect))
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001654 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai6e969d92011-07-11 11:28:13 +02001655
1656 if (spec->smart51_enabled)
1657 nums = spec->autocfg.line_outs + spec->smart51_nums;
1658 else
1659 nums = spec->autocfg.line_outs;
1660 toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
1661
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001662 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001663}
1664
Harald Welte69e52a82008-09-09 15:57:32 +08001665static void via_gpio_control(struct hda_codec *codec)
1666{
1667 unsigned int gpio_data;
1668 unsigned int vol_counter;
1669 unsigned int vol;
1670 unsigned int master_vol;
1671
1672 struct via_spec *spec = codec->spec;
1673
1674 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1675 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1676
1677 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1678 0xF84, 0) & 0x3F0000) >> 16;
1679
1680 vol = vol_counter & 0x1F;
1681 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1682 AC_VERB_GET_AMP_GAIN_MUTE,
1683 AC_AMP_GET_INPUT);
1684
1685 if (gpio_data == 0x02) {
1686 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001687 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1688 AC_VERB_SET_PIN_WIDGET_CONTROL,
1689 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001690 if (vol_counter & 0x20) {
1691 /* decrease volume */
1692 if (vol > master_vol)
1693 vol = master_vol;
1694 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1695 0, HDA_AMP_VOLMASK,
1696 master_vol-vol);
1697 } else {
1698 /* increase volume */
1699 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1700 HDA_AMP_VOLMASK,
1701 ((master_vol+vol) > 0x2A) ? 0x2A :
1702 (master_vol+vol));
1703 }
1704 } else if (!(gpio_data & 0x02)) {
1705 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001706 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1707 AC_VERB_SET_PIN_WIDGET_CONTROL,
1708 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001709 }
1710}
1711
1712/* unsolicited event for jack sensing */
1713static void via_unsol_event(struct hda_codec *codec,
1714 unsigned int res)
1715{
1716 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001717
Lydia Wanga34df192009-10-10 19:08:01 +08001718 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001719 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001720
1721 res &= ~VIA_JACK_EVENT;
1722
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001723 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001724 via_hp_automute(codec);
1725 else if (res == VIA_GPIO_EVENT)
1726 via_gpio_control(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001727}
1728
Takashi Iwai2a439522011-07-26 09:52:50 +02001729#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001730static int via_suspend(struct hda_codec *codec, pm_message_t state)
1731{
1732 struct via_spec *spec = codec->spec;
1733 vt1708_stop_hp_work(spec);
1734 return 0;
1735}
1736#endif
1737
Takashi Iwaicb53c622007-08-10 17:21:45 +02001738#ifdef CONFIG_SND_HDA_POWER_SAVE
1739static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1740{
1741 struct via_spec *spec = codec->spec;
1742 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1743}
1744#endif
1745
Joseph Chanc577b8a2006-11-29 15:29:40 +01001746/*
1747 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001748
1749static int via_init(struct hda_codec *codec);
1750
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001751static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001752 .build_controls = via_build_controls,
1753 .build_pcms = via_build_pcms,
1754 .init = via_init,
1755 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001756 .unsol_event = via_unsol_event,
Takashi Iwai2a439522011-07-26 09:52:50 +02001757#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001758 .suspend = via_suspend,
1759#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001760#ifdef CONFIG_SND_HDA_POWER_SAVE
1761 .check_power_status = via_check_power_status,
1762#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001763};
1764
Takashi Iwai4a796162011-06-17 17:53:38 +02001765static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001766{
Takashi Iwai4a796162011-06-17 17:53:38 +02001767 struct via_spec *spec = codec->spec;
1768 int i;
1769
1770 for (i = 0; i < spec->multiout.num_dacs; i++) {
1771 if (spec->multiout.dac_nids[i] == dac)
1772 return false;
1773 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001774 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001775 return false;
1776 return true;
1777}
1778
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001779static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001780 hda_nid_t target_dac, int with_aa_mix,
1781 struct nid_path *path, int depth)
Takashi Iwai4a796162011-06-17 17:53:38 +02001782{
Takashi Iwai3214b962011-07-18 12:49:25 +02001783 struct via_spec *spec = codec->spec;
Takashi Iwai4a796162011-06-17 17:53:38 +02001784 hda_nid_t conn[8];
1785 int i, nums;
1786
Takashi Iwai3214b962011-07-18 12:49:25 +02001787 if (nid == spec->aa_mix_nid) {
1788 if (!with_aa_mix)
1789 return false;
1790 with_aa_mix = 2; /* mark aa-mix is included */
1791 }
1792
Takashi Iwai4a796162011-06-17 17:53:38 +02001793 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1794 for (i = 0; i < nums; i++) {
1795 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1796 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001797 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1798 /* aa-mix is requested but not included? */
1799 if (!(spec->aa_mix_nid && with_aa_mix == 1))
1800 goto found;
1801 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001802 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001803 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001804 return false;
1805 for (i = 0; i < nums; i++) {
1806 unsigned int type;
1807 type = get_wcaps_type(get_wcaps(codec, conn[i]));
Takashi Iwai3214b962011-07-18 12:49:25 +02001808 if (type == AC_WID_AUD_OUT)
Takashi Iwai4a796162011-06-17 17:53:38 +02001809 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001810 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai3214b962011-07-18 12:49:25 +02001811 with_aa_mix, path, depth + 1))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001812 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001813 }
1814 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001815
1816 found:
1817 path->path[path->depth] = conn[i];
1818 path->idx[path->depth] = i;
1819 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1820 path->multi[path->depth] = 1;
1821 path->depth++;
1822 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001823}
1824
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001825static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001826 hda_nid_t target_dac, int with_aa_mix,
1827 struct nid_path *path)
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001828{
Takashi Iwai3214b962011-07-18 12:49:25 +02001829 if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001830 path->path[path->depth] = nid;
1831 path->depth++;
Takashi Iwai3214b962011-07-18 12:49:25 +02001832 snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
1833 path->depth, path->path[0], path->path[1],
1834 path->path[2], path->path[3], path->path[4]);
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001835 return true;
1836 }
1837 return false;
1838}
1839
Takashi Iwai4a796162011-06-17 17:53:38 +02001840static int via_auto_fill_dac_nids(struct hda_codec *codec)
1841{
1842 struct via_spec *spec = codec->spec;
1843 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001844 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001845 hda_nid_t nid;
1846
Joseph Chanc577b8a2006-11-29 15:29:40 +01001847 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001848 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001849 for (i = 0; i < cfg->line_outs; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001850 hda_nid_t dac = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001851 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001852 if (!nid)
1853 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001854 if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
1855 dac = spec->out_path[i].path[0];
1856 if (!i && parse_output_path(codec, nid, dac, 1,
1857 &spec->out_mix_path))
1858 dac = spec->out_mix_path.path[0];
1859 if (dac) {
1860 spec->private_dac_nids[i] = dac;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001861 dac_num++;
1862 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001863 }
Takashi Iwai3214b962011-07-18 12:49:25 +02001864 if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
1865 spec->out_path[0] = spec->out_mix_path;
1866 spec->out_mix_path.depth = 0;
1867 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001868 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001869 return 0;
1870}
1871
Takashi Iwai4a796162011-06-17 17:53:38 +02001872static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001873 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001874{
Takashi Iwai4a796162011-06-17 17:53:38 +02001875 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001876 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001877 hda_nid_t dac, pin, sel, nid;
1878 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001879
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001880 dac = check_dac ? path->path[0] : 0;
1881 pin = path->path[path->depth - 1];
1882 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001883
Takashi Iwai8df2a312011-06-21 11:48:29 +02001884 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001885 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001886 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001887 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001888 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1889 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001890 else
1891 nid = 0;
1892 if (nid) {
1893 sprintf(name, "%s Playback Volume", pfx);
1894 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001895 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001896 if (err < 0)
1897 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001898 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001899 }
1900
Takashi Iwai8df2a312011-06-21 11:48:29 +02001901 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001902 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001903 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001904 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001905 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1906 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001907 else
1908 nid = 0;
1909 if (nid) {
1910 sprintf(name, "%s Playback Switch", pfx);
1911 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1912 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1913 if (err < 0)
1914 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001915 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001916 }
1917 return 0;
1918}
1919
Takashi Iwaif4a78282011-06-17 18:46:48 +02001920static void mangle_smart51(struct hda_codec *codec)
1921{
1922 struct via_spec *spec = codec->spec;
1923 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001924 struct auto_pin_cfg_item *ins = cfg->inputs;
1925 int i, j, nums, attr;
1926 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001927
Takashi Iwai0f98c242011-06-21 12:51:33 +02001928 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1929 nums = 0;
1930 for (i = 0; i < cfg->num_inputs; i++) {
1931 unsigned int def;
1932 if (ins[i].type > AUTO_PIN_LINE_IN)
1933 continue;
1934 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1935 if (snd_hda_get_input_pin_attr(def) != attr)
1936 continue;
1937 for (j = 0; j < nums; j++)
1938 if (ins[pins[j]].type < ins[i].type) {
1939 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001940 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001941 break;
1942 }
1943 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001944 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001945 }
1946 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001947 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001948 for (i = 0; i < nums; i++) {
1949 hda_nid_t pin = ins[pins[i]].pin;
1950 spec->smart51_pins[spec->smart51_nums++] = pin;
1951 cfg->line_out_pins[cfg->line_outs++] = pin;
1952 if (cfg->line_outs == 3)
1953 break;
1954 }
1955 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001956 }
1957}
1958
Takashi Iwai020066d2011-07-21 13:45:56 +02001959static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src)
1960{
1961 dst->vol_ctl = src->vol_ctl;
1962 dst->mute_ctl = src->mute_ctl;
1963}
1964
Takashi Iwai4a796162011-06-17 17:53:38 +02001965/* add playback controls from the parsed DAC table */
1966static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1967{
1968 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001969 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai3214b962011-07-18 12:49:25 +02001970 struct nid_path *path;
Takashi Iwaiea734962011-01-17 11:29:34 +01001971 static const char * const chname[4] = {
1972 "Front", "Surround", "C/LFE", "Side"
1973 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001974 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001975 int old_line_outs;
1976
1977 /* check smart51 */
1978 old_line_outs = cfg->line_outs;
1979 if (cfg->line_outs == 1)
1980 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001981
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001982 err = via_auto_fill_dac_nids(codec);
1983 if (err < 0)
1984 return err;
1985
Lydia Wang5c9a5612011-07-08 14:03:43 +08001986 if (spec->multiout.num_dacs < 3) {
1987 spec->smart51_nums = 0;
1988 cfg->line_outs = old_line_outs;
1989 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001990 for (i = 0; i < cfg->line_outs; i++) {
1991 hda_nid_t pin, dac;
1992 pin = cfg->line_out_pins[i];
1993 dac = spec->multiout.dac_nids[i];
1994 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001995 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001996 path = spec->out_path + i;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001997 if (i == HDA_CLFE) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001998 err = create_ch_ctls(codec, "Center", 1, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001999 if (err < 0)
2000 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002001 err = create_ch_ctls(codec, "LFE", 2, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002002 if (err < 0)
2003 return err;
2004 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02002005 const char *pfx = chname[i];
2006 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
2007 cfg->line_outs == 1)
2008 pfx = "Speaker";
Takashi Iwai3214b962011-07-18 12:49:25 +02002009 err = create_ch_ctls(codec, pfx, 3, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002010 if (err < 0)
2011 return err;
2012 }
Takashi Iwai020066d2011-07-21 13:45:56 +02002013 if (path != spec->out_path + i)
2014 copy_path_mixer_ctls(&spec->out_path[i], path);
2015 if (path == spec->out_path && spec->out_mix_path.depth)
2016 copy_path_mixer_ctls(&spec->out_mix_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002017 }
2018
Takashi Iwai4a796162011-06-17 17:53:38 +02002019 idx = get_connection_index(codec, spec->aa_mix_nid,
2020 spec->multiout.dac_nids[0]);
2021 if (idx >= 0) {
2022 /* add control to mixer */
Takashi Iwai3214b962011-07-18 12:49:25 +02002023 const char *name;
2024 name = spec->out_mix_path.depth ?
2025 "PCM Loopback Playback Volume" : "PCM Playback Volume";
2026 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002027 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2028 idx, HDA_INPUT));
2029 if (err < 0)
2030 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002031 name = spec->out_mix_path.depth ?
2032 "PCM Loopback Playback Switch" : "PCM Playback Switch";
2033 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002034 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2035 idx, HDA_INPUT));
2036 if (err < 0)
2037 return err;
2038 }
2039
Takashi Iwaif4a78282011-06-17 18:46:48 +02002040 cfg->line_outs = old_line_outs;
2041
Joseph Chanc577b8a2006-11-29 15:29:40 +01002042 return 0;
2043}
2044
Takashi Iwai4a796162011-06-17 17:53:38 +02002045static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002046{
Takashi Iwai4a796162011-06-17 17:53:38 +02002047 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002048 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002049 bool check_dac;
Takashi Iwai3214b962011-07-18 12:49:25 +02002050 int i, err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002051
2052 if (!pin)
2053 return 0;
2054
Takashi Iwai3214b962011-07-18 12:49:25 +02002055 if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
2056 for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
2057 if (i < spec->multiout.num_dacs &&
2058 parse_output_path(codec, pin,
2059 spec->multiout.dac_nids[i], 0,
2060 &spec->hp_indep_path)) {
2061 spec->hp_indep_shared = i;
2062 break;
2063 }
2064 }
Takashi Iwai25250502011-06-30 17:24:47 +02002065 }
Takashi Iwai3214b962011-07-18 12:49:25 +02002066 if (spec->hp_indep_path.depth) {
2067 spec->hp_dac_nid = spec->hp_indep_path.path[0];
2068 if (!spec->hp_indep_shared)
2069 spec->hp_path = spec->hp_indep_path;
2070 }
2071 /* optionally check front-path w/o AA-mix */
2072 if (!spec->hp_path.depth)
2073 parse_output_path(codec, pin,
2074 spec->multiout.dac_nids[HDA_FRONT], 0,
2075 &spec->hp_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002076
Takashi Iwaiece8d042011-06-19 16:24:21 +02002077 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai3214b962011-07-18 12:49:25 +02002078 1, &spec->hp_mix_path) && !spec->hp_path.depth)
Takashi Iwaiece8d042011-06-19 16:24:21 +02002079 return 0;
2080
Takashi Iwai3214b962011-07-18 12:49:25 +02002081 if (spec->hp_path.depth) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002082 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002083 check_dac = true;
2084 } else {
Takashi Iwai3214b962011-07-18 12:49:25 +02002085 path = &spec->hp_mix_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002086 check_dac = false;
2087 }
2088 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002089 if (err < 0)
2090 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002091 if (check_dac)
2092 copy_path_mixer_ctls(&spec->hp_mix_path, path);
2093 else
2094 copy_path_mixer_ctls(&spec->hp_path, path);
2095 if (spec->hp_indep_path.depth)
2096 copy_path_mixer_ctls(&spec->hp_indep_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002097 return 0;
2098}
2099
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002100static int via_auto_create_speaker_ctls(struct hda_codec *codec)
2101{
2102 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +02002103 struct nid_path *path;
2104 bool check_dac;
Wang Shaoyan81c0a782011-08-05 18:51:29 +08002105 hda_nid_t pin, dac = 0;
Takashi Iwai3214b962011-07-18 12:49:25 +02002106 int err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002107
2108 pin = spec->autocfg.speaker_pins[0];
2109 if (!spec->autocfg.speaker_outs || !pin)
2110 return 0;
2111
Takashi Iwai3214b962011-07-18 12:49:25 +02002112 if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02002113 dac = spec->speaker_path.path[0];
Takashi Iwai3214b962011-07-18 12:49:25 +02002114 if (!dac)
2115 parse_output_path(codec, pin,
2116 spec->multiout.dac_nids[HDA_FRONT], 0,
2117 &spec->speaker_path);
2118 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
2119 1, &spec->speaker_mix_path) && !dac)
2120 return 0;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002121
Takashi Iwai3214b962011-07-18 12:49:25 +02002122 /* no AA-path for front? */
2123 if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
2124 dac = 0;
2125
2126 spec->speaker_dac_nid = dac;
2127 spec->multiout.extra_out_nid[0] = dac;
2128 if (dac) {
2129 path = &spec->speaker_path;
2130 check_dac = true;
2131 } else {
2132 path = &spec->speaker_mix_path;
2133 check_dac = false;
2134 }
2135 err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
2136 if (err < 0)
2137 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002138 if (check_dac)
2139 copy_path_mixer_ctls(&spec->speaker_mix_path, path);
2140 else
2141 copy_path_mixer_ctls(&spec->speaker_path, path);
Takashi Iwai3214b962011-07-18 12:49:25 +02002142 return 0;
2143}
2144
2145#define via_aamix_ctl_info via_pin_power_ctl_info
2146
2147static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol,
2148 struct snd_ctl_elem_value *ucontrol)
2149{
2150 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2151 struct via_spec *spec = codec->spec;
2152 ucontrol->value.enumerated.item[0] = spec->aamix_mode;
2153 return 0;
2154}
2155
2156static void update_aamix_paths(struct hda_codec *codec, int do_mix,
2157 struct nid_path *nomix, struct nid_path *mix)
2158{
2159 if (do_mix) {
2160 activate_output_path(codec, nomix, false, false);
2161 activate_output_path(codec, mix, true, false);
2162 } else {
2163 activate_output_path(codec, mix, false, false);
2164 activate_output_path(codec, nomix, true, false);
2165 }
2166}
2167
2168static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol,
2169 struct snd_ctl_elem_value *ucontrol)
2170{
2171 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2172 struct via_spec *spec = codec->spec;
2173 unsigned int val = ucontrol->value.enumerated.item[0];
2174
2175 if (val == spec->aamix_mode)
2176 return 0;
2177 spec->aamix_mode = val;
2178 /* update front path */
2179 update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
2180 /* update HP path */
2181 if (!spec->hp_independent_mode) {
2182 update_aamix_paths(codec, val, &spec->hp_path,
2183 &spec->hp_mix_path);
2184 }
2185 /* update speaker path */
2186 update_aamix_paths(codec, val, &spec->speaker_path,
2187 &spec->speaker_mix_path);
2188 return 1;
2189}
2190
2191static const struct snd_kcontrol_new via_aamix_ctl_enum = {
2192 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2193 .name = "Loopback Mixing",
2194 .info = via_aamix_ctl_info,
2195 .get = via_aamix_ctl_get,
2196 .put = via_aamix_ctl_put,
2197};
2198
2199static int via_auto_create_loopback_switch(struct hda_codec *codec)
2200{
2201 struct via_spec *spec = codec->spec;
2202
2203 if (!spec->aa_mix_nid || !spec->out_mix_path.depth)
2204 return 0; /* no loopback switching available */
2205 if (!via_clone_control(spec, &via_aamix_ctl_enum))
2206 return -ENOMEM;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002207 return 0;
2208}
2209
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002210/* look for ADCs */
2211static int via_fill_adcs(struct hda_codec *codec)
2212{
2213 struct via_spec *spec = codec->spec;
2214 hda_nid_t nid = codec->start_nid;
2215 int i;
2216
2217 for (i = 0; i < codec->num_nodes; i++, nid++) {
2218 unsigned int wcaps = get_wcaps(codec, nid);
2219 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2220 continue;
2221 if (wcaps & AC_WCAP_DIGITAL)
2222 continue;
2223 if (!(wcaps & AC_WCAP_CONN_LIST))
2224 continue;
2225 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2226 return -ENOMEM;
2227 spec->adc_nids[spec->num_adc_nids++] = nid;
2228 }
2229 return 0;
2230}
2231
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002232/* input-src control */
2233static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2234 struct snd_ctl_elem_info *uinfo)
2235{
2236 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2237 struct via_spec *spec = codec->spec;
2238
2239 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2240 uinfo->count = 1;
2241 uinfo->value.enumerated.items = spec->num_inputs;
2242 if (uinfo->value.enumerated.item >= spec->num_inputs)
2243 uinfo->value.enumerated.item = spec->num_inputs - 1;
2244 strcpy(uinfo->value.enumerated.name,
2245 spec->inputs[uinfo->value.enumerated.item].label);
2246 return 0;
2247}
2248
2249static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2250 struct snd_ctl_elem_value *ucontrol)
2251{
2252 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2253 struct via_spec *spec = codec->spec;
2254 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2255
2256 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2257 return 0;
2258}
2259
2260static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2261 struct snd_ctl_elem_value *ucontrol)
2262{
2263 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2264 struct via_spec *spec = codec->spec;
2265 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2266 hda_nid_t mux;
2267 int cur;
2268
2269 cur = ucontrol->value.enumerated.item[0];
2270 if (cur < 0 || cur >= spec->num_inputs)
2271 return -EINVAL;
2272 if (spec->cur_mux[idx] == cur)
2273 return 0;
2274 spec->cur_mux[idx] = cur;
2275 if (spec->dyn_adc_switch) {
2276 int adc_idx = spec->inputs[cur].adc_idx;
2277 mux = spec->mux_nids[adc_idx];
2278 via_dyn_adc_pcm_resetup(codec, cur);
2279 } else {
2280 mux = spec->mux_nids[idx];
2281 if (snd_BUG_ON(!mux))
2282 return -EINVAL;
2283 }
2284
2285 if (mux) {
2286 /* switch to D0 beofre change index */
2287 if (snd_hda_codec_read(codec, mux, 0,
2288 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2289 snd_hda_codec_write(codec, mux, 0,
2290 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2291 snd_hda_codec_write(codec, mux, 0,
2292 AC_VERB_SET_CONNECT_SEL,
2293 spec->inputs[cur].mux_idx);
2294 }
2295
2296 /* update jack power state */
2297 set_widgets_power_state(codec);
2298 return 0;
2299}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002300
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002301static const struct snd_kcontrol_new via_input_src_ctl = {
2302 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2303 /* The multiple "Capture Source" controls confuse alsamixer
2304 * So call somewhat different..
2305 */
2306 /* .name = "Capture Source", */
2307 .name = "Input Source",
2308 .info = via_mux_enum_info,
2309 .get = via_mux_enum_get,
2310 .put = via_mux_enum_put,
2311};
2312
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002313static int create_input_src_ctls(struct hda_codec *codec, int count)
2314{
2315 struct via_spec *spec = codec->spec;
2316 struct snd_kcontrol_new *knew;
2317
2318 if (spec->num_inputs <= 1 || !count)
2319 return 0; /* no need for single src */
2320
2321 knew = via_clone_control(spec, &via_input_src_ctl);
2322 if (!knew)
2323 return -ENOMEM;
2324 knew->count = count;
2325 return 0;
2326}
2327
2328/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002329static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2330{
2331 struct hda_amp_list *list;
2332
2333 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2334 return;
2335 list = spec->loopback_list + spec->num_loopbacks;
2336 list->nid = mix;
2337 list->dir = HDA_INPUT;
2338 list->idx = idx;
2339 spec->num_loopbacks++;
2340 spec->loopback.amplist = spec->loopback_list;
2341}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002342
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002343static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002344 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002345{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002346 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002347}
2348
2349/* add the input-route to the given pin */
2350static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002351{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002352 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002353 int c, idx;
2354
2355 spec->inputs[spec->num_inputs].adc_idx = -1;
2356 spec->inputs[spec->num_inputs].pin = pin;
2357 for (c = 0; c < spec->num_adc_nids; c++) {
2358 if (spec->mux_nids[c]) {
2359 idx = get_connection_index(codec, spec->mux_nids[c],
2360 pin);
2361 if (idx < 0)
2362 continue;
2363 spec->inputs[spec->num_inputs].mux_idx = idx;
2364 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002365 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002366 continue;
2367 }
2368 spec->inputs[spec->num_inputs].adc_idx = c;
2369 /* Can primary ADC satisfy all inputs? */
2370 if (!spec->dyn_adc_switch &&
2371 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2372 snd_printd(KERN_INFO
2373 "via: dynamic ADC switching enabled\n");
2374 spec->dyn_adc_switch = 1;
2375 }
2376 return true;
2377 }
2378 return false;
2379}
2380
2381static int get_mux_nids(struct hda_codec *codec);
2382
2383/* parse input-routes; fill ADCs, MUXs and input-src entries */
2384static int parse_analog_inputs(struct hda_codec *codec)
2385{
2386 struct via_spec *spec = codec->spec;
2387 const struct auto_pin_cfg *cfg = &spec->autocfg;
2388 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002389
2390 err = via_fill_adcs(codec);
2391 if (err < 0)
2392 return err;
2393 err = get_mux_nids(codec);
2394 if (err < 0)
2395 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002396
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002397 /* fill all input-routes */
2398 for (i = 0; i < cfg->num_inputs; i++) {
2399 if (add_input_route(codec, cfg->inputs[i].pin))
2400 spec->inputs[spec->num_inputs++].label =
2401 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002402 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002403
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002404 /* check for internal loopback recording */
2405 if (spec->aa_mix_nid &&
2406 add_input_route(codec, spec->aa_mix_nid))
2407 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2408
2409 return 0;
2410}
2411
2412/* create analog-loopback volume/switch controls */
2413static int create_loopback_ctls(struct hda_codec *codec)
2414{
2415 struct via_spec *spec = codec->spec;
2416 const struct auto_pin_cfg *cfg = &spec->autocfg;
2417 const char *prev_label = NULL;
2418 int type_idx = 0;
2419 int i, j, err, idx;
2420
2421 if (!spec->aa_mix_nid)
2422 return 0;
2423
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002424 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002425 hda_nid_t pin = cfg->inputs[i].pin;
2426 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2427
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002428 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002429 type_idx++;
2430 else
2431 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002432 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002433 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2434 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002435 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002436 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002437 if (err < 0)
2438 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002439 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002440 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002441
2442 /* remember the label for smart51 control */
2443 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002444 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002445 spec->smart51_idxs[j] = idx;
2446 spec->smart51_labels[j] = label;
2447 break;
2448 }
2449 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002450 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002451 return 0;
2452}
2453
2454/* create mic-boost controls (if present) */
2455static int create_mic_boost_ctls(struct hda_codec *codec)
2456{
2457 struct via_spec *spec = codec->spec;
2458 const struct auto_pin_cfg *cfg = &spec->autocfg;
2459 int i, err;
2460
2461 for (i = 0; i < cfg->num_inputs; i++) {
2462 hda_nid_t pin = cfg->inputs[i].pin;
2463 unsigned int caps;
2464 const char *label;
2465 char name[32];
2466
2467 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2468 continue;
2469 caps = query_amp_caps(codec, pin, HDA_INPUT);
2470 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2471 continue;
2472 label = hda_get_autocfg_input_label(codec, cfg, i);
2473 snprintf(name, sizeof(name), "%s Boost Volume", label);
2474 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2475 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2476 if (err < 0)
2477 return err;
2478 }
2479 return 0;
2480}
2481
2482/* create capture and input-src controls for multiple streams */
2483static int create_multi_adc_ctls(struct hda_codec *codec)
2484{
2485 struct via_spec *spec = codec->spec;
2486 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002487
2488 /* create capture mixer elements */
2489 for (i = 0; i < spec->num_adc_nids; i++) {
2490 hda_nid_t adc = spec->adc_nids[i];
2491 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2492 "Capture Volume", i,
2493 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2494 HDA_INPUT));
2495 if (err < 0)
2496 return err;
2497 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2498 "Capture Switch", i,
2499 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2500 HDA_INPUT));
2501 if (err < 0)
2502 return err;
2503 }
2504
2505 /* input-source control */
2506 for (i = 0; i < spec->num_adc_nids; i++)
2507 if (!spec->mux_nids[i])
2508 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002509 err = create_input_src_ctls(codec, i);
2510 if (err < 0)
2511 return err;
2512 return 0;
2513}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002514
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002515/* bind capture volume/switch */
2516static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2517 HDA_BIND_VOL("Capture Volume", 0);
2518static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2519 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002520
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002521static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2522 struct hda_ctl_ops *ops)
2523{
2524 struct hda_bind_ctls *ctl;
2525 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002526
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002527 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2528 if (!ctl)
2529 return -ENOMEM;
2530 ctl->ops = ops;
2531 for (i = 0; i < spec->num_adc_nids; i++)
2532 ctl->values[i] =
2533 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2534 *ctl_ret = ctl;
2535 return 0;
2536}
2537
2538/* create capture and input-src controls for dynamic ADC-switch case */
2539static int create_dyn_adc_ctls(struct hda_codec *codec)
2540{
2541 struct via_spec *spec = codec->spec;
2542 struct snd_kcontrol_new *knew;
2543 int err;
2544
2545 /* set up the bind capture ctls */
2546 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2547 if (err < 0)
2548 return err;
2549 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2550 if (err < 0)
2551 return err;
2552
2553 /* create capture mixer elements */
2554 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2555 if (!knew)
2556 return -ENOMEM;
2557 knew->private_value = (long)spec->bind_cap_vol;
2558
2559 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2560 if (!knew)
2561 return -ENOMEM;
2562 knew->private_value = (long)spec->bind_cap_sw;
2563
2564 /* input-source control */
2565 err = create_input_src_ctls(codec, 1);
2566 if (err < 0)
2567 return err;
2568 return 0;
2569}
2570
2571/* parse and create capture-related stuff */
2572static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2573{
2574 struct via_spec *spec = codec->spec;
2575 int err;
2576
2577 err = parse_analog_inputs(codec);
2578 if (err < 0)
2579 return err;
2580 if (spec->dyn_adc_switch)
2581 err = create_dyn_adc_ctls(codec);
2582 else
2583 err = create_multi_adc_ctls(codec);
2584 if (err < 0)
2585 return err;
2586 err = create_loopback_ctls(codec);
2587 if (err < 0)
2588 return err;
2589 err = create_mic_boost_ctls(codec);
2590 if (err < 0)
2591 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002592 return 0;
2593}
2594
Harald Welte76d9b0d2008-09-09 15:50:37 +08002595static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2596{
2597 unsigned int def_conf;
2598 unsigned char seqassoc;
2599
Takashi Iwai2f334f92009-02-20 14:37:42 +01002600 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002601 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2602 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002603 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2604 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2605 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2606 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002607 }
2608
2609 return;
2610}
2611
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002612static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002613 struct snd_ctl_elem_value *ucontrol)
2614{
2615 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2616 struct via_spec *spec = codec->spec;
2617
2618 if (spec->codec_type != VT1708)
2619 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002620 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002621 return 0;
2622}
2623
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002624static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002625 struct snd_ctl_elem_value *ucontrol)
2626{
2627 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2628 struct via_spec *spec = codec->spec;
Takashi Iwai187d3332011-11-24 16:33:09 +01002629 int val;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002630
2631 if (spec->codec_type != VT1708)
2632 return 0;
Takashi Iwai187d3332011-11-24 16:33:09 +01002633 val = !!ucontrol->value.integer.value[0];
2634 if (spec->vt1708_jack_detect == val)
2635 return 0;
2636 spec->vt1708_jack_detect = val;
2637 if (spec->vt1708_jack_detect &&
2638 snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") != 1) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002639 mute_aa_path(codec, 1);
2640 notify_aa_path_ctls(codec);
2641 }
Takashi Iwai187d3332011-11-24 16:33:09 +01002642 via_hp_automute(codec);
2643 vt1708_update_hp_work(spec);
2644 return 1;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002645}
2646
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002647static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2648 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2649 .name = "Jack Detect",
2650 .count = 1,
2651 .info = snd_ctl_boolean_mono_info,
2652 .get = vt1708_jack_detect_get,
2653 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002654};
2655
Takashi Iwai12daef62011-06-18 17:45:49 +02002656static void fill_dig_outs(struct hda_codec *codec);
2657static void fill_dig_in(struct hda_codec *codec);
2658
2659static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002660{
2661 struct via_spec *spec = codec->spec;
2662 int err;
2663
2664 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2665 if (err < 0)
2666 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002667 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002668 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002669
Takashi Iwai4a796162011-06-17 17:53:38 +02002670 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002671 if (err < 0)
2672 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002673 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002674 if (err < 0)
2675 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002676 err = via_auto_create_speaker_ctls(codec);
2677 if (err < 0)
2678 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002679 err = via_auto_create_loopback_switch(codec);
2680 if (err < 0)
2681 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002682 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002683 if (err < 0)
2684 return err;
2685
2686 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2687
Takashi Iwai12daef62011-06-18 17:45:49 +02002688 fill_dig_outs(codec);
2689 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002690
Takashi Iwai603c4012008-07-30 15:01:44 +02002691 if (spec->kctls.list)
2692 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002693
Joseph Chanc577b8a2006-11-29 15:29:40 +01002694
Takashi Iwai3214b962011-07-18 12:49:25 +02002695 if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002696 err = via_hp_build(codec);
2697 if (err < 0)
2698 return err;
2699 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002700
Takashi Iwaif4a78282011-06-17 18:46:48 +02002701 err = via_smart51_build(codec);
2702 if (err < 0)
2703 return err;
2704
Takashi Iwai5d417622011-06-20 11:32:27 +02002705 /* assign slave outs */
2706 if (spec->slave_dig_outs[0])
2707 codec->slave_dig_outs = spec->slave_dig_outs;
2708
Joseph Chanc577b8a2006-11-29 15:29:40 +01002709 return 1;
2710}
2711
Takashi Iwai5d417622011-06-20 11:32:27 +02002712static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002713{
Lydia Wang25eaba22009-10-10 19:08:43 +08002714 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002715 if (spec->multiout.dig_out_nid)
2716 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2717 if (spec->slave_dig_outs[0])
2718 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2719}
Lydia Wang25eaba22009-10-10 19:08:43 +08002720
Takashi Iwai5d417622011-06-20 11:32:27 +02002721static void via_auto_init_dig_in(struct hda_codec *codec)
2722{
2723 struct via_spec *spec = codec->spec;
2724 if (!spec->dig_in_nid)
2725 return;
2726 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2727 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2728}
2729
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002730/* initialize the unsolicited events */
2731static void via_auto_init_unsol_event(struct hda_codec *codec)
2732{
2733 struct via_spec *spec = codec->spec;
2734 struct auto_pin_cfg *cfg = &spec->autocfg;
2735 unsigned int ev;
2736 int i;
2737
2738 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2739 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2740 AC_VERB_SET_UNSOLICITED_ENABLE,
2741 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2742
2743 if (cfg->speaker_pins[0])
2744 ev = VIA_LINE_EVENT;
2745 else
2746 ev = 0;
2747 for (i = 0; i < cfg->line_outs; i++) {
2748 if (cfg->line_out_pins[i] &&
2749 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002750 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002751 AC_VERB_SET_UNSOLICITED_ENABLE,
2752 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2753 }
2754
2755 for (i = 0; i < cfg->num_inputs; i++) {
2756 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2757 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2758 AC_VERB_SET_UNSOLICITED_ENABLE,
2759 AC_USRSP_EN | VIA_JACK_EVENT);
2760 }
2761}
2762
Takashi Iwai5d417622011-06-20 11:32:27 +02002763static int via_init(struct hda_codec *codec)
2764{
2765 struct via_spec *spec = codec->spec;
2766 int i;
2767
2768 for (i = 0; i < spec->num_iverbs; i++)
2769 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2770
Joseph Chanc577b8a2006-11-29 15:29:40 +01002771 via_auto_init_multi_out(codec);
2772 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002773 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002774 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002775 via_auto_init_dig_outs(codec);
2776 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002777
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002778 via_auto_init_unsol_event(codec);
2779
2780 via_hp_automute(codec);
Takashi Iwai187d3332011-11-24 16:33:09 +01002781 vt1708_update_hp_work(spec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002782
Joseph Chanc577b8a2006-11-29 15:29:40 +01002783 return 0;
2784}
2785
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002786static void vt1708_update_hp_jack_state(struct work_struct *work)
2787{
2788 struct via_spec *spec = container_of(work, struct via_spec,
2789 vt1708_hp_work.work);
2790 if (spec->codec_type != VT1708)
2791 return;
2792 /* 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 }
Takashi Iwai187d3332011-11-24 16:33:09 +01002798 if (spec->vt1708_jack_detect)
2799 schedule_delayed_work(&spec->vt1708_hp_work,
2800 msecs_to_jiffies(100));
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002801}
2802
Takashi Iwai337b9d02009-07-07 18:18:59 +02002803static int get_mux_nids(struct hda_codec *codec)
2804{
2805 struct via_spec *spec = codec->spec;
2806 hda_nid_t nid, conn[8];
2807 unsigned int type;
2808 int i, n;
2809
2810 for (i = 0; i < spec->num_adc_nids; i++) {
2811 nid = spec->adc_nids[i];
2812 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002813 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002814 if (type == AC_WID_PIN)
2815 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002816 n = snd_hda_get_connections(codec, nid, conn,
2817 ARRAY_SIZE(conn));
2818 if (n <= 0)
2819 break;
2820 if (n > 1) {
2821 spec->mux_nids[i] = nid;
2822 break;
2823 }
2824 nid = conn[0];
2825 }
2826 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002827 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002828}
2829
Joseph Chanc577b8a2006-11-29 15:29:40 +01002830static int patch_vt1708(struct hda_codec *codec)
2831{
2832 struct via_spec *spec;
2833 int err;
2834
2835 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002836 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002837 if (spec == NULL)
2838 return -ENOMEM;
2839
Takashi Iwai620e2b22011-06-17 17:19:19 +02002840 spec->aa_mix_nid = 0x17;
2841
Takashi Iwai12daef62011-06-18 17:45:49 +02002842 /* Add HP and CD pin config connect bit re-config action */
2843 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2844 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2845
Joseph Chanc577b8a2006-11-29 15:29:40 +01002846 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002847 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002848 if (err < 0) {
2849 via_free(codec);
2850 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002851 }
2852
Takashi Iwai12daef62011-06-18 17:45:49 +02002853 /* add jack detect on/off control */
2854 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2855 return -ENOMEM;
2856
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002857 /* disable 32bit format on VT1708 */
2858 if (codec->vendor_id == 0x11061708)
2859 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002860
Lydia Wange322a362011-06-29 13:52:02 +08002861 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2862
Joseph Chanc577b8a2006-11-29 15:29:40 +01002863 codec->patch_ops = via_patch_ops;
2864
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002865 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002866 return 0;
2867}
2868
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002869static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002870{
2871 struct via_spec *spec;
2872 int err;
2873
2874 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002875 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002876 if (spec == NULL)
2877 return -ENOMEM;
2878
Takashi Iwai620e2b22011-06-17 17:19:19 +02002879 spec->aa_mix_nid = 0x18;
2880
Takashi Iwai12daef62011-06-18 17:45:49 +02002881 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002882 if (err < 0) {
2883 via_free(codec);
2884 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002885 }
2886
Joseph Chanc577b8a2006-11-29 15:29:40 +01002887 codec->patch_ops = via_patch_ops;
2888
Josepch Chanf7278fd2007-12-13 16:40:40 +01002889 return 0;
2890}
2891
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002892static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2893{
2894 struct via_spec *spec = codec->spec;
2895 int imux_is_smixer;
2896 unsigned int parm;
2897 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002898 if ((spec->codec_type != VT1708B_4CH) &&
2899 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002900 is_8ch = 1;
2901
2902 /* SW0 (17h) = stereo mixer */
2903 imux_is_smixer =
2904 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2905 == ((spec->codec_type == VT1708S) ? 5 : 0));
2906 /* inputs */
2907 /* PW 1/2/5 (1ah/1bh/1eh) */
2908 parm = AC_PWRST_D3;
2909 set_pin_power_state(codec, 0x1a, &parm);
2910 set_pin_power_state(codec, 0x1b, &parm);
2911 set_pin_power_state(codec, 0x1e, &parm);
2912 if (imux_is_smixer)
2913 parm = AC_PWRST_D0;
2914 /* SW0 (17h), AIW 0/1 (13h/14h) */
2915 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2916 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2917 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2918
2919 /* outputs */
2920 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2921 parm = AC_PWRST_D3;
2922 set_pin_power_state(codec, 0x19, &parm);
2923 if (spec->smart51_enabled)
2924 set_pin_power_state(codec, 0x1b, &parm);
2925 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2926 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2927
2928 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2929 if (is_8ch) {
2930 parm = AC_PWRST_D3;
2931 set_pin_power_state(codec, 0x22, &parm);
2932 if (spec->smart51_enabled)
2933 set_pin_power_state(codec, 0x1a, &parm);
2934 snd_hda_codec_write(codec, 0x26, 0,
2935 AC_VERB_SET_POWER_STATE, parm);
2936 snd_hda_codec_write(codec, 0x24, 0,
2937 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002938 } else if (codec->vendor_id == 0x11064397) {
2939 /* PW7(23h), SW2(27h), AOW2(25h) */
2940 parm = AC_PWRST_D3;
2941 set_pin_power_state(codec, 0x23, &parm);
2942 if (spec->smart51_enabled)
2943 set_pin_power_state(codec, 0x1a, &parm);
2944 snd_hda_codec_write(codec, 0x27, 0,
2945 AC_VERB_SET_POWER_STATE, parm);
2946 snd_hda_codec_write(codec, 0x25, 0,
2947 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002948 }
2949
2950 /* PW 3/4/7 (1ch/1dh/23h) */
2951 parm = AC_PWRST_D3;
2952 /* force to D0 for internal Speaker */
2953 set_pin_power_state(codec, 0x1c, &parm);
2954 set_pin_power_state(codec, 0x1d, &parm);
2955 if (is_8ch)
2956 set_pin_power_state(codec, 0x23, &parm);
2957
2958 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2959 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2960 imux_is_smixer ? AC_PWRST_D0 : parm);
2961 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2962 if (is_8ch) {
2963 snd_hda_codec_write(codec, 0x25, 0,
2964 AC_VERB_SET_POWER_STATE, parm);
2965 snd_hda_codec_write(codec, 0x27, 0,
2966 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002967 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2968 snd_hda_codec_write(codec, 0x25, 0,
2969 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002970}
2971
Lydia Wang518bf3b2009-10-10 19:07:29 +08002972static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002973static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002974{
2975 struct via_spec *spec;
2976 int err;
2977
Lydia Wang518bf3b2009-10-10 19:07:29 +08002978 if (get_codec_type(codec) == VT1708BCE)
2979 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002980
Josepch Chanf7278fd2007-12-13 16:40:40 +01002981 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002982 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002983 if (spec == NULL)
2984 return -ENOMEM;
2985
Takashi Iwai620e2b22011-06-17 17:19:19 +02002986 spec->aa_mix_nid = 0x16;
2987
Josepch Chanf7278fd2007-12-13 16:40:40 +01002988 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002989 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002990 if (err < 0) {
2991 via_free(codec);
2992 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002993 }
2994
Josepch Chanf7278fd2007-12-13 16:40:40 +01002995 codec->patch_ops = via_patch_ops;
2996
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002997 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2998
Josepch Chanf7278fd2007-12-13 16:40:40 +01002999 return 0;
3000}
3001
Harald Welted949cac2008-09-09 15:56:01 +08003002/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02003003static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08003004 /* Enable Mic Boost Volume backdoor */
3005 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003006 /* don't bybass mixer */
3007 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08003008 { }
3009};
3010
Takashi Iwai9da29272009-05-07 16:31:14 +02003011/* fill out digital output widgets; one for master and one for slave outputs */
3012static void fill_dig_outs(struct hda_codec *codec)
3013{
3014 struct via_spec *spec = codec->spec;
3015 int i;
3016
3017 for (i = 0; i < spec->autocfg.dig_outs; i++) {
3018 hda_nid_t nid;
3019 int conn;
3020
3021 nid = spec->autocfg.dig_out_pins[i];
3022 if (!nid)
3023 continue;
3024 conn = snd_hda_get_connections(codec, nid, &nid, 1);
3025 if (conn < 1)
3026 continue;
3027 if (!spec->multiout.dig_out_nid)
3028 spec->multiout.dig_out_nid = nid;
3029 else {
3030 spec->slave_dig_outs[0] = nid;
3031 break; /* at most two dig outs */
3032 }
3033 }
3034}
3035
Takashi Iwai12daef62011-06-18 17:45:49 +02003036static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08003037{
3038 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02003039 hda_nid_t dig_nid;
3040 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08003041
Takashi Iwai12daef62011-06-18 17:45:49 +02003042 if (!spec->autocfg.dig_in_pin)
3043 return;
Harald Welted949cac2008-09-09 15:56:01 +08003044
Takashi Iwai12daef62011-06-18 17:45:49 +02003045 dig_nid = codec->start_nid;
3046 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
3047 unsigned int wcaps = get_wcaps(codec, dig_nid);
3048 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
3049 continue;
3050 if (!(wcaps & AC_WCAP_DIGITAL))
3051 continue;
3052 if (!(wcaps & AC_WCAP_CONN_LIST))
3053 continue;
3054 err = get_connection_index(codec, dig_nid,
3055 spec->autocfg.dig_in_pin);
3056 if (err >= 0) {
3057 spec->dig_in_nid = dig_nid;
3058 break;
3059 }
3060 }
Harald Welted949cac2008-09-09 15:56:01 +08003061}
3062
Lydia Wang6369bcf2009-10-10 19:08:31 +08003063static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
3064 int offset, int num_steps, int step_size)
3065{
3066 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
3067 (offset << AC_AMPCAP_OFFSET_SHIFT) |
3068 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
3069 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
3070 (0 << AC_AMPCAP_MUTE_SHIFT));
3071}
3072
Harald Welted949cac2008-09-09 15:56:01 +08003073static int patch_vt1708S(struct hda_codec *codec)
3074{
3075 struct via_spec *spec;
3076 int err;
3077
3078 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003079 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003080 if (spec == NULL)
3081 return -ENOMEM;
3082
Takashi Iwai620e2b22011-06-17 17:19:19 +02003083 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003084 override_mic_boost(codec, 0x1a, 0, 3, 40);
3085 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003086
Harald Welted949cac2008-09-09 15:56:01 +08003087 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003088 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003089 if (err < 0) {
3090 via_free(codec);
3091 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003092 }
3093
Takashi Iwai096a8852011-06-20 12:09:02 +02003094 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003095
Harald Welted949cac2008-09-09 15:56:01 +08003096 codec->patch_ops = via_patch_ops;
3097
Lydia Wang518bf3b2009-10-10 19:07:29 +08003098 /* correct names for VT1708BCE */
3099 if (get_codec_type(codec) == VT1708BCE) {
3100 kfree(codec->chip_name);
3101 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3102 snprintf(codec->bus->card->mixername,
3103 sizeof(codec->bus->card->mixername),
3104 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08003105 }
Lydia Wangbc92df72011-03-23 17:56:05 +08003106 /* correct names for VT1705 */
3107 if (codec->vendor_id == 0x11064397) {
3108 kfree(codec->chip_name);
3109 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3110 snprintf(codec->bus->card->mixername,
3111 sizeof(codec->bus->card->mixername),
3112 "%s %s", codec->vendor_name, codec->chip_name);
3113 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003114 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08003115 return 0;
3116}
3117
3118/* Patch for VT1702 */
3119
Takashi Iwai096a8852011-06-20 12:09:02 +02003120static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003121 /* mixer enable */
3122 {0x1, 0xF88, 0x3},
3123 /* GPIO 0~2 */
3124 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003125 { }
3126};
3127
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003128static void set_widgets_power_state_vt1702(struct hda_codec *codec)
3129{
3130 int imux_is_smixer =
3131 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3132 unsigned int parm;
3133 /* inputs */
3134 /* PW 1/2/5 (14h/15h/18h) */
3135 parm = AC_PWRST_D3;
3136 set_pin_power_state(codec, 0x14, &parm);
3137 set_pin_power_state(codec, 0x15, &parm);
3138 set_pin_power_state(codec, 0x18, &parm);
3139 if (imux_is_smixer)
3140 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
3141 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
3142 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3143 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
3144 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3145 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
3146
3147 /* outputs */
3148 /* PW 3/4 (16h/17h) */
3149 parm = AC_PWRST_D3;
3150 set_pin_power_state(codec, 0x17, &parm);
3151 set_pin_power_state(codec, 0x16, &parm);
3152 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
3153 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
3154 imux_is_smixer ? AC_PWRST_D0 : parm);
3155 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3156 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3157}
3158
Harald Welted949cac2008-09-09 15:56:01 +08003159static int patch_vt1702(struct hda_codec *codec)
3160{
3161 struct via_spec *spec;
3162 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003163
3164 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003165 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003166 if (spec == NULL)
3167 return -ENOMEM;
3168
Takashi Iwai620e2b22011-06-17 17:19:19 +02003169 spec->aa_mix_nid = 0x1a;
3170
Takashi Iwai12daef62011-06-18 17:45:49 +02003171 /* limit AA path volume to 0 dB */
3172 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3173 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3174 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3175 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3176 (1 << AC_AMPCAP_MUTE_SHIFT));
3177
Harald Welted949cac2008-09-09 15:56:01 +08003178 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003179 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003180 if (err < 0) {
3181 via_free(codec);
3182 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003183 }
3184
Takashi Iwai096a8852011-06-20 12:09:02 +02003185 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003186
Harald Welted949cac2008-09-09 15:56:01 +08003187 codec->patch_ops = via_patch_ops;
3188
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003189 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08003190 return 0;
3191}
3192
Lydia Wangeb7188c2009-10-10 19:08:34 +08003193/* Patch for VT1718S */
3194
Takashi Iwai096a8852011-06-20 12:09:02 +02003195static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08003196 /* Enable MW0 adjust Gain 5 */
3197 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003198 /* Enable Boost Volume backdoor */
3199 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02003200
Lydia Wangeb7188c2009-10-10 19:08:34 +08003201 { }
3202};
3203
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003204static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
3205{
3206 struct via_spec *spec = codec->spec;
3207 int imux_is_smixer;
3208 unsigned int parm;
3209 /* MUX6 (1eh) = stereo mixer */
3210 imux_is_smixer =
3211 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3212 /* inputs */
3213 /* PW 5/6/7 (29h/2ah/2bh) */
3214 parm = AC_PWRST_D3;
3215 set_pin_power_state(codec, 0x29, &parm);
3216 set_pin_power_state(codec, 0x2a, &parm);
3217 set_pin_power_state(codec, 0x2b, &parm);
3218 if (imux_is_smixer)
3219 parm = AC_PWRST_D0;
3220 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
3221 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3222 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3223 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3224 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3225
3226 /* outputs */
3227 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
3228 parm = AC_PWRST_D3;
3229 set_pin_power_state(codec, 0x27, &parm);
3230 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
3231 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
3232
3233 /* PW2 (26h), AOW2 (ah) */
3234 parm = AC_PWRST_D3;
3235 set_pin_power_state(codec, 0x26, &parm);
3236 if (spec->smart51_enabled)
3237 set_pin_power_state(codec, 0x2b, &parm);
3238 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
3239
3240 /* PW0 (24h), AOW0 (8h) */
3241 parm = AC_PWRST_D3;
3242 set_pin_power_state(codec, 0x24, &parm);
3243 if (!spec->hp_independent_mode) /* check for redirected HP */
3244 set_pin_power_state(codec, 0x28, &parm);
3245 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3246 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
3247 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
3248 imux_is_smixer ? AC_PWRST_D0 : parm);
3249
3250 /* PW1 (25h), AOW1 (9h) */
3251 parm = AC_PWRST_D3;
3252 set_pin_power_state(codec, 0x25, &parm);
3253 if (spec->smart51_enabled)
3254 set_pin_power_state(codec, 0x2a, &parm);
3255 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
3256
3257 if (spec->hp_independent_mode) {
3258 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3259 parm = AC_PWRST_D3;
3260 set_pin_power_state(codec, 0x28, &parm);
3261 snd_hda_codec_write(codec, 0x1b, 0,
3262 AC_VERB_SET_POWER_STATE, parm);
3263 snd_hda_codec_write(codec, 0x34, 0,
3264 AC_VERB_SET_POWER_STATE, parm);
3265 snd_hda_codec_write(codec, 0xc, 0,
3266 AC_VERB_SET_POWER_STATE, parm);
3267 }
3268}
3269
Takashi Iwai30b45032011-07-11 17:05:04 +02003270/* Add a connection to the primary DAC from AA-mixer for some codecs
3271 * This isn't listed from the raw info, but the chip has a secret connection.
3272 */
3273static int add_secret_dac_path(struct hda_codec *codec)
3274{
3275 struct via_spec *spec = codec->spec;
3276 int i, nums;
3277 hda_nid_t conn[8];
3278 hda_nid_t nid;
3279
3280 if (!spec->aa_mix_nid)
3281 return 0;
3282 nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
3283 ARRAY_SIZE(conn) - 1);
3284 for (i = 0; i < nums; i++) {
3285 if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
3286 return 0;
3287 }
3288
3289 /* find the primary DAC and add to the connection list */
3290 nid = codec->start_nid;
3291 for (i = 0; i < codec->num_nodes; i++, nid++) {
3292 unsigned int caps = get_wcaps(codec, nid);
3293 if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
3294 !(caps & AC_WCAP_DIGITAL)) {
3295 conn[nums++] = nid;
3296 return snd_hda_override_conn_list(codec,
3297 spec->aa_mix_nid,
3298 nums, conn);
3299 }
3300 }
3301 return 0;
3302}
3303
3304
Lydia Wangeb7188c2009-10-10 19:08:34 +08003305static int patch_vt1718S(struct hda_codec *codec)
3306{
3307 struct via_spec *spec;
3308 int err;
3309
3310 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003311 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003312 if (spec == NULL)
3313 return -ENOMEM;
3314
Takashi Iwai620e2b22011-06-17 17:19:19 +02003315 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003316 override_mic_boost(codec, 0x2b, 0, 3, 40);
3317 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003318 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003319
Lydia Wangeb7188c2009-10-10 19:08:34 +08003320 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003321 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003322 if (err < 0) {
3323 via_free(codec);
3324 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003325 }
3326
Takashi Iwai096a8852011-06-20 12:09:02 +02003327 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003328
Lydia Wangeb7188c2009-10-10 19:08:34 +08003329 codec->patch_ops = via_patch_ops;
3330
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003331 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3332
Lydia Wangeb7188c2009-10-10 19:08:34 +08003333 return 0;
3334}
Lydia Wangf3db4232009-10-10 19:08:41 +08003335
3336/* Patch for VT1716S */
3337
3338static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3339 struct snd_ctl_elem_info *uinfo)
3340{
3341 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3342 uinfo->count = 1;
3343 uinfo->value.integer.min = 0;
3344 uinfo->value.integer.max = 1;
3345 return 0;
3346}
3347
3348static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3349 struct snd_ctl_elem_value *ucontrol)
3350{
3351 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3352 int index = 0;
3353
3354 index = snd_hda_codec_read(codec, 0x26, 0,
3355 AC_VERB_GET_CONNECT_SEL, 0);
3356 if (index != -1)
3357 *ucontrol->value.integer.value = index;
3358
3359 return 0;
3360}
3361
3362static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3363 struct snd_ctl_elem_value *ucontrol)
3364{
3365 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3366 struct via_spec *spec = codec->spec;
3367 int index = *ucontrol->value.integer.value;
3368
3369 snd_hda_codec_write(codec, 0x26, 0,
3370 AC_VERB_SET_CONNECT_SEL, index);
3371 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003372 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003373 return 1;
3374}
3375
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003376static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003377 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3378 {
3379 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3380 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003381 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003382 .count = 1,
3383 .info = vt1716s_dmic_info,
3384 .get = vt1716s_dmic_get,
3385 .put = vt1716s_dmic_put,
3386 },
3387 {} /* end */
3388};
3389
3390
3391/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003392static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003393 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3394 { } /* end */
3395};
3396
Takashi Iwai096a8852011-06-20 12:09:02 +02003397static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003398 /* Enable Boost Volume backdoor */
3399 {0x1, 0xf8a, 0x80},
3400 /* don't bybass mixer */
3401 {0x1, 0xf88, 0xc0},
3402 /* Enable mono output */
3403 {0x1, 0xf90, 0x08},
3404 { }
3405};
3406
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003407static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3408{
3409 struct via_spec *spec = codec->spec;
3410 int imux_is_smixer;
3411 unsigned int parm;
3412 unsigned int mono_out, present;
3413 /* SW0 (17h) = stereo mixer */
3414 imux_is_smixer =
3415 (snd_hda_codec_read(codec, 0x17, 0,
3416 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3417 /* inputs */
3418 /* PW 1/2/5 (1ah/1bh/1eh) */
3419 parm = AC_PWRST_D3;
3420 set_pin_power_state(codec, 0x1a, &parm);
3421 set_pin_power_state(codec, 0x1b, &parm);
3422 set_pin_power_state(codec, 0x1e, &parm);
3423 if (imux_is_smixer)
3424 parm = AC_PWRST_D0;
3425 /* SW0 (17h), AIW0(13h) */
3426 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3427 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3428
3429 parm = AC_PWRST_D3;
3430 set_pin_power_state(codec, 0x1e, &parm);
3431 /* PW11 (22h) */
3432 if (spec->dmic_enabled)
3433 set_pin_power_state(codec, 0x22, &parm);
3434 else
3435 snd_hda_codec_write(codec, 0x22, 0,
3436 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3437
3438 /* SW2(26h), AIW1(14h) */
3439 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3440 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3441
3442 /* outputs */
3443 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3444 parm = AC_PWRST_D3;
3445 set_pin_power_state(codec, 0x19, &parm);
3446 /* Smart 5.1 PW2(1bh) */
3447 if (spec->smart51_enabled)
3448 set_pin_power_state(codec, 0x1b, &parm);
3449 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3450 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3451
3452 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3453 parm = AC_PWRST_D3;
3454 set_pin_power_state(codec, 0x23, &parm);
3455 /* Smart 5.1 PW1(1ah) */
3456 if (spec->smart51_enabled)
3457 set_pin_power_state(codec, 0x1a, &parm);
3458 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3459
3460 /* Smart 5.1 PW5(1eh) */
3461 if (spec->smart51_enabled)
3462 set_pin_power_state(codec, 0x1e, &parm);
3463 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3464
3465 /* Mono out */
3466 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3467 present = snd_hda_jack_detect(codec, 0x1c);
3468
3469 if (present)
3470 mono_out = 0;
3471 else {
3472 present = snd_hda_jack_detect(codec, 0x1d);
3473 if (!spec->hp_independent_mode && present)
3474 mono_out = 0;
3475 else
3476 mono_out = 1;
3477 }
3478 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3479 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3480 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3481 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3482
3483 /* PW 3/4 (1ch/1dh) */
3484 parm = AC_PWRST_D3;
3485 set_pin_power_state(codec, 0x1c, &parm);
3486 set_pin_power_state(codec, 0x1d, &parm);
3487 /* HP Independent Mode, power on AOW3 */
3488 if (spec->hp_independent_mode)
3489 snd_hda_codec_write(codec, 0x25, 0,
3490 AC_VERB_SET_POWER_STATE, parm);
3491
3492 /* force to D0 for internal Speaker */
3493 /* MW0 (16h), AOW0 (10h) */
3494 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3495 imux_is_smixer ? AC_PWRST_D0 : parm);
3496 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3497 mono_out ? AC_PWRST_D0 : parm);
3498}
3499
Lydia Wangf3db4232009-10-10 19:08:41 +08003500static int patch_vt1716S(struct hda_codec *codec)
3501{
3502 struct via_spec *spec;
3503 int err;
3504
3505 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003506 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003507 if (spec == NULL)
3508 return -ENOMEM;
3509
Takashi Iwai620e2b22011-06-17 17:19:19 +02003510 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003511 override_mic_boost(codec, 0x1a, 0, 3, 40);
3512 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003513
Lydia Wangf3db4232009-10-10 19:08:41 +08003514 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003515 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003516 if (err < 0) {
3517 via_free(codec);
3518 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003519 }
3520
Takashi Iwai096a8852011-06-20 12:09:02 +02003521 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003522
Lydia Wangf3db4232009-10-10 19:08:41 +08003523 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3524 spec->num_mixers++;
3525
3526 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3527
3528 codec->patch_ops = via_patch_ops;
3529
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003530 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003531 return 0;
3532}
Lydia Wang25eaba22009-10-10 19:08:43 +08003533
3534/* for vt2002P */
3535
Takashi Iwai096a8852011-06-20 12:09:02 +02003536static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003537 /* Class-D speaker related verbs */
3538 {0x1, 0xfe0, 0x4},
3539 {0x1, 0xfe9, 0x80},
3540 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003541 /* Enable Boost Volume backdoor */
3542 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003543 /* Enable AOW0 to MW9 */
3544 {0x1, 0xfb8, 0x88},
3545 { }
3546};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003547
Takashi Iwai096a8852011-06-20 12:09:02 +02003548static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003549 /* Enable Boost Volume backdoor */
3550 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003551 /* Enable AOW0 to MW9 */
3552 {0x1, 0xfb8, 0x88},
3553 { }
3554};
Lydia Wang25eaba22009-10-10 19:08:43 +08003555
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003556static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3557{
3558 struct via_spec *spec = codec->spec;
3559 int imux_is_smixer;
3560 unsigned int parm;
3561 unsigned int present;
3562 /* MUX9 (1eh) = stereo mixer */
3563 imux_is_smixer =
3564 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3565 /* inputs */
3566 /* PW 5/6/7 (29h/2ah/2bh) */
3567 parm = AC_PWRST_D3;
3568 set_pin_power_state(codec, 0x29, &parm);
3569 set_pin_power_state(codec, 0x2a, &parm);
3570 set_pin_power_state(codec, 0x2b, &parm);
3571 parm = AC_PWRST_D0;
3572 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3573 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3574 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3575 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3576 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3577
3578 /* outputs */
3579 /* AOW0 (8h)*/
3580 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3581
Lydia Wang118909562011-03-23 17:57:34 +08003582 if (spec->codec_type == VT1802) {
3583 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3584 parm = AC_PWRST_D3;
3585 set_pin_power_state(codec, 0x28, &parm);
3586 snd_hda_codec_write(codec, 0x18, 0,
3587 AC_VERB_SET_POWER_STATE, parm);
3588 snd_hda_codec_write(codec, 0x38, 0,
3589 AC_VERB_SET_POWER_STATE, parm);
3590 } else {
3591 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3592 parm = AC_PWRST_D3;
3593 set_pin_power_state(codec, 0x26, &parm);
3594 snd_hda_codec_write(codec, 0x1c, 0,
3595 AC_VERB_SET_POWER_STATE, parm);
3596 snd_hda_codec_write(codec, 0x37, 0,
3597 AC_VERB_SET_POWER_STATE, parm);
3598 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003599
Lydia Wang118909562011-03-23 17:57:34 +08003600 if (spec->codec_type == VT1802) {
3601 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3602 parm = AC_PWRST_D3;
3603 set_pin_power_state(codec, 0x25, &parm);
3604 snd_hda_codec_write(codec, 0x15, 0,
3605 AC_VERB_SET_POWER_STATE, parm);
3606 snd_hda_codec_write(codec, 0x35, 0,
3607 AC_VERB_SET_POWER_STATE, parm);
3608 } else {
3609 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3610 parm = AC_PWRST_D3;
3611 set_pin_power_state(codec, 0x25, &parm);
3612 snd_hda_codec_write(codec, 0x19, 0,
3613 AC_VERB_SET_POWER_STATE, parm);
3614 snd_hda_codec_write(codec, 0x35, 0,
3615 AC_VERB_SET_POWER_STATE, parm);
3616 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003617
3618 if (spec->hp_independent_mode)
3619 snd_hda_codec_write(codec, 0x9, 0,
3620 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3621
3622 /* Class-D */
3623 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3624 present = snd_hda_jack_detect(codec, 0x25);
3625
3626 parm = AC_PWRST_D3;
3627 set_pin_power_state(codec, 0x24, &parm);
3628 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003629 if (spec->codec_type == VT1802)
3630 snd_hda_codec_write(codec, 0x14, 0,
3631 AC_VERB_SET_POWER_STATE, parm);
3632 else
3633 snd_hda_codec_write(codec, 0x18, 0,
3634 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003635 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3636
3637 /* Mono Out */
3638 present = snd_hda_jack_detect(codec, 0x26);
3639
3640 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003641 if (spec->codec_type == VT1802) {
3642 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3643 snd_hda_codec_write(codec, 0x33, 0,
3644 AC_VERB_SET_POWER_STATE, parm);
3645 snd_hda_codec_write(codec, 0x1c, 0,
3646 AC_VERB_SET_POWER_STATE, parm);
3647 snd_hda_codec_write(codec, 0x3c, 0,
3648 AC_VERB_SET_POWER_STATE, parm);
3649 } else {
3650 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3651 snd_hda_codec_write(codec, 0x31, 0,
3652 AC_VERB_SET_POWER_STATE, parm);
3653 snd_hda_codec_write(codec, 0x17, 0,
3654 AC_VERB_SET_POWER_STATE, parm);
3655 snd_hda_codec_write(codec, 0x3b, 0,
3656 AC_VERB_SET_POWER_STATE, parm);
3657 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003658 /* MW9 (21h) */
3659 if (imux_is_smixer || !is_aa_path_mute(codec))
3660 snd_hda_codec_write(codec, 0x21, 0,
3661 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3662 else
3663 snd_hda_codec_write(codec, 0x21, 0,
3664 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3665}
Lydia Wang25eaba22009-10-10 19:08:43 +08003666
3667/* patch for vt2002P */
3668static int patch_vt2002P(struct hda_codec *codec)
3669{
3670 struct via_spec *spec;
3671 int err;
3672
3673 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003674 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003675 if (spec == NULL)
3676 return -ENOMEM;
3677
Takashi Iwai620e2b22011-06-17 17:19:19 +02003678 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003679 override_mic_boost(codec, 0x2b, 0, 3, 40);
3680 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003681 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003682
Lydia Wang25eaba22009-10-10 19:08:43 +08003683 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003684 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003685 if (err < 0) {
3686 via_free(codec);
3687 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003688 }
3689
Lydia Wang118909562011-03-23 17:57:34 +08003690 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003691 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003692 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003693 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003694
Lydia Wang25eaba22009-10-10 19:08:43 +08003695 codec->patch_ops = via_patch_ops;
3696
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003697 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003698 return 0;
3699}
Lydia Wangab6734e2009-10-10 19:08:46 +08003700
3701/* for vt1812 */
3702
Takashi Iwai096a8852011-06-20 12:09:02 +02003703static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003704 /* Enable Boost Volume backdoor */
3705 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003706 /* Enable AOW0 to MW9 */
3707 {0x1, 0xfb8, 0xa8},
3708 { }
3709};
3710
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003711static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3712{
3713 struct via_spec *spec = codec->spec;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003714 unsigned int parm;
3715 unsigned int present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003716 /* inputs */
3717 /* PW 5/6/7 (29h/2ah/2bh) */
3718 parm = AC_PWRST_D3;
3719 set_pin_power_state(codec, 0x29, &parm);
3720 set_pin_power_state(codec, 0x2a, &parm);
3721 set_pin_power_state(codec, 0x2b, &parm);
3722 parm = AC_PWRST_D0;
3723 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3724 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3725 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3726 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3727 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3728
3729 /* outputs */
3730 /* AOW0 (8h)*/
3731 snd_hda_codec_write(codec, 0x8, 0,
3732 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3733
3734 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3735 parm = AC_PWRST_D3;
3736 set_pin_power_state(codec, 0x28, &parm);
3737 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3738 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3739
3740 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3741 parm = AC_PWRST_D3;
3742 set_pin_power_state(codec, 0x25, &parm);
3743 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3744 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3745 if (spec->hp_independent_mode)
3746 snd_hda_codec_write(codec, 0x9, 0,
3747 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3748
3749 /* Internal Speaker */
3750 /* PW0 (24h), MW0(14h), MUX0(34h) */
3751 present = snd_hda_jack_detect(codec, 0x25);
3752
3753 parm = AC_PWRST_D3;
3754 set_pin_power_state(codec, 0x24, &parm);
3755 if (present) {
3756 snd_hda_codec_write(codec, 0x14, 0,
3757 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3758 snd_hda_codec_write(codec, 0x34, 0,
3759 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3760 } else {
3761 snd_hda_codec_write(codec, 0x14, 0,
3762 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3763 snd_hda_codec_write(codec, 0x34, 0,
3764 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3765 }
3766
3767
3768 /* Mono Out */
3769 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3770 present = snd_hda_jack_detect(codec, 0x28);
3771
3772 parm = AC_PWRST_D3;
3773 set_pin_power_state(codec, 0x31, &parm);
3774 if (present) {
3775 snd_hda_codec_write(codec, 0x1c, 0,
3776 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3777 snd_hda_codec_write(codec, 0x3c, 0,
3778 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3779 snd_hda_codec_write(codec, 0x3e, 0,
3780 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3781 } else {
3782 snd_hda_codec_write(codec, 0x1c, 0,
3783 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3784 snd_hda_codec_write(codec, 0x3c, 0,
3785 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3786 snd_hda_codec_write(codec, 0x3e, 0,
3787 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3788 }
3789
3790 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3791 parm = AC_PWRST_D3;
3792 set_pin_power_state(codec, 0x33, &parm);
3793 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3794 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3795
3796}
Lydia Wangab6734e2009-10-10 19:08:46 +08003797
3798/* patch for vt1812 */
3799static int patch_vt1812(struct hda_codec *codec)
3800{
3801 struct via_spec *spec;
3802 int err;
3803
3804 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003805 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003806 if (spec == NULL)
3807 return -ENOMEM;
3808
Takashi Iwai620e2b22011-06-17 17:19:19 +02003809 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003810 override_mic_boost(codec, 0x2b, 0, 3, 40);
3811 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003812 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003813
Lydia Wangab6734e2009-10-10 19:08:46 +08003814 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003815 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003816 if (err < 0) {
3817 via_free(codec);
3818 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003819 }
3820
Takashi Iwai096a8852011-06-20 12:09:02 +02003821 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003822
Lydia Wangab6734e2009-10-10 19:08:46 +08003823 codec->patch_ops = via_patch_ops;
3824
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003825 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003826 return 0;
3827}
3828
Joseph Chanc577b8a2006-11-29 15:29:40 +01003829/*
3830 * patch entries
3831 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003832static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003833 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3834 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3835 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3836 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3837 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003838 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003839 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003840 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003841 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003842 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003843 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003844 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003845 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003846 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003847 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003848 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003849 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003850 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003851 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003852 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003853 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003854 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003855 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003856 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003857 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003858 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003859 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003860 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003861 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003862 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003863 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003864 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003865 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003866 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003867 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003868 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003869 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003870 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003871 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003872 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003873 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003874 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003875 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003876 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003877 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003878 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003879 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003880 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003881 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003882 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003883 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003884 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003885 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003886 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003887 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003888 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003889 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003890 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003891 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003892 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003893 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003894 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003895 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003896 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003897 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003898 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003899 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003900 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003901 { .id = 0x11060428, .name = "VT1718S",
3902 .patch = patch_vt1718S},
3903 { .id = 0x11064428, .name = "VT1718S",
3904 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003905 { .id = 0x11060441, .name = "VT2020",
3906 .patch = patch_vt1718S},
3907 { .id = 0x11064441, .name = "VT1828S",
3908 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003909 { .id = 0x11060433, .name = "VT1716S",
3910 .patch = patch_vt1716S},
3911 { .id = 0x1106a721, .name = "VT1716S",
3912 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003913 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3914 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003915 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003916 { .id = 0x11060440, .name = "VT1818S",
3917 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003918 { .id = 0x11060446, .name = "VT1802",
3919 .patch = patch_vt2002P},
3920 { .id = 0x11068446, .name = "VT1802",
3921 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003922 {} /* terminator */
3923};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003924
3925MODULE_ALIAS("snd-hda-codec-id:1106*");
3926
3927static struct hda_codec_preset_list via_list = {
3928 .preset = snd_hda_preset_via,
3929 .owner = THIS_MODULE,
3930};
3931
3932MODULE_LICENSE("GPL");
3933MODULE_DESCRIPTION("VIA HD-audio codec");
3934
3935static int __init patch_via_init(void)
3936{
3937 return snd_hda_add_codec_preset(&via_list);
3938}
3939
3940static void __exit patch_via_exit(void)
3941{
3942 snd_hda_delete_codec_preset(&via_list);
3943}
3944
3945module_init(patch_via_init)
3946module_exit(patch_via_exit)