blob: 06214fdc9486d2c5d1d6c59eca22325330c5c3f3 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Paul Gortmakerda155d52011-07-15 12:38:28 -040052#include <linux/module.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010053#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080054#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010055#include "hda_codec.h"
56#include "hda_local.h"
Takashi Iwai1835a0f2011-10-27 22:12:46 +020057#include "hda_jack.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010058
Joseph Chanc577b8a2006-11-29 15:29:40 +010059/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080060#define VT1708_HP_PIN_NID 0x20
61#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010062
Harald Welted7426322008-09-15 22:43:23 +080063enum VIA_HDA_CODEC {
64 UNKNOWN = -1,
65 VT1708,
66 VT1709_10CH,
67 VT1709_6CH,
68 VT1708B_8CH,
69 VT1708B_4CH,
70 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080071 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080072 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080073 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080074 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080075 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080076 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080077 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080078 CODEC_TYPES,
79};
80
Lydia Wang118909562011-03-23 17:57:34 +080081#define VT2002P_COMPATIBLE(spec) \
82 ((spec)->codec_type == VT2002P ||\
83 (spec)->codec_type == VT1812 ||\
84 (spec)->codec_type == VT1802)
85
Takashi Iwai8e3679d2011-06-21 09:01:36 +020086#define MAX_NID_PATH_DEPTH 5
87
Takashi Iwai09a9ad62011-06-21 15:57:44 +020088/* output-path: DAC -> ... -> pin
89 * idx[] contains the source index number of the next widget;
90 * e.g. idx[0] is the index of the DAC selected by path[1] widget
91 * multi[] indicates whether it's a selector widget with multi-connectors
92 * (i.e. the connection selection is mandatory)
93 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
94 */
Takashi Iwai4a796162011-06-17 17:53:38 +020095struct nid_path {
96 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020097 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad62011-06-21 15:57:44 +020098 unsigned char idx[MAX_NID_PATH_DEPTH];
99 unsigned char multi[MAX_NID_PATH_DEPTH];
100 unsigned int vol_ctl;
101 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200102};
103
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200104/* input-path */
105struct via_input {
106 hda_nid_t pin; /* input-pin or aa-mix */
107 int adc_idx; /* ADC index to be used */
108 int mux_idx; /* MUX index (if any) */
109 const char *label; /* input-source label */
110};
111
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200112#define VIA_MAX_ADCS 3
113
Takashi Iwai3b607e32011-07-18 16:54:40 +0200114enum {
115 STREAM_MULTI_OUT = (1 << 0),
116 STREAM_INDEP_HP = (1 << 1),
117};
118
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800119struct via_spec {
120 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200121 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800122 unsigned int num_mixers;
123
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200124 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800125 unsigned int num_iverbs;
126
Takashi Iwai82673bc2011-06-17 16:24:21 +0200127 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200128 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200129 const struct hda_pcm_stream *stream_analog_playback;
130 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800131
Takashi Iwai82673bc2011-06-17 16:24:21 +0200132 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200133 const struct hda_pcm_stream *stream_digital_playback;
134 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800135
136 /* playback */
137 struct hda_multi_out multiout;
138 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200139 hda_nid_t hp_dac_nid;
Takashi Iwai3214b962011-07-18 12:49:25 +0200140 hda_nid_t speaker_dac_nid;
141 int hp_indep_shared; /* indep HP-DAC is shared with side ch */
Takashi Iwai3b607e32011-07-18 16:54:40 +0200142 int opened_streams; /* STREAM_* bits */
143 int active_streams; /* STREAM_* bits */
Takashi Iwai3214b962011-07-18 12:49:25 +0200144 int aamix_mode; /* loopback is enabled for output-path? */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800145
Takashi Iwai3214b962011-07-18 12:49:25 +0200146 /* Output-paths:
147 * There are different output-paths depending on the setup.
148 * out_path, hp_path and speaker_path are primary paths. If both
149 * direct DAC and aa-loopback routes are available, these contain
150 * the former paths. Meanwhile *_mix_path contain the paths with
151 * loopback mixer. (Since the loopback is only for front channel,
152 * no out_mix_path for surround channels.)
153 * The HP output has another path, hp_indep_path, which is used in
154 * the independent-HP mode.
155 */
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200156 struct nid_path out_path[HDA_SIDE + 1];
Takashi Iwai3214b962011-07-18 12:49:25 +0200157 struct nid_path out_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200158 struct nid_path hp_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200159 struct nid_path hp_mix_path;
160 struct nid_path hp_indep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200161 struct nid_path speaker_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200162 struct nid_path speaker_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200163
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800164 /* capture */
165 unsigned int num_adc_nids;
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200166 hda_nid_t adc_nids[VIA_MAX_ADCS];
167 hda_nid_t mux_nids[VIA_MAX_ADCS];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200168 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800169 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800170
171 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200172 bool dyn_adc_switch;
173 int num_inputs;
174 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200175 unsigned int cur_mux[VIA_MAX_ADCS];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800176
Takashi Iwai3b607e32011-07-18 16:54:40 +0200177 /* dynamic DAC switching */
178 unsigned int cur_dac_stream_tag;
179 unsigned int cur_dac_format;
180 unsigned int cur_hp_stream_tag;
181 unsigned int cur_hp_format;
182
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200183 /* dynamic ADC switching */
184 hda_nid_t cur_adc;
185 unsigned int cur_adc_stream_tag;
186 unsigned int cur_adc_format;
187
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800188 /* PCM information */
189 struct hda_pcm pcm_rec[3];
190
191 /* dynamic controls, init_verbs and input_mux */
192 struct auto_pin_cfg autocfg;
193 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800194 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
195
196 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800197 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800198 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200199 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800200 enum VIA_HDA_CODEC codec_type;
201
Takashi Iwaie9d010c2012-02-01 10:33:23 +0100202 /* analog low-power control */
203 bool alc_mode;
204
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200205 /* smart51 setup */
206 unsigned int smart51_nums;
207 hda_nid_t smart51_pins[2];
208 int smart51_idxs[2];
209 const char *smart51_labels[2];
210 unsigned int smart51_enabled;
211
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800212 /* work to check hp jack state */
213 struct hda_codec *codec;
214 struct delayed_work vt1708_hp_work;
Takashi Iwai187d3332011-11-24 16:33:09 +0100215 int hp_work_active;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200216 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800217 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800218
219 void (*set_widgets_power_state)(struct hda_codec *codec);
220
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800221 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200222 int num_loopbacks;
223 struct hda_amp_list loopback_list[8];
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200224
225 /* bind capture-volume */
226 struct hda_bind_ctls *bind_cap_vol;
227 struct hda_bind_ctls *bind_cap_sw;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200228
229 struct mutex config_mutex;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800230};
231
Lydia Wang0341ccd2011-03-22 16:25:03 +0800232static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100233static struct via_spec * via_new_spec(struct hda_codec *codec)
234{
235 struct via_spec *spec;
236
237 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
238 if (spec == NULL)
239 return NULL;
240
Takashi Iwai3b607e32011-07-18 16:54:40 +0200241 mutex_init(&spec->config_mutex);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100242 codec->spec = spec;
243 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800244 spec->codec_type = get_codec_type(codec);
245 /* VT1708BCE & VT1708S are almost same */
246 if (spec->codec_type == VT1708BCE)
247 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100248 return spec;
249}
250
Lydia Wang744ff5f2009-10-10 19:07:26 +0800251static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800252{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800253 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800254 u16 ven_id = vendor_id >> 16;
255 u16 dev_id = vendor_id & 0xffff;
256 enum VIA_HDA_CODEC codec_type;
257
258 /* get codec type */
259 if (ven_id != 0x1106)
260 codec_type = UNKNOWN;
261 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
262 codec_type = VT1708;
263 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
264 codec_type = VT1709_10CH;
265 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
266 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800267 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800268 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800269 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
270 codec_type = VT1708BCE;
271 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800272 codec_type = VT1708B_4CH;
273 else if ((dev_id & 0xfff) == 0x397
274 && (dev_id >> 12) < 8)
275 codec_type = VT1708S;
276 else if ((dev_id & 0xfff) == 0x398
277 && (dev_id >> 12) < 8)
278 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800279 else if ((dev_id & 0xfff) == 0x428
280 && (dev_id >> 12) < 8)
281 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800282 else if (dev_id == 0x0433 || dev_id == 0xa721)
283 codec_type = VT1716S;
Lydia Wangbb3c6bf2009-10-10 19:08:39 +0800284 else if (dev_id == 0x0441 || dev_id == 0x4441)
285 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800286 else if (dev_id == 0x0438 || dev_id == 0x4438)
287 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800288 else if (dev_id == 0x0448)
289 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800290 else if (dev_id == 0x0440)
291 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800292 else if ((dev_id & 0xfff) == 0x446)
293 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800294 else
295 codec_type = UNKNOWN;
296 return codec_type;
297};
298
Lydia Wangec7e7e42011-03-24 12:43:44 +0800299#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800300#define VIA_HP_EVENT 0x01
301#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200302#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800303
Joseph Chanc577b8a2006-11-29 15:29:40 +0100304enum {
305 VIA_CTL_WIDGET_VOL,
306 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800307 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100308};
309
Takashi Iwaiada509e2011-06-20 15:40:19 +0200310static void analog_low_current_mode(struct hda_codec *codec);
311static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800312
Takashi Iwai187d3332011-11-24 16:33:09 +0100313#define hp_detect_with_aa(codec) \
314 (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
315 !is_aa_path_mute(codec))
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800316
317static void vt1708_stop_hp_work(struct via_spec *spec)
318{
319 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
320 return;
Takashi Iwai187d3332011-11-24 16:33:09 +0100321 if (spec->hp_work_active) {
322 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 1);
323 cancel_delayed_work_sync(&spec->vt1708_hp_work);
324 spec->hp_work_active = 0;
325 }
326}
327
328static void vt1708_update_hp_work(struct via_spec *spec)
329{
330 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800331 return;
Takashi Iwai187d3332011-11-24 16:33:09 +0100332 if (spec->vt1708_jack_detect &&
333 (spec->active_streams || hp_detect_with_aa(spec->codec))) {
334 if (!spec->hp_work_active) {
335 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 0);
336 schedule_delayed_work(&spec->vt1708_hp_work,
337 msecs_to_jiffies(100));
338 spec->hp_work_active = 1;
339 }
340 } else if (!hp_detect_with_aa(spec->codec))
341 vt1708_stop_hp_work(spec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800342}
Lydia Wangf5271102009-10-10 19:07:35 +0800343
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800344static void set_widgets_power_state(struct hda_codec *codec)
345{
346 struct via_spec *spec = codec->spec;
347 if (spec->set_widgets_power_state)
348 spec->set_widgets_power_state(codec);
349}
Lydia Wang25eaba22009-10-10 19:08:43 +0800350
Lydia Wangf5271102009-10-10 19:07:35 +0800351static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
352 struct snd_ctl_elem_value *ucontrol)
353{
354 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
355 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
356
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800357 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200358 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Takashi Iwai187d3332011-11-24 16:33:09 +0100359 vt1708_update_hp_work(codec->spec);
Lydia Wangf5271102009-10-10 19:07:35 +0800360 return change;
361}
362
363/* modify .put = snd_hda_mixer_amp_switch_put */
364#define ANALOG_INPUT_MUTE \
365 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
366 .name = NULL, \
367 .index = 0, \
368 .info = snd_hda_mixer_amp_switch_info, \
369 .get = snd_hda_mixer_amp_switch_get, \
370 .put = analog_input_switch_put, \
371 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
372
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200373static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100374 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
375 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800376 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100377};
378
Lydia Wangab6734e2009-10-10 19:08:46 +0800379
Joseph Chanc577b8a2006-11-29 15:29:40 +0100380/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200381static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
382 const struct snd_kcontrol_new *tmpl,
383 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100384{
385 struct snd_kcontrol_new *knew;
386
Takashi Iwai603c4012008-07-30 15:01:44 +0200387 snd_array_init(&spec->kctls, sizeof(*knew), 32);
388 knew = snd_array_new(&spec->kctls);
389 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200390 return NULL;
391 *knew = *tmpl;
392 if (!name)
393 name = tmpl->name;
394 if (name) {
395 knew->name = kstrdup(name, GFP_KERNEL);
396 if (!knew->name)
397 return NULL;
398 }
399 return knew;
400}
401
402static int __via_add_control(struct via_spec *spec, int type, const char *name,
403 int idx, unsigned long val)
404{
405 struct snd_kcontrol_new *knew;
406
407 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
408 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100409 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200410 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100411 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100412 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100413 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100414 return 0;
415}
416
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200417#define via_add_control(spec, type, name, val) \
418 __via_add_control(spec, type, name, 0, val)
419
Takashi Iwai291c9e32011-06-17 16:15:26 +0200420#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100421
Takashi Iwai603c4012008-07-30 15:01:44 +0200422static void via_free_kctls(struct hda_codec *codec)
423{
424 struct via_spec *spec = codec->spec;
425
426 if (spec->kctls.list) {
427 struct snd_kcontrol_new *kctl = spec->kctls.list;
428 int i;
429 for (i = 0; i < spec->kctls.used; i++)
430 kfree(kctl[i].name);
431 }
432 snd_array_free(&spec->kctls);
433}
434
Joseph Chanc577b8a2006-11-29 15:29:40 +0100435/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800436static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200437 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100438{
439 char name[32];
440 int err;
441
442 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200443 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100444 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
445 if (err < 0)
446 return err;
447 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200448 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100449 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
450 if (err < 0)
451 return err;
452 return 0;
453}
454
Takashi Iwai5d417622011-06-20 11:32:27 +0200455#define get_connection_index(codec, mux, nid) \
Takashi Iwai8d087c72011-06-28 12:45:47 +0200456 snd_hda_get_conn_index(codec, mux, nid, 0)
Takashi Iwai5d417622011-06-20 11:32:27 +0200457
Takashi Iwai8df2a312011-06-21 11:48:29 +0200458static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
459 unsigned int mask)
460{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200461 unsigned int caps;
462 if (!nid)
463 return false;
464 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200465 if (dir == HDA_INPUT)
466 caps &= AC_WCAP_IN_AMP;
467 else
468 caps &= AC_WCAP_OUT_AMP;
469 if (!caps)
470 return false;
471 if (query_amp_caps(codec, nid, dir) & mask)
472 return true;
473 return false;
474}
475
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200476#define have_mute(codec, nid, dir) \
477 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200478
Lydia Wangd69607b2011-07-08 14:02:52 +0800479/* enable/disable the output-route mixers */
480static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
Takashi Iwai3214b962011-07-18 12:49:25 +0200481 hda_nid_t mix_nid, int idx, bool enable)
Lydia Wangd69607b2011-07-08 14:02:52 +0800482{
483 int i, num, val;
Lydia Wangd69607b2011-07-08 14:02:52 +0800484
485 if (!path)
486 return;
487 num = snd_hda_get_conn_list(codec, mix_nid, NULL);
Lydia Wangd69607b2011-07-08 14:02:52 +0800488 for (i = 0; i < num; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +0200489 if (i == idx)
490 val = AMP_IN_UNMUTE(i);
491 else
492 val = AMP_IN_MUTE(i);
Lydia Wangd69607b2011-07-08 14:02:52 +0800493 snd_hda_codec_write(codec, mix_nid, 0,
494 AC_VERB_SET_AMP_GAIN_MUTE, val);
495 }
496}
497
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200498/* enable/disable the output-route */
499static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
500 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200501{
Lydia Wangd69607b2011-07-08 14:02:52 +0800502 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200503 int i;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200504 for (i = 0; i < path->depth; i++) {
505 hda_nid_t src, dst;
506 int idx = path->idx[i];
507 src = path->path[i];
508 if (i < path->depth - 1)
509 dst = path->path[i + 1];
510 else
511 dst = 0;
512 if (enable && path->multi[i])
513 snd_hda_codec_write(codec, dst, 0,
514 AC_VERB_SET_CONNECT_SEL, idx);
Takashi Iwai3214b962011-07-18 12:49:25 +0200515 if (!force && (dst == spec->aa_mix_nid))
Lydia Wange5e14682011-07-01 10:55:07 +0800516 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +0200517 if (have_mute(codec, dst, HDA_INPUT))
518 activate_output_mix(codec, path, dst, idx, enable);
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200519 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
520 continue;
521 if (have_mute(codec, src, HDA_OUTPUT)) {
522 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
523 snd_hda_codec_write(codec, src, 0,
524 AC_VERB_SET_AMP_GAIN_MUTE, val);
525 }
526 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200527}
528
529/* set the given pin as output */
530static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
531 int pin_type)
532{
533 if (!pin)
534 return;
535 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
536 pin_type);
537 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
538 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200539 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100540}
541
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200542static void via_auto_init_output(struct hda_codec *codec,
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200543 struct nid_path *path, int pin_type)
Takashi Iwai5d417622011-06-20 11:32:27 +0200544{
Takashi Iwai5d417622011-06-20 11:32:27 +0200545 unsigned int caps;
Lydia Wangd69607b2011-07-08 14:02:52 +0800546 hda_nid_t pin;
Takashi Iwai5d417622011-06-20 11:32:27 +0200547
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200548 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200549 return;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200550 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200551
552 init_output_pin(codec, pin, pin_type);
Takashi Iwai77e314f2012-02-22 12:34:08 +0100553 if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
554 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
555 else
556 caps = 0;
Takashi Iwai5d417622011-06-20 11:32:27 +0200557 if (caps & AC_AMPCAP_MUTE) {
558 unsigned int val;
559 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
560 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
561 AMP_OUT_MUTE | val);
562 }
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200563 activate_output_path(codec, path, true, true); /* force on */
Takashi Iwai5d417622011-06-20 11:32:27 +0200564}
565
Joseph Chanc577b8a2006-11-29 15:29:40 +0100566static void via_auto_init_multi_out(struct hda_codec *codec)
567{
568 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200569 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100570 int i;
571
Takashi Iwai3214b962011-07-18 12:49:25 +0200572 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) {
573 path = &spec->out_path[i];
574 if (!i && spec->aamix_mode && spec->out_mix_path.depth)
575 path = &spec->out_mix_path;
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200576 via_auto_init_output(codec, path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200577 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100578}
579
Takashi Iwai020066d2011-07-21 13:45:56 +0200580/* deactivate the inactive headphone-paths */
581static void deactivate_hp_paths(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100582{
583 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200584 int shared = spec->hp_indep_shared;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100585
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200586 if (spec->hp_independent_mode) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200587 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200588 activate_output_path(codec, &spec->hp_mix_path, false, false);
589 if (shared)
590 activate_output_path(codec, &spec->out_path[shared],
591 false, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200592 } else if (spec->aamix_mode || !spec->hp_path.depth) {
593 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200594 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200595 } else {
Takashi Iwai020066d2011-07-21 13:45:56 +0200596 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200597 activate_output_path(codec, &spec->hp_mix_path, false, false);
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200598 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100599}
600
Takashi Iwai020066d2011-07-21 13:45:56 +0200601static void via_auto_init_hp_out(struct hda_codec *codec)
602{
603 struct via_spec *spec = codec->spec;
604
605 if (!spec->hp_path.depth) {
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 return;
608 }
609 deactivate_hp_paths(codec);
610 if (spec->hp_independent_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200611 via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200612 else if (spec->aamix_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200613 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200614 else
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200615 via_auto_init_output(codec, &spec->hp_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200616}
617
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200618static void via_auto_init_speaker_out(struct hda_codec *codec)
619{
620 struct via_spec *spec = codec->spec;
621
Takashi Iwai3214b962011-07-18 12:49:25 +0200622 if (!spec->autocfg.speaker_outs)
623 return;
624 if (!spec->speaker_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200625 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200626 return;
627 }
628 if (!spec->aamix_mode) {
629 activate_output_path(codec, &spec->speaker_mix_path,
630 false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200631 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200632 } else {
633 activate_output_path(codec, &spec->speaker_path, false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200634 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200635 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200636}
637
Takashi Iwaif4a78282011-06-17 18:46:48 +0200638static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200639static void via_hp_automute(struct hda_codec *codec);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200640
Joseph Chanc577b8a2006-11-29 15:29:40 +0100641static void via_auto_init_analog_input(struct hda_codec *codec)
642{
643 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200644 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200645 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200646 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200647 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100648
Takashi Iwai096a8852011-06-20 12:09:02 +0200649 /* init ADCs */
650 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwai77e314f2012-02-22 12:34:08 +0100651 hda_nid_t nid = spec->adc_nids[i];
652 if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP) ||
653 !(query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE))
654 continue;
Takashi Iwai096a8852011-06-20 12:09:02 +0200655 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
656 AC_VERB_SET_AMP_GAIN_MUTE,
657 AMP_IN_UNMUTE(0));
658 }
659
660 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200661 for (i = 0; i < cfg->num_inputs; i++) {
662 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200663 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200664 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100665 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200666 ctl = PIN_VREF50;
667 else
668 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100669 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200670 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100671 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200672
673 /* init input-src */
674 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200675 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
Takashi Iwaifc1156c2012-02-13 15:04:06 +0100676 /* secondary ADCs must have the unique MUX */
677 if (i > 0 && !spec->mux_nids[i])
678 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200679 if (spec->mux_nids[adc_idx]) {
680 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
681 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
682 AC_VERB_SET_CONNECT_SEL,
683 mux_idx);
684 }
685 if (spec->dyn_adc_switch)
686 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200687 }
688
689 /* init aa-mixer */
690 if (!spec->aa_mix_nid)
691 return;
692 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
693 ARRAY_SIZE(conn));
694 for (i = 0; i < num_conns; i++) {
695 unsigned int caps = get_wcaps(codec, conn[i]);
696 if (get_wcaps_type(caps) == AC_WID_PIN)
697 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
698 AC_VERB_SET_AMP_GAIN_MUTE,
699 AMP_IN_MUTE(i));
700 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100701}
Lydia Wangf5271102009-10-10 19:07:35 +0800702
Takashi Iwai054d8672012-01-24 12:25:50 +0100703static void update_power_state(struct hda_codec *codec, hda_nid_t nid,
704 unsigned int parm)
705{
706 if (snd_hda_codec_read(codec, nid, 0,
707 AC_VERB_GET_POWER_STATE, 0) == parm)
708 return;
709 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
710}
711
Lydia Wangf5271102009-10-10 19:07:35 +0800712static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
713 unsigned int *affected_parm)
714{
715 unsigned parm;
716 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
717 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
718 >> AC_DEFCFG_MISC_SHIFT
719 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800720 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200721 unsigned present = 0;
722
723 no_presence |= spec->no_pin_power_ctl;
724 if (!no_presence)
725 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200726 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800727 || ((no_presence || present)
728 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800729 *affected_parm = AC_PWRST_D0; /* if it's connected */
730 parm = AC_PWRST_D0;
731 } else
732 parm = AC_PWRST_D3;
733
Takashi Iwai054d8672012-01-24 12:25:50 +0100734 update_power_state(codec, nid, parm);
Lydia Wangf5271102009-10-10 19:07:35 +0800735}
736
Takashi Iwai24088a52011-06-17 16:59:21 +0200737static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
738 struct snd_ctl_elem_info *uinfo)
739{
740 static const char * const texts[] = {
741 "Disabled", "Enabled"
742 };
743
744 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
745 uinfo->count = 1;
746 uinfo->value.enumerated.items = 2;
747 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
748 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
749 strcpy(uinfo->value.enumerated.name,
750 texts[uinfo->value.enumerated.item]);
751 return 0;
752}
753
754static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
755 struct snd_ctl_elem_value *ucontrol)
756{
757 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
758 struct via_spec *spec = codec->spec;
759 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
760 return 0;
761}
762
763static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
764 struct snd_ctl_elem_value *ucontrol)
765{
766 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
767 struct via_spec *spec = codec->spec;
768 unsigned int val = !ucontrol->value.enumerated.item[0];
769
770 if (val == spec->no_pin_power_ctl)
771 return 0;
772 spec->no_pin_power_ctl = val;
773 set_widgets_power_state(codec);
Takashi Iwaie9d010c2012-02-01 10:33:23 +0100774 analog_low_current_mode(codec);
Takashi Iwai24088a52011-06-17 16:59:21 +0200775 return 1;
776}
777
778static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
779 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
780 .name = "Dynamic Power-Control",
781 .info = via_pin_power_ctl_info,
782 .get = via_pin_power_ctl_get,
783 .put = via_pin_power_ctl_put,
784};
785
786
Harald Welte0aa62ae2008-09-09 15:58:27 +0800787static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
788 struct snd_ctl_elem_info *uinfo)
789{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200790 static const char * const texts[] = { "OFF", "ON" };
791
792 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
793 uinfo->count = 1;
794 uinfo->value.enumerated.items = 2;
795 if (uinfo->value.enumerated.item >= 2)
796 uinfo->value.enumerated.item = 1;
797 strcpy(uinfo->value.enumerated.name,
798 texts[uinfo->value.enumerated.item]);
799 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800800}
801
802static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
803 struct snd_ctl_elem_value *ucontrol)
804{
805 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800806 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800807
Takashi Iwaiece8d042011-06-19 16:24:21 +0200808 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800809 return 0;
810}
811
Takashi Iwai3b607e32011-07-18 16:54:40 +0200812/* adjust spec->multiout setup according to the current flags */
813static void setup_playback_multi_pcm(struct via_spec *spec)
814{
815 const struct auto_pin_cfg *cfg = &spec->autocfg;
816 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
817 spec->multiout.hp_nid = 0;
818 if (!spec->hp_independent_mode) {
819 if (!spec->hp_indep_shared)
820 spec->multiout.hp_nid = spec->hp_dac_nid;
821 } else {
822 if (spec->hp_indep_shared)
823 spec->multiout.num_dacs = cfg->line_outs - 1;
824 }
825}
826
827/* update DAC setups according to indep-HP switch;
828 * this function is called only when indep-HP is modified
829 */
830static void switch_indep_hp_dacs(struct hda_codec *codec)
831{
832 struct via_spec *spec = codec->spec;
833 int shared = spec->hp_indep_shared;
834 hda_nid_t shared_dac, hp_dac;
835
836 if (!spec->opened_streams)
837 return;
838
839 shared_dac = shared ? spec->multiout.dac_nids[shared] : 0;
840 hp_dac = spec->hp_dac_nid;
841 if (spec->hp_independent_mode) {
842 /* switch to indep-HP mode */
843 if (spec->active_streams & STREAM_MULTI_OUT) {
844 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
845 __snd_hda_codec_cleanup_stream(codec, shared_dac, 1);
846 }
847 if (spec->active_streams & STREAM_INDEP_HP)
848 snd_hda_codec_setup_stream(codec, hp_dac,
849 spec->cur_hp_stream_tag, 0,
850 spec->cur_hp_format);
851 } else {
852 /* back to HP or shared-DAC */
853 if (spec->active_streams & STREAM_INDEP_HP)
854 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
855 if (spec->active_streams & STREAM_MULTI_OUT) {
856 hda_nid_t dac;
857 int ch;
858 if (shared_dac) { /* reset mutli-ch DAC */
859 dac = shared_dac;
860 ch = shared * 2;
861 } else { /* reset HP DAC */
862 dac = hp_dac;
863 ch = 0;
864 }
865 snd_hda_codec_setup_stream(codec, dac,
866 spec->cur_dac_stream_tag, ch,
867 spec->cur_dac_format);
868 }
869 }
870 setup_playback_multi_pcm(spec);
871}
872
Harald Welte0aa62ae2008-09-09 15:58:27 +0800873static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
874 struct snd_ctl_elem_value *ucontrol)
875{
876 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
877 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200878 int cur, shared;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200879
Takashi Iwai3b607e32011-07-18 16:54:40 +0200880 mutex_lock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200881 cur = !!ucontrol->value.enumerated.item[0];
Takashi Iwai3b607e32011-07-18 16:54:40 +0200882 if (spec->hp_independent_mode == cur) {
883 mutex_unlock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200884 return 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200885 }
Takashi Iwai25250502011-06-30 17:24:47 +0200886 spec->hp_independent_mode = cur;
Takashi Iwai3214b962011-07-18 12:49:25 +0200887 shared = spec->hp_indep_shared;
Takashi Iwai020066d2011-07-21 13:45:56 +0200888 deactivate_hp_paths(codec);
889 if (cur)
890 activate_output_path(codec, &spec->hp_indep_path, true, false);
891 else {
Takashi Iwai3214b962011-07-18 12:49:25 +0200892 if (shared)
893 activate_output_path(codec, &spec->out_path[shared],
Takashi Iwai25250502011-06-30 17:24:47 +0200894 true, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200895 if (spec->aamix_mode || !spec->hp_path.depth)
896 activate_output_path(codec, &spec->hp_mix_path,
897 true, false);
898 else
899 activate_output_path(codec, &spec->hp_path,
900 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200901 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800902
Takashi Iwai3b607e32011-07-18 16:54:40 +0200903 switch_indep_hp_dacs(codec);
904 mutex_unlock(&spec->config_mutex);
905
Lydia Wangce0e5a92011-03-22 16:22:37 +0800906 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800907 set_widgets_power_state(codec);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200908 via_hp_automute(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200909 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800910}
911
Takashi Iwaiece8d042011-06-19 16:24:21 +0200912static const struct snd_kcontrol_new via_hp_mixer = {
913 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
914 .name = "Independent HP",
915 .info = via_independent_hp_info,
916 .get = via_independent_hp_get,
917 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800918};
919
Takashi Iwai3d83e572010-04-14 14:36:23 +0200920static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100921{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200922 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100923 struct snd_kcontrol_new *knew;
924 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100925
Takashi Iwaiece8d042011-06-19 16:24:21 +0200926 nid = spec->autocfg.hp_pins[0];
927 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200928 if (knew == NULL)
929 return -ENOMEM;
930
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100931 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100932
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100933 return 0;
934}
935
Lydia Wang1564b282009-10-10 19:07:52 +0800936static void notify_aa_path_ctls(struct hda_codec *codec)
937{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200938 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800939 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800940
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200941 for (i = 0; i < spec->smart51_nums; i++) {
942 struct snd_kcontrol *ctl;
943 struct snd_ctl_elem_id id;
944 memset(&id, 0, sizeof(id));
945 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
946 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800947 ctl = snd_hda_find_mixer_ctl(codec, id.name);
948 if (ctl)
949 snd_ctl_notify(codec->bus->card,
950 SNDRV_CTL_EVENT_MASK_VALUE,
951 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800952 }
953}
954
955static void mute_aa_path(struct hda_codec *codec, int mute)
956{
957 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200958 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800959 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200960
Lydia Wang1564b282009-10-10 19:07:52 +0800961 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200962 for (i = 0; i < spec->smart51_nums; i++) {
963 if (spec->smart51_idxs[i] < 0)
964 continue;
965 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
966 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800967 HDA_AMP_MUTE, val);
968 }
969}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200970
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200971static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
972{
973 struct via_spec *spec = codec->spec;
974 int i;
975
976 for (i = 0; i < spec->smart51_nums; i++)
977 if (spec->smart51_pins[i] == pin)
978 return true;
979 return false;
980}
981
Lydia Wang1564b282009-10-10 19:07:52 +0800982static int via_smart51_get(struct snd_kcontrol *kcontrol,
983 struct snd_ctl_elem_value *ucontrol)
984{
985 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
986 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800987
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200988 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800989 return 0;
990}
991
992static int via_smart51_put(struct snd_kcontrol *kcontrol,
993 struct snd_ctl_elem_value *ucontrol)
994{
995 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
996 struct via_spec *spec = codec->spec;
997 int out_in = *ucontrol->value.integer.value
998 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800999 int i;
1000
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001001 for (i = 0; i < spec->smart51_nums; i++) {
1002 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001003 unsigned int parm;
1004
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001005 parm = snd_hda_codec_read(codec, nid, 0,
1006 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1007 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
1008 parm |= out_in;
1009 snd_hda_codec_write(codec, nid, 0,
1010 AC_VERB_SET_PIN_WIDGET_CONTROL,
1011 parm);
1012 if (out_in == AC_PINCTL_OUT_EN) {
1013 mute_aa_path(codec, 1);
1014 notify_aa_path_ctls(codec);
1015 }
Lydia Wang1564b282009-10-10 19:07:52 +08001016 }
1017 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001018 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +08001019 return 1;
1020}
1021
Takashi Iwai5f4b36d2011-06-17 14:55:02 +02001022static const struct snd_kcontrol_new via_smart51_mixer = {
1023 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1024 .name = "Smart 5.1",
1025 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001026 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +02001027 .get = via_smart51_get,
1028 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +08001029};
1030
Takashi Iwaif4a78282011-06-17 18:46:48 +02001031static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001032{
Takashi Iwaif4a78282011-06-17 18:46:48 +02001033 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001034
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001035 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +08001036 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001037 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001038 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001039 return 0;
1040}
1041
Takashi Iwaiada509e2011-06-20 15:40:19 +02001042/* check AA path's mute status */
1043static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001044{
Lydia Wangf5271102009-10-10 19:07:35 +08001045 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001046 const struct hda_amp_list *p;
1047 int i, ch, v;
1048
1049 for (i = 0; i < spec->num_loopbacks; i++) {
1050 p = &spec->loopback_list[i];
1051 for (ch = 0; ch < 2; ch++) {
1052 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
1053 p->idx);
1054 if (!(v & HDA_AMP_MUTE) && v > 0)
1055 return false;
Lydia Wangf5271102009-10-10 19:07:35 +08001056 }
1057 }
Takashi Iwaiada509e2011-06-20 15:40:19 +02001058 return true;
Lydia Wangf5271102009-10-10 19:07:35 +08001059}
1060
1061/* enter/exit analog low-current mode */
Takashi Iwaie9d010c2012-02-01 10:33:23 +01001062static void __analog_low_current_mode(struct hda_codec *codec, bool force)
Lydia Wangf5271102009-10-10 19:07:35 +08001063{
1064 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001065 bool enable;
1066 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +08001067
Takashi Iwaie9d010c2012-02-01 10:33:23 +01001068 if (spec->no_pin_power_ctl)
1069 enable = false;
1070 else
1071 enable = is_aa_path_mute(codec) && !spec->opened_streams;
1072 if (enable == spec->alc_mode && !force)
1073 return;
1074 spec->alc_mode = enable;
Lydia Wangf5271102009-10-10 19:07:35 +08001075
1076 /* decide low current mode's verb & parameter */
1077 switch (spec->codec_type) {
1078 case VT1708B_8CH:
1079 case VT1708B_4CH:
1080 verb = 0xf70;
1081 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1082 break;
1083 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001084 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001085 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001086 verb = 0xf73;
1087 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1088 break;
1089 case VT1702:
1090 verb = 0xf73;
1091 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1092 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001093 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001094 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +08001095 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +08001096 verb = 0xf93;
1097 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1098 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001099 default:
1100 return; /* other codecs are not supported */
1101 }
1102 /* send verb */
1103 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1104}
1105
Takashi Iwaie9d010c2012-02-01 10:33:23 +01001106static void analog_low_current_mode(struct hda_codec *codec)
1107{
1108 return __analog_low_current_mode(codec, false);
1109}
1110
Joseph Chanc577b8a2006-11-29 15:29:40 +01001111/*
1112 * generic initialization of ADC, input mixers and output mixers
1113 */
Takashi Iwai096a8852011-06-20 12:09:02 +02001114static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001115 /* power down jack detect function */
1116 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001117 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001118};
1119
Takashi Iwai3b607e32011-07-18 16:54:40 +02001120static void set_stream_open(struct hda_codec *codec, int bit, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001121{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001122 struct via_spec *spec = codec->spec;
1123
1124 if (active)
Takashi Iwai3b607e32011-07-18 16:54:40 +02001125 spec->opened_streams |= bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001126 else
Takashi Iwai3b607e32011-07-18 16:54:40 +02001127 spec->opened_streams &= ~bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001128 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001129}
1130
Takashi Iwaiece8d042011-06-19 16:24:21 +02001131static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001132 struct hda_codec *codec,
1133 struct snd_pcm_substream *substream)
1134{
1135 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001136 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001137 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001138
Takashi Iwai25250502011-06-30 17:24:47 +02001139 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
Takashi Iwai25250502011-06-30 17:24:47 +02001140 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001141 set_stream_open(codec, STREAM_MULTI_OUT, true);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001142 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1143 hinfo);
1144 if (err < 0) {
Takashi Iwai3b607e32011-07-18 16:54:40 +02001145 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001146 return err;
1147 }
1148 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001149}
1150
Takashi Iwaiece8d042011-06-19 16:24:21 +02001151static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001152 struct hda_codec *codec,
1153 struct snd_pcm_substream *substream)
1154{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001155 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001156 return 0;
1157}
1158
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001159static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1160 struct hda_codec *codec,
1161 struct snd_pcm_substream *substream)
1162{
1163 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001164
Takashi Iwaiece8d042011-06-19 16:24:21 +02001165 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001166 return -EINVAL;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001167 set_stream_open(codec, STREAM_INDEP_HP, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001168 return 0;
1169}
1170
1171static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1172 struct hda_codec *codec,
1173 struct snd_pcm_substream *substream)
1174{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001175 set_stream_open(codec, STREAM_INDEP_HP, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001176 return 0;
1177}
1178
1179static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1180 struct hda_codec *codec,
1181 unsigned int stream_tag,
1182 unsigned int format,
1183 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001184{
1185 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001186
Takashi Iwai3b607e32011-07-18 16:54:40 +02001187 mutex_lock(&spec->config_mutex);
1188 setup_playback_multi_pcm(spec);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001189 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1190 format, substream);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001191 /* remember for dynamic DAC switch with indep-HP */
1192 spec->active_streams |= STREAM_MULTI_OUT;
1193 spec->cur_dac_stream_tag = stream_tag;
1194 spec->cur_dac_format = format;
1195 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001196 vt1708_update_hp_work(spec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001197 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001198}
1199
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001200static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1201 struct hda_codec *codec,
1202 unsigned int stream_tag,
1203 unsigned int format,
1204 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001205{
1206 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001207
Takashi Iwai3b607e32011-07-18 16:54:40 +02001208 mutex_lock(&spec->config_mutex);
1209 if (spec->hp_independent_mode)
1210 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1211 stream_tag, 0, format);
1212 spec->active_streams |= STREAM_INDEP_HP;
1213 spec->cur_hp_stream_tag = stream_tag;
1214 spec->cur_hp_format = format;
1215 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001216 vt1708_update_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001217 return 0;
1218}
1219
1220static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1221 struct hda_codec *codec,
1222 struct snd_pcm_substream *substream)
1223{
1224 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001225
Takashi Iwai3b607e32011-07-18 16:54:40 +02001226 mutex_lock(&spec->config_mutex);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001227 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001228 spec->active_streams &= ~STREAM_MULTI_OUT;
1229 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001230 vt1708_update_hp_work(spec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001231 return 0;
1232}
1233
1234static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1235 struct hda_codec *codec,
1236 struct snd_pcm_substream *substream)
1237{
1238 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001239
Takashi Iwai3b607e32011-07-18 16:54:40 +02001240 mutex_lock(&spec->config_mutex);
1241 if (spec->hp_independent_mode)
1242 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
1243 spec->active_streams &= ~STREAM_INDEP_HP;
1244 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001245 vt1708_update_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001246 return 0;
1247}
1248
Joseph Chanc577b8a2006-11-29 15:29:40 +01001249/*
1250 * Digital out
1251 */
1252static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1253 struct hda_codec *codec,
1254 struct snd_pcm_substream *substream)
1255{
1256 struct via_spec *spec = codec->spec;
1257 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1258}
1259
1260static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1261 struct hda_codec *codec,
1262 struct snd_pcm_substream *substream)
1263{
1264 struct via_spec *spec = codec->spec;
1265 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1266}
1267
Harald Welte5691ec72008-09-15 22:42:26 +08001268static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001269 struct hda_codec *codec,
1270 unsigned int stream_tag,
1271 unsigned int format,
1272 struct snd_pcm_substream *substream)
1273{
1274 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001275 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1276 stream_tag, format, substream);
1277}
Harald Welte5691ec72008-09-15 22:42:26 +08001278
Takashi Iwai9da29272009-05-07 16:31:14 +02001279static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1280 struct hda_codec *codec,
1281 struct snd_pcm_substream *substream)
1282{
1283 struct via_spec *spec = codec->spec;
1284 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001285 return 0;
1286}
1287
Joseph Chanc577b8a2006-11-29 15:29:40 +01001288/*
1289 * Analog capture
1290 */
1291static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1292 struct hda_codec *codec,
1293 unsigned int stream_tag,
1294 unsigned int format,
1295 struct snd_pcm_substream *substream)
1296{
1297 struct via_spec *spec = codec->spec;
1298
1299 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1300 stream_tag, 0, format);
1301 return 0;
1302}
1303
1304static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1305 struct hda_codec *codec,
1306 struct snd_pcm_substream *substream)
1307{
1308 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001309 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001310 return 0;
1311}
1312
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001313/* analog capture with dynamic ADC switching */
1314static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1315 struct hda_codec *codec,
1316 unsigned int stream_tag,
1317 unsigned int format,
1318 struct snd_pcm_substream *substream)
1319{
1320 struct via_spec *spec = codec->spec;
1321 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1322
Takashi Iwai3b607e32011-07-18 16:54:40 +02001323 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001324 spec->cur_adc = spec->adc_nids[adc_idx];
1325 spec->cur_adc_stream_tag = stream_tag;
1326 spec->cur_adc_format = format;
1327 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001328 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001329 return 0;
1330}
1331
1332static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1333 struct hda_codec *codec,
1334 struct snd_pcm_substream *substream)
1335{
1336 struct via_spec *spec = codec->spec;
1337
Takashi Iwai3b607e32011-07-18 16:54:40 +02001338 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001339 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1340 spec->cur_adc = 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001341 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001342 return 0;
1343}
1344
1345/* re-setup the stream if running; called from input-src put */
1346static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1347{
1348 struct via_spec *spec = codec->spec;
1349 int adc_idx = spec->inputs[cur].adc_idx;
1350 hda_nid_t adc = spec->adc_nids[adc_idx];
Takashi Iwai3b607e32011-07-18 16:54:40 +02001351 bool ret = false;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001352
Takashi Iwai3b607e32011-07-18 16:54:40 +02001353 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001354 if (spec->cur_adc && spec->cur_adc != adc) {
1355 /* stream is running, let's swap the current ADC */
1356 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1357 spec->cur_adc = adc;
1358 snd_hda_codec_setup_stream(codec, adc,
1359 spec->cur_adc_stream_tag, 0,
1360 spec->cur_adc_format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001361 ret = true;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001362 }
Takashi Iwai3b607e32011-07-18 16:54:40 +02001363 mutex_unlock(&spec->config_mutex);
1364 return ret;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001365}
1366
Takashi Iwai9af74212011-06-18 16:17:45 +02001367static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001368 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001369 .channels_min = 2,
1370 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001371 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001372 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001373 .open = via_playback_multi_pcm_open,
1374 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001375 .prepare = via_playback_multi_pcm_prepare,
1376 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001377 },
1378};
1379
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001380static const struct hda_pcm_stream via_pcm_hp_playback = {
1381 .substreams = 1,
1382 .channels_min = 2,
1383 .channels_max = 2,
1384 /* NID is set in via_build_pcms */
1385 .ops = {
1386 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001387 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001388 .prepare = via_playback_hp_pcm_prepare,
1389 .cleanup = via_playback_hp_pcm_cleanup
1390 },
1391};
1392
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001393static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001394 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001395 .channels_min = 2,
1396 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001397 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001398 /* We got noisy outputs on the right channel on VT1708 when
1399 * 24bit samples are used. Until any workaround is found,
1400 * disable the 24bit format, so far.
1401 */
1402 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1403 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001404 .open = via_playback_multi_pcm_open,
1405 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001406 .prepare = via_playback_multi_pcm_prepare,
1407 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001408 },
1409};
1410
Takashi Iwai9af74212011-06-18 16:17:45 +02001411static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001412 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001413 .channels_min = 2,
1414 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001415 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001416 .ops = {
1417 .prepare = via_capture_pcm_prepare,
1418 .cleanup = via_capture_pcm_cleanup
1419 },
1420};
1421
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001422static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1423 .substreams = 1,
1424 .channels_min = 2,
1425 .channels_max = 2,
1426 /* NID is set in via_build_pcms */
1427 .ops = {
1428 .prepare = via_dyn_adc_capture_pcm_prepare,
1429 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1430 },
1431};
1432
Takashi Iwai9af74212011-06-18 16:17:45 +02001433static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001434 .substreams = 1,
1435 .channels_min = 2,
1436 .channels_max = 2,
1437 /* NID is set in via_build_pcms */
1438 .ops = {
1439 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001440 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001441 .prepare = via_dig_playback_pcm_prepare,
1442 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001443 },
1444};
1445
Takashi Iwai9af74212011-06-18 16:17:45 +02001446static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001447 .substreams = 1,
1448 .channels_min = 2,
1449 .channels_max = 2,
1450};
1451
Takashi Iwai370bafb2011-06-20 12:47:45 +02001452/*
1453 * slave controls for virtual master
1454 */
Takashi Iwai9322ca52012-02-03 14:28:01 +01001455static const char * const via_slave_pfxs[] = {
1456 "Front", "Surround", "Center", "LFE", "Side",
1457 "Headphone", "Speaker",
Takashi Iwai370bafb2011-06-20 12:47:45 +02001458 NULL,
1459};
1460
Joseph Chanc577b8a2006-11-29 15:29:40 +01001461static int via_build_controls(struct hda_codec *codec)
1462{
1463 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001464 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001465 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001466
Takashi Iwaib5bcc182012-02-02 10:30:17 +01001467 spec->no_pin_power_ctl = 1;
Takashi Iwai24088a52011-06-17 16:59:21 +02001468 if (spec->set_widgets_power_state)
1469 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1470 return -ENOMEM;
1471
Joseph Chanc577b8a2006-11-29 15:29:40 +01001472 for (i = 0; i < spec->num_mixers; i++) {
1473 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1474 if (err < 0)
1475 return err;
1476 }
1477
1478 if (spec->multiout.dig_out_nid) {
1479 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001480 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001481 spec->multiout.dig_out_nid);
1482 if (err < 0)
1483 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001484 err = snd_hda_create_spdif_share_sw(codec,
1485 &spec->multiout);
1486 if (err < 0)
1487 return err;
1488 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001489 }
1490 if (spec->dig_in_nid) {
1491 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1492 if (err < 0)
1493 return err;
1494 }
Lydia Wang17314372009-10-10 19:07:37 +08001495
Takashi Iwai370bafb2011-06-20 12:47:45 +02001496 /* if we have no master control, let's create it */
1497 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1498 unsigned int vmaster_tlv[4];
1499 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1500 HDA_OUTPUT, vmaster_tlv);
1501 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
Takashi Iwai9322ca52012-02-03 14:28:01 +01001502 vmaster_tlv, via_slave_pfxs,
1503 "Playback Volume");
Takashi Iwai370bafb2011-06-20 12:47:45 +02001504 if (err < 0)
1505 return err;
1506 }
1507 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1508 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
Takashi Iwai9322ca52012-02-03 14:28:01 +01001509 NULL, via_slave_pfxs,
1510 "Playback Switch");
Takashi Iwai370bafb2011-06-20 12:47:45 +02001511 if (err < 0)
1512 return err;
1513 }
1514
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001515 /* assign Capture Source enums to NID */
1516 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1517 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai77e314f2012-02-22 12:34:08 +01001518 if (!spec->mux_nids[i])
1519 continue;
Takashi Iwai21949f02009-12-23 08:31:59 +01001520 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001521 if (err < 0)
1522 return err;
1523 }
1524
Takashi Iwai603c4012008-07-30 15:01:44 +02001525 via_free_kctls(codec); /* no longer needed */
Takashi Iwai01a61e12011-10-28 00:03:22 +02001526
1527 err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
1528 if (err < 0)
1529 return err;
1530
Joseph Chanc577b8a2006-11-29 15:29:40 +01001531 return 0;
1532}
1533
1534static int via_build_pcms(struct hda_codec *codec)
1535{
1536 struct via_spec *spec = codec->spec;
1537 struct hda_pcm *info = spec->pcm_rec;
1538
Takashi Iwaia5973102011-09-28 16:43:36 +02001539 codec->num_pcms = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001540 codec->pcm_info = info;
1541
Takashi Iwaia5973102011-09-28 16:43:36 +02001542 if (spec->multiout.num_dacs || spec->num_adc_nids) {
1543 snprintf(spec->stream_name_analog,
1544 sizeof(spec->stream_name_analog),
1545 "%s Analog", codec->chip_name);
1546 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001547
Takashi Iwaia5973102011-09-28 16:43:36 +02001548 if (spec->multiout.num_dacs) {
1549 if (!spec->stream_analog_playback)
1550 spec->stream_analog_playback =
1551 &via_pcm_analog_playback;
1552 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1553 *spec->stream_analog_playback;
1554 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1555 spec->multiout.dac_nids[0];
1556 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1557 spec->multiout.max_channels;
1558 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001559
Takashi Iwaia5973102011-09-28 16:43:36 +02001560 if (!spec->stream_analog_capture) {
1561 if (spec->dyn_adc_switch)
1562 spec->stream_analog_capture =
1563 &via_pcm_dyn_adc_analog_capture;
1564 else
1565 spec->stream_analog_capture =
1566 &via_pcm_analog_capture;
1567 }
1568 if (spec->num_adc_nids) {
1569 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1570 *spec->stream_analog_capture;
1571 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1572 spec->adc_nids[0];
1573 if (!spec->dyn_adc_switch)
1574 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1575 spec->num_adc_nids;
1576 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001577 codec->num_pcms++;
1578 info++;
Takashi Iwaia5973102011-09-28 16:43:36 +02001579 }
1580
1581 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
Takashi Iwai82673bc2011-06-17 16:24:21 +02001582 snprintf(spec->stream_name_digital,
1583 sizeof(spec->stream_name_digital),
1584 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001585 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001586 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001587 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001588 if (!spec->stream_digital_playback)
1589 spec->stream_digital_playback =
1590 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001591 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001592 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001593 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1594 spec->multiout.dig_out_nid;
1595 }
1596 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001597 if (!spec->stream_digital_capture)
1598 spec->stream_digital_capture =
1599 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001600 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001601 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001602 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1603 spec->dig_in_nid;
1604 }
Takashi Iwaia5973102011-09-28 16:43:36 +02001605 codec->num_pcms++;
1606 info++;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001607 }
1608
Takashi Iwaiece8d042011-06-19 16:24:21 +02001609 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001610 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1611 "%s HP", codec->chip_name);
1612 info->name = spec->stream_name_hp;
1613 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1614 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001615 spec->hp_dac_nid;
Takashi Iwaia5973102011-09-28 16:43:36 +02001616 codec->num_pcms++;
1617 info++;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001618 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001619 return 0;
1620}
1621
1622static void via_free(struct hda_codec *codec)
1623{
1624 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001625
1626 if (!spec)
1627 return;
1628
Takashi Iwai603c4012008-07-30 15:01:44 +02001629 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001630 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001631 kfree(spec->bind_cap_vol);
1632 kfree(spec->bind_cap_sw);
1633 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001634}
1635
Takashi Iwai64be2852011-06-17 16:51:39 +02001636/* mute/unmute outputs */
1637static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1638 hda_nid_t *pins, bool mute)
1639{
1640 int i;
Takashi Iwai94994732011-07-11 11:36:44 +02001641 for (i = 0; i < num_pins; i++) {
1642 unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
1643 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1644 if (parm & AC_PINCTL_IN_EN)
1645 continue;
1646 if (mute)
1647 parm &= ~AC_PINCTL_OUT_EN;
1648 else
1649 parm |= AC_PINCTL_OUT_EN;
Takashi Iwai64be2852011-06-17 16:51:39 +02001650 snd_hda_codec_write(codec, pins[i], 0,
Takashi Iwai94994732011-07-11 11:36:44 +02001651 AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
1652 }
Takashi Iwai64be2852011-06-17 16:51:39 +02001653}
1654
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001655/* mute internal speaker if line-out is plugged */
1656static void via_line_automute(struct hda_codec *codec, int present)
1657{
1658 struct via_spec *spec = codec->spec;
1659
1660 if (!spec->autocfg.speaker_outs)
1661 return;
1662 if (!present)
1663 present = snd_hda_jack_detect(codec,
1664 spec->autocfg.line_out_pins[0]);
1665 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1666 spec->autocfg.speaker_pins,
1667 present);
1668}
1669
Harald Welte69e52a82008-09-09 15:57:32 +08001670/* mute internal speaker if HP is plugged */
1671static void via_hp_automute(struct hda_codec *codec)
1672{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001673 int present = 0;
Takashi Iwai6e969d92011-07-11 11:28:13 +02001674 int nums;
Harald Welte69e52a82008-09-09 15:57:32 +08001675 struct via_spec *spec = codec->spec;
1676
Takashi Iwai187d3332011-11-24 16:33:09 +01001677 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0] &&
1678 (spec->codec_type != VT1708 || spec->vt1708_jack_detect))
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001679 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai6e969d92011-07-11 11:28:13 +02001680
1681 if (spec->smart51_enabled)
1682 nums = spec->autocfg.line_outs + spec->smart51_nums;
1683 else
1684 nums = spec->autocfg.line_outs;
1685 toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
1686
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001687 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001688}
1689
Harald Welte69e52a82008-09-09 15:57:32 +08001690static void via_gpio_control(struct hda_codec *codec)
1691{
1692 unsigned int gpio_data;
1693 unsigned int vol_counter;
1694 unsigned int vol;
1695 unsigned int master_vol;
1696
1697 struct via_spec *spec = codec->spec;
1698
1699 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1700 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1701
1702 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1703 0xF84, 0) & 0x3F0000) >> 16;
1704
1705 vol = vol_counter & 0x1F;
1706 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1707 AC_VERB_GET_AMP_GAIN_MUTE,
1708 AC_AMP_GET_INPUT);
1709
1710 if (gpio_data == 0x02) {
1711 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001712 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1713 AC_VERB_SET_PIN_WIDGET_CONTROL,
1714 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001715 if (vol_counter & 0x20) {
1716 /* decrease volume */
1717 if (vol > master_vol)
1718 vol = master_vol;
1719 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1720 0, HDA_AMP_VOLMASK,
1721 master_vol-vol);
1722 } else {
1723 /* increase volume */
1724 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1725 HDA_AMP_VOLMASK,
1726 ((master_vol+vol) > 0x2A) ? 0x2A :
1727 (master_vol+vol));
1728 }
1729 } else if (!(gpio_data & 0x02)) {
1730 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001731 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1732 AC_VERB_SET_PIN_WIDGET_CONTROL,
1733 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001734 }
1735}
1736
1737/* unsolicited event for jack sensing */
1738static void via_unsol_event(struct hda_codec *codec,
1739 unsigned int res)
1740{
1741 res >>= 26;
Takashi Iwai3a938972011-10-28 01:16:55 +02001742 res = snd_hda_jack_get_action(codec, res);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001743
Lydia Wanga34df192009-10-10 19:08:01 +08001744 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001745 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001746
1747 res &= ~VIA_JACK_EVENT;
1748
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001749 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001750 via_hp_automute(codec);
1751 else if (res == VIA_GPIO_EVENT)
1752 via_gpio_control(codec);
Takashi Iwai01a61e12011-10-28 00:03:22 +02001753 snd_hda_jack_report_sync(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001754}
1755
Takashi Iwai2a439522011-07-26 09:52:50 +02001756#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001757static int via_suspend(struct hda_codec *codec, pm_message_t state)
1758{
1759 struct via_spec *spec = codec->spec;
1760 vt1708_stop_hp_work(spec);
1761 return 0;
1762}
1763#endif
1764
Takashi Iwaicb53c622007-08-10 17:21:45 +02001765#ifdef CONFIG_SND_HDA_POWER_SAVE
1766static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1767{
1768 struct via_spec *spec = codec->spec;
1769 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1770}
1771#endif
1772
Joseph Chanc577b8a2006-11-29 15:29:40 +01001773/*
1774 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001775
1776static int via_init(struct hda_codec *codec);
1777
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001778static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001779 .build_controls = via_build_controls,
1780 .build_pcms = via_build_pcms,
1781 .init = via_init,
1782 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001783 .unsol_event = via_unsol_event,
Takashi Iwai2a439522011-07-26 09:52:50 +02001784#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001785 .suspend = via_suspend,
1786#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001787#ifdef CONFIG_SND_HDA_POWER_SAVE
1788 .check_power_status = via_check_power_status,
1789#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001790};
1791
Takashi Iwai4a796162011-06-17 17:53:38 +02001792static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001793{
Takashi Iwai4a796162011-06-17 17:53:38 +02001794 struct via_spec *spec = codec->spec;
1795 int i;
1796
1797 for (i = 0; i < spec->multiout.num_dacs; i++) {
1798 if (spec->multiout.dac_nids[i] == dac)
1799 return false;
1800 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001801 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001802 return false;
1803 return true;
1804}
1805
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001806static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001807 hda_nid_t target_dac, int with_aa_mix,
1808 struct nid_path *path, int depth)
Takashi Iwai4a796162011-06-17 17:53:38 +02001809{
Takashi Iwai3214b962011-07-18 12:49:25 +02001810 struct via_spec *spec = codec->spec;
Takashi Iwai4a796162011-06-17 17:53:38 +02001811 hda_nid_t conn[8];
1812 int i, nums;
1813
Takashi Iwai3214b962011-07-18 12:49:25 +02001814 if (nid == spec->aa_mix_nid) {
1815 if (!with_aa_mix)
1816 return false;
1817 with_aa_mix = 2; /* mark aa-mix is included */
1818 }
1819
Takashi Iwai4a796162011-06-17 17:53:38 +02001820 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1821 for (i = 0; i < nums; i++) {
1822 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1823 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001824 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1825 /* aa-mix is requested but not included? */
1826 if (!(spec->aa_mix_nid && with_aa_mix == 1))
1827 goto found;
1828 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001829 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001830 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001831 return false;
1832 for (i = 0; i < nums; i++) {
1833 unsigned int type;
1834 type = get_wcaps_type(get_wcaps(codec, conn[i]));
Takashi Iwai3214b962011-07-18 12:49:25 +02001835 if (type == AC_WID_AUD_OUT)
Takashi Iwai4a796162011-06-17 17:53:38 +02001836 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001837 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai3214b962011-07-18 12:49:25 +02001838 with_aa_mix, path, depth + 1))
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001839 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001840 }
1841 return false;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001842
1843 found:
1844 path->path[path->depth] = conn[i];
1845 path->idx[path->depth] = i;
1846 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1847 path->multi[path->depth] = 1;
1848 path->depth++;
1849 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001850}
1851
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001852static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001853 hda_nid_t target_dac, int with_aa_mix,
1854 struct nid_path *path)
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001855{
Takashi Iwai3214b962011-07-18 12:49:25 +02001856 if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001857 path->path[path->depth] = nid;
1858 path->depth++;
Takashi Iwai3214b962011-07-18 12:49:25 +02001859 snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
1860 path->depth, path->path[0], path->path[1],
1861 path->path[2], path->path[3], path->path[4]);
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001862 return true;
1863 }
1864 return false;
1865}
1866
Takashi Iwai4a796162011-06-17 17:53:38 +02001867static int via_auto_fill_dac_nids(struct hda_codec *codec)
1868{
1869 struct via_spec *spec = codec->spec;
1870 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001871 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001872 hda_nid_t nid;
1873
Joseph Chanc577b8a2006-11-29 15:29:40 +01001874 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001875 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001876 for (i = 0; i < cfg->line_outs; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001877 hda_nid_t dac = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001878 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001879 if (!nid)
1880 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001881 if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
1882 dac = spec->out_path[i].path[0];
1883 if (!i && parse_output_path(codec, nid, dac, 1,
1884 &spec->out_mix_path))
1885 dac = spec->out_mix_path.path[0];
1886 if (dac) {
1887 spec->private_dac_nids[i] = dac;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001888 dac_num++;
1889 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001890 }
Takashi Iwai3214b962011-07-18 12:49:25 +02001891 if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
1892 spec->out_path[0] = spec->out_mix_path;
1893 spec->out_mix_path.depth = 0;
1894 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001895 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001896 return 0;
1897}
1898
Takashi Iwai4a796162011-06-17 17:53:38 +02001899static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001900 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001901{
Takashi Iwai4a796162011-06-17 17:53:38 +02001902 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001903 char name[32];
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001904 hda_nid_t dac, pin, sel, nid;
1905 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001906
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001907 dac = check_dac ? path->path[0] : 0;
1908 pin = path->path[path->depth - 1];
1909 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001910
Takashi Iwai8df2a312011-06-21 11:48:29 +02001911 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001912 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001913 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001914 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001915 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1916 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001917 else
1918 nid = 0;
1919 if (nid) {
1920 sprintf(name, "%s Playback Volume", pfx);
1921 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001922 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001923 if (err < 0)
1924 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001925 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001926 }
1927
Takashi Iwai8df2a312011-06-21 11:48:29 +02001928 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001929 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001930 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001931 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001932 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1933 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001934 else
1935 nid = 0;
1936 if (nid) {
1937 sprintf(name, "%s Playback Switch", pfx);
1938 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1939 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1940 if (err < 0)
1941 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001942 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001943 }
1944 return 0;
1945}
1946
Takashi Iwaif4a78282011-06-17 18:46:48 +02001947static void mangle_smart51(struct hda_codec *codec)
1948{
1949 struct via_spec *spec = codec->spec;
1950 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001951 struct auto_pin_cfg_item *ins = cfg->inputs;
1952 int i, j, nums, attr;
1953 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001954
Takashi Iwai0f98c242011-06-21 12:51:33 +02001955 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1956 nums = 0;
1957 for (i = 0; i < cfg->num_inputs; i++) {
1958 unsigned int def;
1959 if (ins[i].type > AUTO_PIN_LINE_IN)
1960 continue;
1961 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1962 if (snd_hda_get_input_pin_attr(def) != attr)
1963 continue;
1964 for (j = 0; j < nums; j++)
1965 if (ins[pins[j]].type < ins[i].type) {
1966 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001967 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001968 break;
1969 }
1970 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001971 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001972 }
1973 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001974 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001975 for (i = 0; i < nums; i++) {
1976 hda_nid_t pin = ins[pins[i]].pin;
1977 spec->smart51_pins[spec->smart51_nums++] = pin;
1978 cfg->line_out_pins[cfg->line_outs++] = pin;
1979 if (cfg->line_outs == 3)
1980 break;
1981 }
1982 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001983 }
1984}
1985
Takashi Iwai020066d2011-07-21 13:45:56 +02001986static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src)
1987{
1988 dst->vol_ctl = src->vol_ctl;
1989 dst->mute_ctl = src->mute_ctl;
1990}
1991
Takashi Iwai4a796162011-06-17 17:53:38 +02001992/* add playback controls from the parsed DAC table */
1993static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1994{
1995 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001996 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai3214b962011-07-18 12:49:25 +02001997 struct nid_path *path;
Takashi Iwaiea734962011-01-17 11:29:34 +01001998 static const char * const chname[4] = {
1999 "Front", "Surround", "C/LFE", "Side"
2000 };
Takashi Iwai4a796162011-06-17 17:53:38 +02002001 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02002002 int old_line_outs;
2003
2004 /* check smart51 */
2005 old_line_outs = cfg->line_outs;
2006 if (cfg->line_outs == 1)
2007 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002008
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002009 err = via_auto_fill_dac_nids(codec);
2010 if (err < 0)
2011 return err;
2012
Lydia Wang5c9a5612011-07-08 14:03:43 +08002013 if (spec->multiout.num_dacs < 3) {
2014 spec->smart51_nums = 0;
2015 cfg->line_outs = old_line_outs;
2016 }
Takashi Iwai4a796162011-06-17 17:53:38 +02002017 for (i = 0; i < cfg->line_outs; i++) {
2018 hda_nid_t pin, dac;
2019 pin = cfg->line_out_pins[i];
2020 dac = spec->multiout.dac_nids[i];
2021 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002022 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02002023 path = spec->out_path + i;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02002024 if (i == HDA_CLFE) {
Takashi Iwai3214b962011-07-18 12:49:25 +02002025 err = create_ch_ctls(codec, "Center", 1, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002026 if (err < 0)
2027 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002028 err = create_ch_ctls(codec, "LFE", 2, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002029 if (err < 0)
2030 return err;
2031 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02002032 const char *pfx = chname[i];
2033 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
2034 cfg->line_outs == 1)
2035 pfx = "Speaker";
Takashi Iwai3214b962011-07-18 12:49:25 +02002036 err = create_ch_ctls(codec, pfx, 3, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002037 if (err < 0)
2038 return err;
2039 }
Takashi Iwai020066d2011-07-21 13:45:56 +02002040 if (path != spec->out_path + i)
2041 copy_path_mixer_ctls(&spec->out_path[i], path);
2042 if (path == spec->out_path && spec->out_mix_path.depth)
2043 copy_path_mixer_ctls(&spec->out_mix_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002044 }
2045
Takashi Iwai4a796162011-06-17 17:53:38 +02002046 idx = get_connection_index(codec, spec->aa_mix_nid,
2047 spec->multiout.dac_nids[0]);
2048 if (idx >= 0) {
2049 /* add control to mixer */
Takashi Iwai3214b962011-07-18 12:49:25 +02002050 const char *name;
2051 name = spec->out_mix_path.depth ?
2052 "PCM Loopback Playback Volume" : "PCM Playback Volume";
2053 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002054 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2055 idx, HDA_INPUT));
2056 if (err < 0)
2057 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002058 name = spec->out_mix_path.depth ?
2059 "PCM Loopback Playback Switch" : "PCM Playback Switch";
2060 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002061 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2062 idx, HDA_INPUT));
2063 if (err < 0)
2064 return err;
2065 }
2066
Takashi Iwaif4a78282011-06-17 18:46:48 +02002067 cfg->line_outs = old_line_outs;
2068
Joseph Chanc577b8a2006-11-29 15:29:40 +01002069 return 0;
2070}
2071
Takashi Iwai4a796162011-06-17 17:53:38 +02002072static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002073{
Takashi Iwai4a796162011-06-17 17:53:38 +02002074 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02002075 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002076 bool check_dac;
Takashi Iwai3214b962011-07-18 12:49:25 +02002077 int i, err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002078
2079 if (!pin)
2080 return 0;
2081
Takashi Iwai3214b962011-07-18 12:49:25 +02002082 if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
2083 for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
2084 if (i < spec->multiout.num_dacs &&
2085 parse_output_path(codec, pin,
2086 spec->multiout.dac_nids[i], 0,
2087 &spec->hp_indep_path)) {
2088 spec->hp_indep_shared = i;
2089 break;
2090 }
2091 }
Takashi Iwai25250502011-06-30 17:24:47 +02002092 }
Takashi Iwai3214b962011-07-18 12:49:25 +02002093 if (spec->hp_indep_path.depth) {
2094 spec->hp_dac_nid = spec->hp_indep_path.path[0];
2095 if (!spec->hp_indep_shared)
2096 spec->hp_path = spec->hp_indep_path;
2097 }
2098 /* optionally check front-path w/o AA-mix */
2099 if (!spec->hp_path.depth)
2100 parse_output_path(codec, pin,
2101 spec->multiout.dac_nids[HDA_FRONT], 0,
2102 &spec->hp_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002103
Takashi Iwaiece8d042011-06-19 16:24:21 +02002104 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai3214b962011-07-18 12:49:25 +02002105 1, &spec->hp_mix_path) && !spec->hp_path.depth)
Takashi Iwaiece8d042011-06-19 16:24:21 +02002106 return 0;
2107
Takashi Iwai3214b962011-07-18 12:49:25 +02002108 if (spec->hp_path.depth) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +02002109 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002110 check_dac = true;
2111 } else {
Takashi Iwai3214b962011-07-18 12:49:25 +02002112 path = &spec->hp_mix_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002113 check_dac = false;
2114 }
2115 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002116 if (err < 0)
2117 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002118 if (check_dac)
2119 copy_path_mixer_ctls(&spec->hp_mix_path, path);
2120 else
2121 copy_path_mixer_ctls(&spec->hp_path, path);
2122 if (spec->hp_indep_path.depth)
2123 copy_path_mixer_ctls(&spec->hp_indep_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002124 return 0;
2125}
2126
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002127static int via_auto_create_speaker_ctls(struct hda_codec *codec)
2128{
2129 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +02002130 struct nid_path *path;
2131 bool check_dac;
Wang Shaoyan81c0a782011-08-05 18:51:29 +08002132 hda_nid_t pin, dac = 0;
Takashi Iwai3214b962011-07-18 12:49:25 +02002133 int err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002134
2135 pin = spec->autocfg.speaker_pins[0];
2136 if (!spec->autocfg.speaker_outs || !pin)
2137 return 0;
2138
Takashi Iwai3214b962011-07-18 12:49:25 +02002139 if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02002140 dac = spec->speaker_path.path[0];
Takashi Iwai3214b962011-07-18 12:49:25 +02002141 if (!dac)
2142 parse_output_path(codec, pin,
2143 spec->multiout.dac_nids[HDA_FRONT], 0,
2144 &spec->speaker_path);
2145 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
2146 1, &spec->speaker_mix_path) && !dac)
2147 return 0;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002148
Takashi Iwai3214b962011-07-18 12:49:25 +02002149 /* no AA-path for front? */
2150 if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
2151 dac = 0;
2152
2153 spec->speaker_dac_nid = dac;
2154 spec->multiout.extra_out_nid[0] = dac;
2155 if (dac) {
2156 path = &spec->speaker_path;
2157 check_dac = true;
2158 } else {
2159 path = &spec->speaker_mix_path;
2160 check_dac = false;
2161 }
2162 err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
2163 if (err < 0)
2164 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002165 if (check_dac)
2166 copy_path_mixer_ctls(&spec->speaker_mix_path, path);
2167 else
2168 copy_path_mixer_ctls(&spec->speaker_path, path);
Takashi Iwai3214b962011-07-18 12:49:25 +02002169 return 0;
2170}
2171
2172#define via_aamix_ctl_info via_pin_power_ctl_info
2173
2174static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol,
2175 struct snd_ctl_elem_value *ucontrol)
2176{
2177 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2178 struct via_spec *spec = codec->spec;
2179 ucontrol->value.enumerated.item[0] = spec->aamix_mode;
2180 return 0;
2181}
2182
2183static void update_aamix_paths(struct hda_codec *codec, int do_mix,
2184 struct nid_path *nomix, struct nid_path *mix)
2185{
2186 if (do_mix) {
2187 activate_output_path(codec, nomix, false, false);
2188 activate_output_path(codec, mix, true, false);
2189 } else {
2190 activate_output_path(codec, mix, false, false);
2191 activate_output_path(codec, nomix, true, false);
2192 }
2193}
2194
2195static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol,
2196 struct snd_ctl_elem_value *ucontrol)
2197{
2198 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2199 struct via_spec *spec = codec->spec;
2200 unsigned int val = ucontrol->value.enumerated.item[0];
2201
2202 if (val == spec->aamix_mode)
2203 return 0;
2204 spec->aamix_mode = val;
2205 /* update front path */
2206 update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
2207 /* update HP path */
2208 if (!spec->hp_independent_mode) {
2209 update_aamix_paths(codec, val, &spec->hp_path,
2210 &spec->hp_mix_path);
2211 }
2212 /* update speaker path */
2213 update_aamix_paths(codec, val, &spec->speaker_path,
2214 &spec->speaker_mix_path);
2215 return 1;
2216}
2217
2218static const struct snd_kcontrol_new via_aamix_ctl_enum = {
2219 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2220 .name = "Loopback Mixing",
2221 .info = via_aamix_ctl_info,
2222 .get = via_aamix_ctl_get,
2223 .put = via_aamix_ctl_put,
2224};
2225
2226static int via_auto_create_loopback_switch(struct hda_codec *codec)
2227{
2228 struct via_spec *spec = codec->spec;
2229
Takashi Iwai4808d122012-01-10 15:16:02 +01002230 if (!spec->aa_mix_nid)
2231 return 0; /* no loopback switching available */
2232 if (!(spec->out_mix_path.depth || spec->hp_mix_path.depth ||
2233 spec->speaker_path.depth))
Takashi Iwai3214b962011-07-18 12:49:25 +02002234 return 0; /* no loopback switching available */
2235 if (!via_clone_control(spec, &via_aamix_ctl_enum))
2236 return -ENOMEM;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002237 return 0;
2238}
2239
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002240/* look for ADCs */
2241static int via_fill_adcs(struct hda_codec *codec)
2242{
2243 struct via_spec *spec = codec->spec;
2244 hda_nid_t nid = codec->start_nid;
2245 int i;
2246
2247 for (i = 0; i < codec->num_nodes; i++, nid++) {
2248 unsigned int wcaps = get_wcaps(codec, nid);
2249 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2250 continue;
2251 if (wcaps & AC_WCAP_DIGITAL)
2252 continue;
2253 if (!(wcaps & AC_WCAP_CONN_LIST))
2254 continue;
2255 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2256 return -ENOMEM;
2257 spec->adc_nids[spec->num_adc_nids++] = nid;
2258 }
2259 return 0;
2260}
2261
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002262/* input-src control */
2263static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2264 struct snd_ctl_elem_info *uinfo)
2265{
2266 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2267 struct via_spec *spec = codec->spec;
2268
2269 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2270 uinfo->count = 1;
2271 uinfo->value.enumerated.items = spec->num_inputs;
2272 if (uinfo->value.enumerated.item >= spec->num_inputs)
2273 uinfo->value.enumerated.item = spec->num_inputs - 1;
2274 strcpy(uinfo->value.enumerated.name,
2275 spec->inputs[uinfo->value.enumerated.item].label);
2276 return 0;
2277}
2278
2279static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2280 struct snd_ctl_elem_value *ucontrol)
2281{
2282 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2283 struct via_spec *spec = codec->spec;
2284 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2285
2286 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2287 return 0;
2288}
2289
2290static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2291 struct snd_ctl_elem_value *ucontrol)
2292{
2293 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2294 struct via_spec *spec = codec->spec;
2295 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2296 hda_nid_t mux;
2297 int cur;
2298
2299 cur = ucontrol->value.enumerated.item[0];
2300 if (cur < 0 || cur >= spec->num_inputs)
2301 return -EINVAL;
2302 if (spec->cur_mux[idx] == cur)
2303 return 0;
2304 spec->cur_mux[idx] = cur;
2305 if (spec->dyn_adc_switch) {
2306 int adc_idx = spec->inputs[cur].adc_idx;
2307 mux = spec->mux_nids[adc_idx];
2308 via_dyn_adc_pcm_resetup(codec, cur);
2309 } else {
2310 mux = spec->mux_nids[idx];
2311 if (snd_BUG_ON(!mux))
2312 return -EINVAL;
2313 }
2314
2315 if (mux) {
2316 /* switch to D0 beofre change index */
Takashi Iwai054d8672012-01-24 12:25:50 +01002317 update_power_state(codec, mux, AC_PWRST_D0);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002318 snd_hda_codec_write(codec, mux, 0,
2319 AC_VERB_SET_CONNECT_SEL,
2320 spec->inputs[cur].mux_idx);
2321 }
2322
2323 /* update jack power state */
2324 set_widgets_power_state(codec);
2325 return 0;
2326}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002327
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002328static const struct snd_kcontrol_new via_input_src_ctl = {
2329 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2330 /* The multiple "Capture Source" controls confuse alsamixer
2331 * So call somewhat different..
2332 */
2333 /* .name = "Capture Source", */
2334 .name = "Input Source",
2335 .info = via_mux_enum_info,
2336 .get = via_mux_enum_get,
2337 .put = via_mux_enum_put,
2338};
2339
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002340static int create_input_src_ctls(struct hda_codec *codec, int count)
2341{
2342 struct via_spec *spec = codec->spec;
2343 struct snd_kcontrol_new *knew;
2344
2345 if (spec->num_inputs <= 1 || !count)
2346 return 0; /* no need for single src */
2347
2348 knew = via_clone_control(spec, &via_input_src_ctl);
2349 if (!knew)
2350 return -ENOMEM;
2351 knew->count = count;
2352 return 0;
2353}
2354
2355/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002356static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2357{
2358 struct hda_amp_list *list;
2359
2360 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2361 return;
2362 list = spec->loopback_list + spec->num_loopbacks;
2363 list->nid = mix;
2364 list->dir = HDA_INPUT;
2365 list->idx = idx;
2366 spec->num_loopbacks++;
2367 spec->loopback.amplist = spec->loopback_list;
2368}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002369
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002370static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002371 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002372{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002373 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002374}
2375
2376/* add the input-route to the given pin */
2377static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002378{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002379 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002380 int c, idx;
2381
2382 spec->inputs[spec->num_inputs].adc_idx = -1;
2383 spec->inputs[spec->num_inputs].pin = pin;
2384 for (c = 0; c < spec->num_adc_nids; c++) {
2385 if (spec->mux_nids[c]) {
2386 idx = get_connection_index(codec, spec->mux_nids[c],
2387 pin);
2388 if (idx < 0)
2389 continue;
2390 spec->inputs[spec->num_inputs].mux_idx = idx;
2391 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002392 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002393 continue;
2394 }
2395 spec->inputs[spec->num_inputs].adc_idx = c;
2396 /* Can primary ADC satisfy all inputs? */
2397 if (!spec->dyn_adc_switch &&
2398 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2399 snd_printd(KERN_INFO
2400 "via: dynamic ADC switching enabled\n");
2401 spec->dyn_adc_switch = 1;
2402 }
2403 return true;
2404 }
2405 return false;
2406}
2407
2408static int get_mux_nids(struct hda_codec *codec);
2409
2410/* parse input-routes; fill ADCs, MUXs and input-src entries */
2411static int parse_analog_inputs(struct hda_codec *codec)
2412{
2413 struct via_spec *spec = codec->spec;
2414 const struct auto_pin_cfg *cfg = &spec->autocfg;
2415 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002416
2417 err = via_fill_adcs(codec);
2418 if (err < 0)
2419 return err;
2420 err = get_mux_nids(codec);
2421 if (err < 0)
2422 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002423
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002424 /* fill all input-routes */
2425 for (i = 0; i < cfg->num_inputs; i++) {
2426 if (add_input_route(codec, cfg->inputs[i].pin))
2427 spec->inputs[spec->num_inputs++].label =
2428 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002429 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002430
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002431 /* check for internal loopback recording */
2432 if (spec->aa_mix_nid &&
2433 add_input_route(codec, spec->aa_mix_nid))
2434 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2435
2436 return 0;
2437}
2438
2439/* create analog-loopback volume/switch controls */
2440static int create_loopback_ctls(struct hda_codec *codec)
2441{
2442 struct via_spec *spec = codec->spec;
2443 const struct auto_pin_cfg *cfg = &spec->autocfg;
2444 const char *prev_label = NULL;
2445 int type_idx = 0;
2446 int i, j, err, idx;
2447
2448 if (!spec->aa_mix_nid)
2449 return 0;
2450
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002451 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002452 hda_nid_t pin = cfg->inputs[i].pin;
2453 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2454
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002455 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002456 type_idx++;
2457 else
2458 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002459 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002460 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2461 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002462 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002463 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002464 if (err < 0)
2465 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002466 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002467 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002468
2469 /* remember the label for smart51 control */
2470 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002471 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002472 spec->smart51_idxs[j] = idx;
2473 spec->smart51_labels[j] = label;
2474 break;
2475 }
2476 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002477 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002478 return 0;
2479}
2480
2481/* create mic-boost controls (if present) */
2482static int create_mic_boost_ctls(struct hda_codec *codec)
2483{
2484 struct via_spec *spec = codec->spec;
2485 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai8d8bbc62012-02-22 12:26:38 +01002486 const char *prev_label = NULL;
2487 int type_idx = 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002488 int i, err;
2489
2490 for (i = 0; i < cfg->num_inputs; i++) {
2491 hda_nid_t pin = cfg->inputs[i].pin;
2492 unsigned int caps;
2493 const char *label;
2494 char name[32];
2495
2496 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2497 continue;
2498 caps = query_amp_caps(codec, pin, HDA_INPUT);
2499 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2500 continue;
2501 label = hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwai8d8bbc62012-02-22 12:26:38 +01002502 if (prev_label && !strcmp(label, prev_label))
2503 type_idx++;
2504 else
2505 type_idx = 0;
2506 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002507 snprintf(name, sizeof(name), "%s Boost Volume", label);
Takashi Iwai8d8bbc62012-02-22 12:26:38 +01002508 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002509 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2510 if (err < 0)
2511 return err;
2512 }
2513 return 0;
2514}
2515
2516/* create capture and input-src controls for multiple streams */
2517static int create_multi_adc_ctls(struct hda_codec *codec)
2518{
2519 struct via_spec *spec = codec->spec;
2520 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002521
2522 /* create capture mixer elements */
2523 for (i = 0; i < spec->num_adc_nids; i++) {
2524 hda_nid_t adc = spec->adc_nids[i];
2525 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2526 "Capture Volume", i,
2527 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2528 HDA_INPUT));
2529 if (err < 0)
2530 return err;
2531 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2532 "Capture Switch", i,
2533 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2534 HDA_INPUT));
2535 if (err < 0)
2536 return err;
2537 }
2538
2539 /* input-source control */
2540 for (i = 0; i < spec->num_adc_nids; i++)
2541 if (!spec->mux_nids[i])
2542 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002543 err = create_input_src_ctls(codec, i);
2544 if (err < 0)
2545 return err;
2546 return 0;
2547}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002548
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002549/* bind capture volume/switch */
2550static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2551 HDA_BIND_VOL("Capture Volume", 0);
2552static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2553 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002554
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002555static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2556 struct hda_ctl_ops *ops)
2557{
2558 struct hda_bind_ctls *ctl;
2559 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002560
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002561 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2562 if (!ctl)
2563 return -ENOMEM;
2564 ctl->ops = ops;
2565 for (i = 0; i < spec->num_adc_nids; i++)
2566 ctl->values[i] =
2567 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2568 *ctl_ret = ctl;
2569 return 0;
2570}
2571
2572/* create capture and input-src controls for dynamic ADC-switch case */
2573static int create_dyn_adc_ctls(struct hda_codec *codec)
2574{
2575 struct via_spec *spec = codec->spec;
2576 struct snd_kcontrol_new *knew;
2577 int err;
2578
2579 /* set up the bind capture ctls */
2580 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2581 if (err < 0)
2582 return err;
2583 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2584 if (err < 0)
2585 return err;
2586
2587 /* create capture mixer elements */
2588 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2589 if (!knew)
2590 return -ENOMEM;
2591 knew->private_value = (long)spec->bind_cap_vol;
2592
2593 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2594 if (!knew)
2595 return -ENOMEM;
2596 knew->private_value = (long)spec->bind_cap_sw;
2597
2598 /* input-source control */
2599 err = create_input_src_ctls(codec, 1);
2600 if (err < 0)
2601 return err;
2602 return 0;
2603}
2604
2605/* parse and create capture-related stuff */
2606static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2607{
2608 struct via_spec *spec = codec->spec;
2609 int err;
2610
2611 err = parse_analog_inputs(codec);
2612 if (err < 0)
2613 return err;
2614 if (spec->dyn_adc_switch)
2615 err = create_dyn_adc_ctls(codec);
2616 else
2617 err = create_multi_adc_ctls(codec);
2618 if (err < 0)
2619 return err;
2620 err = create_loopback_ctls(codec);
2621 if (err < 0)
2622 return err;
2623 err = create_mic_boost_ctls(codec);
2624 if (err < 0)
2625 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002626 return 0;
2627}
2628
Harald Welte76d9b0d2008-09-09 15:50:37 +08002629static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2630{
2631 unsigned int def_conf;
2632 unsigned char seqassoc;
2633
Takashi Iwai2f334f92009-02-20 14:37:42 +01002634 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002635 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2636 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002637 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2638 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2639 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2640 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002641 }
2642
2643 return;
2644}
2645
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002646static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002647 struct snd_ctl_elem_value *ucontrol)
2648{
2649 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2650 struct via_spec *spec = codec->spec;
2651
2652 if (spec->codec_type != VT1708)
2653 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002654 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002655 return 0;
2656}
2657
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002658static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002659 struct snd_ctl_elem_value *ucontrol)
2660{
2661 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2662 struct via_spec *spec = codec->spec;
Takashi Iwai187d3332011-11-24 16:33:09 +01002663 int val;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002664
2665 if (spec->codec_type != VT1708)
2666 return 0;
Takashi Iwai187d3332011-11-24 16:33:09 +01002667 val = !!ucontrol->value.integer.value[0];
2668 if (spec->vt1708_jack_detect == val)
2669 return 0;
2670 spec->vt1708_jack_detect = val;
2671 if (spec->vt1708_jack_detect &&
2672 snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") != 1) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002673 mute_aa_path(codec, 1);
2674 notify_aa_path_ctls(codec);
2675 }
Takashi Iwai187d3332011-11-24 16:33:09 +01002676 via_hp_automute(codec);
2677 vt1708_update_hp_work(spec);
2678 return 1;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002679}
2680
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002681static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2682 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2683 .name = "Jack Detect",
2684 .count = 1,
2685 .info = snd_ctl_boolean_mono_info,
2686 .get = vt1708_jack_detect_get,
2687 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002688};
2689
Takashi Iwai12daef62011-06-18 17:45:49 +02002690static void fill_dig_outs(struct hda_codec *codec);
2691static void fill_dig_in(struct hda_codec *codec);
2692
2693static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002694{
2695 struct via_spec *spec = codec->spec;
2696 int err;
2697
2698 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2699 if (err < 0)
2700 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002701 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002702 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002703
Takashi Iwai4a796162011-06-17 17:53:38 +02002704 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002705 if (err < 0)
2706 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002707 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002708 if (err < 0)
2709 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002710 err = via_auto_create_speaker_ctls(codec);
2711 if (err < 0)
2712 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002713 err = via_auto_create_loopback_switch(codec);
2714 if (err < 0)
2715 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002716 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002717 if (err < 0)
2718 return err;
2719
2720 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2721
Takashi Iwai12daef62011-06-18 17:45:49 +02002722 fill_dig_outs(codec);
2723 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002724
Takashi Iwai603c4012008-07-30 15:01:44 +02002725 if (spec->kctls.list)
2726 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002727
Joseph Chanc577b8a2006-11-29 15:29:40 +01002728
Takashi Iwai3214b962011-07-18 12:49:25 +02002729 if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002730 err = via_hp_build(codec);
2731 if (err < 0)
2732 return err;
2733 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002734
Takashi Iwaif4a78282011-06-17 18:46:48 +02002735 err = via_smart51_build(codec);
2736 if (err < 0)
2737 return err;
2738
Takashi Iwai5d417622011-06-20 11:32:27 +02002739 /* assign slave outs */
2740 if (spec->slave_dig_outs[0])
2741 codec->slave_dig_outs = spec->slave_dig_outs;
2742
Joseph Chanc577b8a2006-11-29 15:29:40 +01002743 return 1;
2744}
2745
Takashi Iwai5d417622011-06-20 11:32:27 +02002746static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002747{
Lydia Wang25eaba22009-10-10 19:08:43 +08002748 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002749 if (spec->multiout.dig_out_nid)
2750 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2751 if (spec->slave_dig_outs[0])
2752 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2753}
Lydia Wang25eaba22009-10-10 19:08:43 +08002754
Takashi Iwai5d417622011-06-20 11:32:27 +02002755static void via_auto_init_dig_in(struct hda_codec *codec)
2756{
2757 struct via_spec *spec = codec->spec;
2758 if (!spec->dig_in_nid)
2759 return;
2760 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2761 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2762}
2763
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002764/* initialize the unsolicited events */
2765static void via_auto_init_unsol_event(struct hda_codec *codec)
2766{
2767 struct via_spec *spec = codec->spec;
2768 struct auto_pin_cfg *cfg = &spec->autocfg;
2769 unsigned int ev;
2770 int i;
2771
2772 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002773 snd_hda_jack_detect_enable(codec, cfg->hp_pins[0],
2774 VIA_HP_EVENT | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002775
2776 if (cfg->speaker_pins[0])
2777 ev = VIA_LINE_EVENT;
2778 else
2779 ev = 0;
2780 for (i = 0; i < cfg->line_outs; i++) {
2781 if (cfg->line_out_pins[i] &&
2782 is_jack_detectable(codec, cfg->line_out_pins[i]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002783 snd_hda_jack_detect_enable(codec, cfg->line_out_pins[i],
2784 ev | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002785 }
2786
2787 for (i = 0; i < cfg->num_inputs; i++) {
2788 if (is_jack_detectable(codec, cfg->inputs[i].pin))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002789 snd_hda_jack_detect_enable(codec, cfg->inputs[i].pin,
2790 VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002791 }
2792}
2793
Takashi Iwai5d417622011-06-20 11:32:27 +02002794static int via_init(struct hda_codec *codec)
2795{
2796 struct via_spec *spec = codec->spec;
2797 int i;
2798
2799 for (i = 0; i < spec->num_iverbs; i++)
2800 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2801
Takashi Iwaie9d010c2012-02-01 10:33:23 +01002802 /* init power states */
2803 set_widgets_power_state(codec);
2804 __analog_low_current_mode(codec, true);
2805
Joseph Chanc577b8a2006-11-29 15:29:40 +01002806 via_auto_init_multi_out(codec);
2807 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002808 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002809 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002810 via_auto_init_dig_outs(codec);
2811 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002812
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002813 via_auto_init_unsol_event(codec);
2814
2815 via_hp_automute(codec);
Takashi Iwai187d3332011-11-24 16:33:09 +01002816 vt1708_update_hp_work(spec);
Takashi Iwai01a61e12011-10-28 00:03:22 +02002817 snd_hda_jack_report_sync(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002818
Joseph Chanc577b8a2006-11-29 15:29:40 +01002819 return 0;
2820}
2821
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002822static void vt1708_update_hp_jack_state(struct work_struct *work)
2823{
2824 struct via_spec *spec = container_of(work, struct via_spec,
2825 vt1708_hp_work.work);
2826 if (spec->codec_type != VT1708)
2827 return;
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002828 snd_hda_jack_set_dirty_all(spec->codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002829 /* if jack state toggled */
2830 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002831 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002832 spec->vt1708_hp_present ^= 1;
2833 via_hp_automute(spec->codec);
2834 }
Takashi Iwai187d3332011-11-24 16:33:09 +01002835 if (spec->vt1708_jack_detect)
2836 schedule_delayed_work(&spec->vt1708_hp_work,
2837 msecs_to_jiffies(100));
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002838}
2839
Takashi Iwai337b9d02009-07-07 18:18:59 +02002840static int get_mux_nids(struct hda_codec *codec)
2841{
2842 struct via_spec *spec = codec->spec;
2843 hda_nid_t nid, conn[8];
2844 unsigned int type;
2845 int i, n;
2846
2847 for (i = 0; i < spec->num_adc_nids; i++) {
2848 nid = spec->adc_nids[i];
2849 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002850 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002851 if (type == AC_WID_PIN)
2852 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002853 n = snd_hda_get_connections(codec, nid, conn,
2854 ARRAY_SIZE(conn));
2855 if (n <= 0)
2856 break;
2857 if (n > 1) {
2858 spec->mux_nids[i] = nid;
2859 break;
2860 }
2861 nid = conn[0];
2862 }
2863 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002864 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002865}
2866
Joseph Chanc577b8a2006-11-29 15:29:40 +01002867static int patch_vt1708(struct hda_codec *codec)
2868{
2869 struct via_spec *spec;
2870 int err;
2871
2872 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002873 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002874 if (spec == NULL)
2875 return -ENOMEM;
2876
Takashi Iwai620e2b22011-06-17 17:19:19 +02002877 spec->aa_mix_nid = 0x17;
2878
Takashi Iwai12daef62011-06-18 17:45:49 +02002879 /* Add HP and CD pin config connect bit re-config action */
2880 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2881 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2882
Joseph Chanc577b8a2006-11-29 15:29:40 +01002883 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002884 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002885 if (err < 0) {
2886 via_free(codec);
2887 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002888 }
2889
Takashi Iwai12daef62011-06-18 17:45:49 +02002890 /* add jack detect on/off control */
2891 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2892 return -ENOMEM;
2893
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002894 /* disable 32bit format on VT1708 */
2895 if (codec->vendor_id == 0x11061708)
2896 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002897
Lydia Wange322a362011-06-29 13:52:02 +08002898 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2899
Joseph Chanc577b8a2006-11-29 15:29:40 +01002900 codec->patch_ops = via_patch_ops;
2901
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002902 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002903 return 0;
2904}
2905
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002906static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002907{
2908 struct via_spec *spec;
2909 int err;
2910
2911 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002912 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002913 if (spec == NULL)
2914 return -ENOMEM;
2915
Takashi Iwai620e2b22011-06-17 17:19:19 +02002916 spec->aa_mix_nid = 0x18;
2917
Takashi Iwai12daef62011-06-18 17:45:49 +02002918 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002919 if (err < 0) {
2920 via_free(codec);
2921 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002922 }
2923
Joseph Chanc577b8a2006-11-29 15:29:40 +01002924 codec->patch_ops = via_patch_ops;
2925
Josepch Chanf7278fd2007-12-13 16:40:40 +01002926 return 0;
2927}
2928
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002929static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2930{
2931 struct via_spec *spec = codec->spec;
2932 int imux_is_smixer;
2933 unsigned int parm;
2934 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002935 if ((spec->codec_type != VT1708B_4CH) &&
2936 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002937 is_8ch = 1;
2938
2939 /* SW0 (17h) = stereo mixer */
2940 imux_is_smixer =
2941 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2942 == ((spec->codec_type == VT1708S) ? 5 : 0));
2943 /* inputs */
2944 /* PW 1/2/5 (1ah/1bh/1eh) */
2945 parm = AC_PWRST_D3;
2946 set_pin_power_state(codec, 0x1a, &parm);
2947 set_pin_power_state(codec, 0x1b, &parm);
2948 set_pin_power_state(codec, 0x1e, &parm);
2949 if (imux_is_smixer)
2950 parm = AC_PWRST_D0;
2951 /* SW0 (17h), AIW 0/1 (13h/14h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01002952 update_power_state(codec, 0x17, parm);
2953 update_power_state(codec, 0x13, parm);
2954 update_power_state(codec, 0x14, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002955
2956 /* outputs */
2957 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2958 parm = AC_PWRST_D3;
2959 set_pin_power_state(codec, 0x19, &parm);
2960 if (spec->smart51_enabled)
2961 set_pin_power_state(codec, 0x1b, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01002962 update_power_state(codec, 0x18, parm);
2963 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002964
2965 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2966 if (is_8ch) {
2967 parm = AC_PWRST_D3;
2968 set_pin_power_state(codec, 0x22, &parm);
2969 if (spec->smart51_enabled)
2970 set_pin_power_state(codec, 0x1a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01002971 update_power_state(codec, 0x26, parm);
2972 update_power_state(codec, 0x24, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002973 } else if (codec->vendor_id == 0x11064397) {
2974 /* PW7(23h), SW2(27h), AOW2(25h) */
2975 parm = AC_PWRST_D3;
2976 set_pin_power_state(codec, 0x23, &parm);
2977 if (spec->smart51_enabled)
2978 set_pin_power_state(codec, 0x1a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01002979 update_power_state(codec, 0x27, parm);
2980 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002981 }
2982
2983 /* PW 3/4/7 (1ch/1dh/23h) */
2984 parm = AC_PWRST_D3;
2985 /* force to D0 for internal Speaker */
2986 set_pin_power_state(codec, 0x1c, &parm);
2987 set_pin_power_state(codec, 0x1d, &parm);
2988 if (is_8ch)
2989 set_pin_power_state(codec, 0x23, &parm);
2990
2991 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01002992 update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
2993 update_power_state(codec, 0x10, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002994 if (is_8ch) {
Takashi Iwai054d8672012-01-24 12:25:50 +01002995 update_power_state(codec, 0x25, parm);
2996 update_power_state(codec, 0x27, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002997 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01002998 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002999}
3000
Lydia Wang518bf3b2009-10-10 19:07:29 +08003001static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003002static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01003003{
3004 struct via_spec *spec;
3005 int err;
3006
Lydia Wang518bf3b2009-10-10 19:07:29 +08003007 if (get_codec_type(codec) == VT1708BCE)
3008 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003009
Josepch Chanf7278fd2007-12-13 16:40:40 +01003010 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003011 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003012 if (spec == NULL)
3013 return -ENOMEM;
3014
Takashi Iwai620e2b22011-06-17 17:19:19 +02003015 spec->aa_mix_nid = 0x16;
3016
Josepch Chanf7278fd2007-12-13 16:40:40 +01003017 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003018 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003019 if (err < 0) {
3020 via_free(codec);
3021 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003022 }
3023
Josepch Chanf7278fd2007-12-13 16:40:40 +01003024 codec->patch_ops = via_patch_ops;
3025
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003026 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
3027
Josepch Chanf7278fd2007-12-13 16:40:40 +01003028 return 0;
3029}
3030
Harald Welted949cac2008-09-09 15:56:01 +08003031/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02003032static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08003033 /* Enable Mic Boost Volume backdoor */
3034 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003035 /* don't bybass mixer */
3036 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08003037 { }
3038};
3039
Takashi Iwai9da29272009-05-07 16:31:14 +02003040/* fill out digital output widgets; one for master and one for slave outputs */
3041static void fill_dig_outs(struct hda_codec *codec)
3042{
3043 struct via_spec *spec = codec->spec;
3044 int i;
3045
3046 for (i = 0; i < spec->autocfg.dig_outs; i++) {
3047 hda_nid_t nid;
3048 int conn;
3049
3050 nid = spec->autocfg.dig_out_pins[i];
3051 if (!nid)
3052 continue;
3053 conn = snd_hda_get_connections(codec, nid, &nid, 1);
3054 if (conn < 1)
3055 continue;
3056 if (!spec->multiout.dig_out_nid)
3057 spec->multiout.dig_out_nid = nid;
3058 else {
3059 spec->slave_dig_outs[0] = nid;
3060 break; /* at most two dig outs */
3061 }
3062 }
3063}
3064
Takashi Iwai12daef62011-06-18 17:45:49 +02003065static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08003066{
3067 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02003068 hda_nid_t dig_nid;
3069 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08003070
Takashi Iwai12daef62011-06-18 17:45:49 +02003071 if (!spec->autocfg.dig_in_pin)
3072 return;
Harald Welted949cac2008-09-09 15:56:01 +08003073
Takashi Iwai12daef62011-06-18 17:45:49 +02003074 dig_nid = codec->start_nid;
3075 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
3076 unsigned int wcaps = get_wcaps(codec, dig_nid);
3077 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
3078 continue;
3079 if (!(wcaps & AC_WCAP_DIGITAL))
3080 continue;
3081 if (!(wcaps & AC_WCAP_CONN_LIST))
3082 continue;
3083 err = get_connection_index(codec, dig_nid,
3084 spec->autocfg.dig_in_pin);
3085 if (err >= 0) {
3086 spec->dig_in_nid = dig_nid;
3087 break;
3088 }
3089 }
Harald Welted949cac2008-09-09 15:56:01 +08003090}
3091
Lydia Wang6369bcf2009-10-10 19:08:31 +08003092static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
3093 int offset, int num_steps, int step_size)
3094{
3095 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
3096 (offset << AC_AMPCAP_OFFSET_SHIFT) |
3097 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
3098 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
3099 (0 << AC_AMPCAP_MUTE_SHIFT));
3100}
3101
Harald Welted949cac2008-09-09 15:56:01 +08003102static int patch_vt1708S(struct hda_codec *codec)
3103{
3104 struct via_spec *spec;
3105 int err;
3106
3107 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003108 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003109 if (spec == NULL)
3110 return -ENOMEM;
3111
Takashi Iwai620e2b22011-06-17 17:19:19 +02003112 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003113 override_mic_boost(codec, 0x1a, 0, 3, 40);
3114 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003115
Harald Welted949cac2008-09-09 15:56:01 +08003116 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003117 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003118 if (err < 0) {
3119 via_free(codec);
3120 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003121 }
3122
Takashi Iwai096a8852011-06-20 12:09:02 +02003123 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003124
Harald Welted949cac2008-09-09 15:56:01 +08003125 codec->patch_ops = via_patch_ops;
3126
Lydia Wang518bf3b2009-10-10 19:07:29 +08003127 /* correct names for VT1708BCE */
3128 if (get_codec_type(codec) == VT1708BCE) {
3129 kfree(codec->chip_name);
3130 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3131 snprintf(codec->bus->card->mixername,
3132 sizeof(codec->bus->card->mixername),
3133 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08003134 }
Lydia Wangbc92df72011-03-23 17:56:05 +08003135 /* correct names for VT1705 */
3136 if (codec->vendor_id == 0x11064397) {
3137 kfree(codec->chip_name);
3138 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3139 snprintf(codec->bus->card->mixername,
3140 sizeof(codec->bus->card->mixername),
3141 "%s %s", codec->vendor_name, codec->chip_name);
3142 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003143 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08003144 return 0;
3145}
3146
3147/* Patch for VT1702 */
3148
Takashi Iwai096a8852011-06-20 12:09:02 +02003149static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003150 /* mixer enable */
3151 {0x1, 0xF88, 0x3},
3152 /* GPIO 0~2 */
3153 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003154 { }
3155};
3156
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003157static void set_widgets_power_state_vt1702(struct hda_codec *codec)
3158{
3159 int imux_is_smixer =
3160 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3161 unsigned int parm;
3162 /* inputs */
3163 /* PW 1/2/5 (14h/15h/18h) */
3164 parm = AC_PWRST_D3;
3165 set_pin_power_state(codec, 0x14, &parm);
3166 set_pin_power_state(codec, 0x15, &parm);
3167 set_pin_power_state(codec, 0x18, &parm);
3168 if (imux_is_smixer)
3169 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
3170 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003171 update_power_state(codec, 0x13, parm);
3172 update_power_state(codec, 0x12, parm);
3173 update_power_state(codec, 0x1f, parm);
3174 update_power_state(codec, 0x20, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003175
3176 /* outputs */
3177 /* PW 3/4 (16h/17h) */
3178 parm = AC_PWRST_D3;
3179 set_pin_power_state(codec, 0x17, &parm);
3180 set_pin_power_state(codec, 0x16, &parm);
3181 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003182 update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm);
3183 update_power_state(codec, 0x10, parm);
3184 update_power_state(codec, 0x1d, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003185}
3186
Harald Welted949cac2008-09-09 15:56:01 +08003187static int patch_vt1702(struct hda_codec *codec)
3188{
3189 struct via_spec *spec;
3190 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003191
3192 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003193 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003194 if (spec == NULL)
3195 return -ENOMEM;
3196
Takashi Iwai620e2b22011-06-17 17:19:19 +02003197 spec->aa_mix_nid = 0x1a;
3198
Takashi Iwai12daef62011-06-18 17:45:49 +02003199 /* limit AA path volume to 0 dB */
3200 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3201 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3202 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3203 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3204 (1 << AC_AMPCAP_MUTE_SHIFT));
3205
Harald Welted949cac2008-09-09 15:56:01 +08003206 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003207 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003208 if (err < 0) {
3209 via_free(codec);
3210 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003211 }
3212
Takashi Iwai096a8852011-06-20 12:09:02 +02003213 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003214
Harald Welted949cac2008-09-09 15:56:01 +08003215 codec->patch_ops = via_patch_ops;
3216
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003217 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08003218 return 0;
3219}
3220
Lydia Wangeb7188c2009-10-10 19:08:34 +08003221/* Patch for VT1718S */
3222
Takashi Iwai096a8852011-06-20 12:09:02 +02003223static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08003224 /* Enable MW0 adjust Gain 5 */
3225 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003226 /* Enable Boost Volume backdoor */
3227 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02003228
Lydia Wangeb7188c2009-10-10 19:08:34 +08003229 { }
3230};
3231
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003232static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
3233{
3234 struct via_spec *spec = codec->spec;
3235 int imux_is_smixer;
3236 unsigned int parm;
3237 /* MUX6 (1eh) = stereo mixer */
3238 imux_is_smixer =
3239 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3240 /* inputs */
3241 /* PW 5/6/7 (29h/2ah/2bh) */
3242 parm = AC_PWRST_D3;
3243 set_pin_power_state(codec, 0x29, &parm);
3244 set_pin_power_state(codec, 0x2a, &parm);
3245 set_pin_power_state(codec, 0x2b, &parm);
3246 if (imux_is_smixer)
3247 parm = AC_PWRST_D0;
3248 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003249 update_power_state(codec, 0x1e, parm);
3250 update_power_state(codec, 0x1f, parm);
3251 update_power_state(codec, 0x10, parm);
3252 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003253
3254 /* outputs */
3255 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
3256 parm = AC_PWRST_D3;
3257 set_pin_power_state(codec, 0x27, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003258 update_power_state(codec, 0x1a, parm);
3259 update_power_state(codec, 0xb, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003260
3261 /* PW2 (26h), AOW2 (ah) */
3262 parm = AC_PWRST_D3;
3263 set_pin_power_state(codec, 0x26, &parm);
3264 if (spec->smart51_enabled)
3265 set_pin_power_state(codec, 0x2b, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003266 update_power_state(codec, 0xa, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003267
3268 /* PW0 (24h), AOW0 (8h) */
3269 parm = AC_PWRST_D3;
3270 set_pin_power_state(codec, 0x24, &parm);
3271 if (!spec->hp_independent_mode) /* check for redirected HP */
3272 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003273 update_power_state(codec, 0x8, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003274 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003275 update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003276
3277 /* PW1 (25h), AOW1 (9h) */
3278 parm = AC_PWRST_D3;
3279 set_pin_power_state(codec, 0x25, &parm);
3280 if (spec->smart51_enabled)
3281 set_pin_power_state(codec, 0x2a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003282 update_power_state(codec, 0x9, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003283
3284 if (spec->hp_independent_mode) {
3285 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3286 parm = AC_PWRST_D3;
3287 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003288 update_power_state(codec, 0x1b, parm);
3289 update_power_state(codec, 0x34, parm);
3290 update_power_state(codec, 0xc, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003291 }
3292}
3293
Takashi Iwai30b45032011-07-11 17:05:04 +02003294/* Add a connection to the primary DAC from AA-mixer for some codecs
3295 * This isn't listed from the raw info, but the chip has a secret connection.
3296 */
3297static int add_secret_dac_path(struct hda_codec *codec)
3298{
3299 struct via_spec *spec = codec->spec;
3300 int i, nums;
3301 hda_nid_t conn[8];
3302 hda_nid_t nid;
3303
3304 if (!spec->aa_mix_nid)
3305 return 0;
3306 nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
3307 ARRAY_SIZE(conn) - 1);
3308 for (i = 0; i < nums; i++) {
3309 if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
3310 return 0;
3311 }
3312
3313 /* find the primary DAC and add to the connection list */
3314 nid = codec->start_nid;
3315 for (i = 0; i < codec->num_nodes; i++, nid++) {
3316 unsigned int caps = get_wcaps(codec, nid);
3317 if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
3318 !(caps & AC_WCAP_DIGITAL)) {
3319 conn[nums++] = nid;
3320 return snd_hda_override_conn_list(codec,
3321 spec->aa_mix_nid,
3322 nums, conn);
3323 }
3324 }
3325 return 0;
3326}
3327
3328
Lydia Wangeb7188c2009-10-10 19:08:34 +08003329static int patch_vt1718S(struct hda_codec *codec)
3330{
3331 struct via_spec *spec;
3332 int err;
3333
3334 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003335 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003336 if (spec == NULL)
3337 return -ENOMEM;
3338
Takashi Iwai620e2b22011-06-17 17:19:19 +02003339 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003340 override_mic_boost(codec, 0x2b, 0, 3, 40);
3341 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003342 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003343
Lydia Wangeb7188c2009-10-10 19:08:34 +08003344 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003345 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003346 if (err < 0) {
3347 via_free(codec);
3348 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003349 }
3350
Takashi Iwai096a8852011-06-20 12:09:02 +02003351 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003352
Lydia Wangeb7188c2009-10-10 19:08:34 +08003353 codec->patch_ops = via_patch_ops;
3354
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003355 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3356
Lydia Wangeb7188c2009-10-10 19:08:34 +08003357 return 0;
3358}
Lydia Wangf3db4232009-10-10 19:08:41 +08003359
3360/* Patch for VT1716S */
3361
3362static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3363 struct snd_ctl_elem_info *uinfo)
3364{
3365 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3366 uinfo->count = 1;
3367 uinfo->value.integer.min = 0;
3368 uinfo->value.integer.max = 1;
3369 return 0;
3370}
3371
3372static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3373 struct snd_ctl_elem_value *ucontrol)
3374{
3375 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3376 int index = 0;
3377
3378 index = snd_hda_codec_read(codec, 0x26, 0,
3379 AC_VERB_GET_CONNECT_SEL, 0);
3380 if (index != -1)
3381 *ucontrol->value.integer.value = index;
3382
3383 return 0;
3384}
3385
3386static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3387 struct snd_ctl_elem_value *ucontrol)
3388{
3389 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3390 struct via_spec *spec = codec->spec;
3391 int index = *ucontrol->value.integer.value;
3392
3393 snd_hda_codec_write(codec, 0x26, 0,
3394 AC_VERB_SET_CONNECT_SEL, index);
3395 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003396 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003397 return 1;
3398}
3399
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003400static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003401 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3402 {
3403 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3404 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003405 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003406 .count = 1,
3407 .info = vt1716s_dmic_info,
3408 .get = vt1716s_dmic_get,
3409 .put = vt1716s_dmic_put,
3410 },
3411 {} /* end */
3412};
3413
3414
3415/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003416static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003417 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3418 { } /* end */
3419};
3420
Takashi Iwai096a8852011-06-20 12:09:02 +02003421static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003422 /* Enable Boost Volume backdoor */
3423 {0x1, 0xf8a, 0x80},
3424 /* don't bybass mixer */
3425 {0x1, 0xf88, 0xc0},
3426 /* Enable mono output */
3427 {0x1, 0xf90, 0x08},
3428 { }
3429};
3430
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003431static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3432{
3433 struct via_spec *spec = codec->spec;
3434 int imux_is_smixer;
3435 unsigned int parm;
3436 unsigned int mono_out, present;
3437 /* SW0 (17h) = stereo mixer */
3438 imux_is_smixer =
3439 (snd_hda_codec_read(codec, 0x17, 0,
3440 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3441 /* inputs */
3442 /* PW 1/2/5 (1ah/1bh/1eh) */
3443 parm = AC_PWRST_D3;
3444 set_pin_power_state(codec, 0x1a, &parm);
3445 set_pin_power_state(codec, 0x1b, &parm);
3446 set_pin_power_state(codec, 0x1e, &parm);
3447 if (imux_is_smixer)
3448 parm = AC_PWRST_D0;
3449 /* SW0 (17h), AIW0(13h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003450 update_power_state(codec, 0x17, parm);
3451 update_power_state(codec, 0x13, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003452
3453 parm = AC_PWRST_D3;
3454 set_pin_power_state(codec, 0x1e, &parm);
3455 /* PW11 (22h) */
3456 if (spec->dmic_enabled)
3457 set_pin_power_state(codec, 0x22, &parm);
3458 else
Takashi Iwai054d8672012-01-24 12:25:50 +01003459 update_power_state(codec, 0x22, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003460
3461 /* SW2(26h), AIW1(14h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003462 update_power_state(codec, 0x26, parm);
3463 update_power_state(codec, 0x14, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003464
3465 /* outputs */
3466 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3467 parm = AC_PWRST_D3;
3468 set_pin_power_state(codec, 0x19, &parm);
3469 /* Smart 5.1 PW2(1bh) */
3470 if (spec->smart51_enabled)
3471 set_pin_power_state(codec, 0x1b, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003472 update_power_state(codec, 0x18, parm);
3473 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003474
3475 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3476 parm = AC_PWRST_D3;
3477 set_pin_power_state(codec, 0x23, &parm);
3478 /* Smart 5.1 PW1(1ah) */
3479 if (spec->smart51_enabled)
3480 set_pin_power_state(codec, 0x1a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003481 update_power_state(codec, 0x27, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003482
3483 /* Smart 5.1 PW5(1eh) */
3484 if (spec->smart51_enabled)
3485 set_pin_power_state(codec, 0x1e, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003486 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003487
3488 /* Mono out */
3489 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3490 present = snd_hda_jack_detect(codec, 0x1c);
3491
3492 if (present)
3493 mono_out = 0;
3494 else {
3495 present = snd_hda_jack_detect(codec, 0x1d);
3496 if (!spec->hp_independent_mode && present)
3497 mono_out = 0;
3498 else
3499 mono_out = 1;
3500 }
3501 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
Takashi Iwai054d8672012-01-24 12:25:50 +01003502 update_power_state(codec, 0x28, parm);
3503 update_power_state(codec, 0x29, parm);
3504 update_power_state(codec, 0x2a, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003505
3506 /* PW 3/4 (1ch/1dh) */
3507 parm = AC_PWRST_D3;
3508 set_pin_power_state(codec, 0x1c, &parm);
3509 set_pin_power_state(codec, 0x1d, &parm);
3510 /* HP Independent Mode, power on AOW3 */
3511 if (spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01003512 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003513
3514 /* force to D0 for internal Speaker */
3515 /* MW0 (16h), AOW0 (10h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003516 update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
3517 update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003518}
3519
Lydia Wangf3db4232009-10-10 19:08:41 +08003520static int patch_vt1716S(struct hda_codec *codec)
3521{
3522 struct via_spec *spec;
3523 int err;
3524
3525 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003526 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003527 if (spec == NULL)
3528 return -ENOMEM;
3529
Takashi Iwai620e2b22011-06-17 17:19:19 +02003530 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003531 override_mic_boost(codec, 0x1a, 0, 3, 40);
3532 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003533
Lydia Wangf3db4232009-10-10 19:08:41 +08003534 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003535 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003536 if (err < 0) {
3537 via_free(codec);
3538 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003539 }
3540
Takashi Iwai096a8852011-06-20 12:09:02 +02003541 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003542
Lydia Wangf3db4232009-10-10 19:08:41 +08003543 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3544 spec->num_mixers++;
3545
3546 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3547
3548 codec->patch_ops = via_patch_ops;
3549
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003550 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003551 return 0;
3552}
Lydia Wang25eaba22009-10-10 19:08:43 +08003553
3554/* for vt2002P */
3555
Takashi Iwai096a8852011-06-20 12:09:02 +02003556static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003557 /* Class-D speaker related verbs */
3558 {0x1, 0xfe0, 0x4},
3559 {0x1, 0xfe9, 0x80},
3560 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003561 /* Enable Boost Volume backdoor */
3562 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003563 /* Enable AOW0 to MW9 */
3564 {0x1, 0xfb8, 0x88},
3565 { }
3566};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003567
Takashi Iwai096a8852011-06-20 12:09:02 +02003568static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003569 /* Enable Boost Volume backdoor */
3570 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003571 /* Enable AOW0 to MW9 */
3572 {0x1, 0xfb8, 0x88},
3573 { }
3574};
Lydia Wang25eaba22009-10-10 19:08:43 +08003575
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003576static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3577{
3578 struct via_spec *spec = codec->spec;
3579 int imux_is_smixer;
3580 unsigned int parm;
3581 unsigned int present;
3582 /* MUX9 (1eh) = stereo mixer */
3583 imux_is_smixer =
3584 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3585 /* inputs */
3586 /* PW 5/6/7 (29h/2ah/2bh) */
3587 parm = AC_PWRST_D3;
3588 set_pin_power_state(codec, 0x29, &parm);
3589 set_pin_power_state(codec, 0x2a, &parm);
3590 set_pin_power_state(codec, 0x2b, &parm);
3591 parm = AC_PWRST_D0;
3592 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003593 update_power_state(codec, 0x1e, parm);
3594 update_power_state(codec, 0x1f, parm);
3595 update_power_state(codec, 0x10, parm);
3596 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003597
3598 /* outputs */
3599 /* AOW0 (8h)*/
Takashi Iwai054d8672012-01-24 12:25:50 +01003600 update_power_state(codec, 0x8, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003601
Lydia Wang118909562011-03-23 17:57:34 +08003602 if (spec->codec_type == VT1802) {
3603 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3604 parm = AC_PWRST_D3;
3605 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003606 update_power_state(codec, 0x18, parm);
3607 update_power_state(codec, 0x38, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003608 } else {
3609 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3610 parm = AC_PWRST_D3;
3611 set_pin_power_state(codec, 0x26, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003612 update_power_state(codec, 0x1c, parm);
3613 update_power_state(codec, 0x37, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003614 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003615
Lydia Wang118909562011-03-23 17:57:34 +08003616 if (spec->codec_type == VT1802) {
3617 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3618 parm = AC_PWRST_D3;
3619 set_pin_power_state(codec, 0x25, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003620 update_power_state(codec, 0x15, parm);
3621 update_power_state(codec, 0x35, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003622 } else {
3623 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3624 parm = AC_PWRST_D3;
3625 set_pin_power_state(codec, 0x25, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003626 update_power_state(codec, 0x19, parm);
3627 update_power_state(codec, 0x35, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003628 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003629
3630 if (spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01003631 update_power_state(codec, 0x9, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003632
3633 /* Class-D */
3634 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3635 present = snd_hda_jack_detect(codec, 0x25);
3636
3637 parm = AC_PWRST_D3;
3638 set_pin_power_state(codec, 0x24, &parm);
3639 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003640 if (spec->codec_type == VT1802)
Takashi Iwai054d8672012-01-24 12:25:50 +01003641 update_power_state(codec, 0x14, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003642 else
Takashi Iwai054d8672012-01-24 12:25:50 +01003643 update_power_state(codec, 0x18, parm);
3644 update_power_state(codec, 0x34, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003645
3646 /* Mono Out */
3647 present = snd_hda_jack_detect(codec, 0x26);
3648
3649 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003650 if (spec->codec_type == VT1802) {
3651 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003652 update_power_state(codec, 0x33, parm);
3653 update_power_state(codec, 0x1c, parm);
3654 update_power_state(codec, 0x3c, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003655 } else {
3656 /* PW15 (31h), MW8(17h), MUX8(3bh) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003657 update_power_state(codec, 0x31, parm);
3658 update_power_state(codec, 0x17, parm);
3659 update_power_state(codec, 0x3b, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003660 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003661 /* MW9 (21h) */
3662 if (imux_is_smixer || !is_aa_path_mute(codec))
Takashi Iwai054d8672012-01-24 12:25:50 +01003663 update_power_state(codec, 0x21, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003664 else
Takashi Iwai054d8672012-01-24 12:25:50 +01003665 update_power_state(codec, 0x21, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003666}
Lydia Wang25eaba22009-10-10 19:08:43 +08003667
3668/* patch for vt2002P */
3669static int patch_vt2002P(struct hda_codec *codec)
3670{
3671 struct via_spec *spec;
3672 int err;
3673
3674 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003675 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003676 if (spec == NULL)
3677 return -ENOMEM;
3678
Takashi Iwai620e2b22011-06-17 17:19:19 +02003679 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003680 override_mic_boost(codec, 0x2b, 0, 3, 40);
3681 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003682 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003683
Lydia Wang25eaba22009-10-10 19:08:43 +08003684 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003685 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003686 if (err < 0) {
3687 via_free(codec);
3688 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003689 }
3690
Lydia Wang118909562011-03-23 17:57:34 +08003691 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003692 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003693 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003694 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003695
Lydia Wang25eaba22009-10-10 19:08:43 +08003696 codec->patch_ops = via_patch_ops;
3697
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003698 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003699 return 0;
3700}
Lydia Wangab6734e2009-10-10 19:08:46 +08003701
3702/* for vt1812 */
3703
Takashi Iwai096a8852011-06-20 12:09:02 +02003704static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003705 /* Enable Boost Volume backdoor */
3706 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003707 /* Enable AOW0 to MW9 */
3708 {0x1, 0xfb8, 0xa8},
3709 { }
3710};
3711
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003712static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3713{
3714 struct via_spec *spec = codec->spec;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003715 unsigned int parm;
3716 unsigned int present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003717 /* inputs */
3718 /* PW 5/6/7 (29h/2ah/2bh) */
3719 parm = AC_PWRST_D3;
3720 set_pin_power_state(codec, 0x29, &parm);
3721 set_pin_power_state(codec, 0x2a, &parm);
3722 set_pin_power_state(codec, 0x2b, &parm);
3723 parm = AC_PWRST_D0;
3724 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003725 update_power_state(codec, 0x1e, parm);
3726 update_power_state(codec, 0x1f, parm);
3727 update_power_state(codec, 0x10, parm);
3728 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003729
3730 /* outputs */
3731 /* AOW0 (8h)*/
Takashi Iwai054d8672012-01-24 12:25:50 +01003732 update_power_state(codec, 0x8, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003733
3734 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3735 parm = AC_PWRST_D3;
3736 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003737 update_power_state(codec, 0x18, parm);
3738 update_power_state(codec, 0x38, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003739
3740 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3741 parm = AC_PWRST_D3;
3742 set_pin_power_state(codec, 0x25, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003743 update_power_state(codec, 0x15, parm);
3744 update_power_state(codec, 0x35, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003745 if (spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01003746 update_power_state(codec, 0x9, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003747
3748 /* Internal Speaker */
3749 /* PW0 (24h), MW0(14h), MUX0(34h) */
3750 present = snd_hda_jack_detect(codec, 0x25);
3751
3752 parm = AC_PWRST_D3;
3753 set_pin_power_state(codec, 0x24, &parm);
3754 if (present) {
Takashi Iwai054d8672012-01-24 12:25:50 +01003755 update_power_state(codec, 0x14, AC_PWRST_D3);
3756 update_power_state(codec, 0x34, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003757 } else {
Takashi Iwai054d8672012-01-24 12:25:50 +01003758 update_power_state(codec, 0x14, AC_PWRST_D0);
3759 update_power_state(codec, 0x34, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003760 }
3761
3762
3763 /* Mono Out */
3764 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3765 present = snd_hda_jack_detect(codec, 0x28);
3766
3767 parm = AC_PWRST_D3;
3768 set_pin_power_state(codec, 0x31, &parm);
3769 if (present) {
Takashi Iwai054d8672012-01-24 12:25:50 +01003770 update_power_state(codec, 0x1c, AC_PWRST_D3);
3771 update_power_state(codec, 0x3c, AC_PWRST_D3);
3772 update_power_state(codec, 0x3e, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003773 } else {
Takashi Iwai054d8672012-01-24 12:25:50 +01003774 update_power_state(codec, 0x1c, AC_PWRST_D0);
3775 update_power_state(codec, 0x3c, AC_PWRST_D0);
3776 update_power_state(codec, 0x3e, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003777 }
3778
3779 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3780 parm = AC_PWRST_D3;
3781 set_pin_power_state(codec, 0x33, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003782 update_power_state(codec, 0x1d, parm);
3783 update_power_state(codec, 0x3d, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003784
3785}
Lydia Wangab6734e2009-10-10 19:08:46 +08003786
3787/* patch for vt1812 */
3788static int patch_vt1812(struct hda_codec *codec)
3789{
3790 struct via_spec *spec;
3791 int err;
3792
3793 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003794 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003795 if (spec == NULL)
3796 return -ENOMEM;
3797
Takashi Iwai620e2b22011-06-17 17:19:19 +02003798 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003799 override_mic_boost(codec, 0x2b, 0, 3, 40);
3800 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003801 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003802
Lydia Wangab6734e2009-10-10 19:08:46 +08003803 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003804 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003805 if (err < 0) {
3806 via_free(codec);
3807 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003808 }
3809
Takashi Iwai096a8852011-06-20 12:09:02 +02003810 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003811
Lydia Wangab6734e2009-10-10 19:08:46 +08003812 codec->patch_ops = via_patch_ops;
3813
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003814 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003815 return 0;
3816}
3817
Joseph Chanc577b8a2006-11-29 15:29:40 +01003818/*
3819 * patch entries
3820 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003821static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003822 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3823 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3824 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3825 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3826 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003827 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003828 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003829 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003830 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003831 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003832 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003833 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003834 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003835 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003836 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003837 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003838 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003839 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003840 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003841 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003842 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003843 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003844 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003845 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003846 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003847 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003848 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003849 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003850 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003851 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003852 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003853 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003854 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003855 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003856 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003857 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003858 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003859 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003860 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003861 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003862 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003863 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003864 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003865 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003866 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003867 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003868 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003869 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003870 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003871 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003872 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003873 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003874 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003875 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003876 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003877 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003878 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003879 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003880 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003881 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003882 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003883 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003884 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003885 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003886 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003887 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003888 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003889 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003890 { .id = 0x11060428, .name = "VT1718S",
3891 .patch = patch_vt1718S},
3892 { .id = 0x11064428, .name = "VT1718S",
3893 .patch = patch_vt1718S},
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08003894 { .id = 0x11060441, .name = "VT2020",
3895 .patch = patch_vt1718S},
3896 { .id = 0x11064441, .name = "VT1828S",
3897 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003898 { .id = 0x11060433, .name = "VT1716S",
3899 .patch = patch_vt1716S},
3900 { .id = 0x1106a721, .name = "VT1716S",
3901 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003902 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3903 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003904 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003905 { .id = 0x11060440, .name = "VT1818S",
3906 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003907 { .id = 0x11060446, .name = "VT1802",
3908 .patch = patch_vt2002P},
3909 { .id = 0x11068446, .name = "VT1802",
3910 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003911 {} /* terminator */
3912};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003913
3914MODULE_ALIAS("snd-hda-codec-id:1106*");
3915
3916static struct hda_codec_preset_list via_list = {
3917 .preset = snd_hda_preset_via,
3918 .owner = THIS_MODULE,
3919};
3920
3921MODULE_LICENSE("GPL");
3922MODULE_DESCRIPTION("VIA HD-audio codec");
3923
3924static int __init patch_via_init(void)
3925{
3926 return snd_hda_add_codec_preset(&via_list);
3927}
3928
3929static void __exit patch_via_exit(void)
3930{
3931 snd_hda_delete_codec_preset(&via_list);
3932}
3933
3934module_init(patch_via_init)
3935module_exit(patch_via_exit)