blob: dff9a00ee8fbf136101243c031d3cd3222cff291 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Paul Gortmakerda155d52011-07-15 12:38:28 -040052#include <linux/module.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010053#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080054#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010055#include "hda_codec.h"
56#include "hda_local.h"
Takashi Iwai1835a0f2011-10-27 22:12:46 +020057#include "hda_jack.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010058
Joseph Chanc577b8a2006-11-29 15:29:40 +010059/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080060#define VT1708_HP_PIN_NID 0x20
61#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010062
Harald Welted7426322008-09-15 22:43:23 +080063enum VIA_HDA_CODEC {
64 UNKNOWN = -1,
65 VT1708,
66 VT1709_10CH,
67 VT1709_6CH,
68 VT1708B_8CH,
69 VT1708B_4CH,
70 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080071 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080072 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080073 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080074 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080075 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080076 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080077 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080078 CODEC_TYPES,
79};
80
Lydia Wang118909562011-03-23 17:57:34 +080081#define VT2002P_COMPATIBLE(spec) \
82 ((spec)->codec_type == VT2002P ||\
83 (spec)->codec_type == VT1812 ||\
84 (spec)->codec_type == VT1802)
85
Takashi Iwai8e3679d2011-06-21 09:01:36 +020086#define MAX_NID_PATH_DEPTH 5
87
Takashi Iwai09a9ad692011-06-21 15:57:44 +020088/* output-path: DAC -> ... -> pin
89 * idx[] contains the source index number of the next widget;
90 * e.g. idx[0] is the index of the DAC selected by path[1] widget
91 * multi[] indicates whether it's a selector widget with multi-connectors
92 * (i.e. the connection selection is mandatory)
93 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
94 */
Takashi Iwai4a796162011-06-17 17:53:38 +020095struct nid_path {
96 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020097 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad692011-06-21 15:57:44 +020098 unsigned char idx[MAX_NID_PATH_DEPTH];
99 unsigned char multi[MAX_NID_PATH_DEPTH];
100 unsigned int vol_ctl;
101 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200102};
103
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200104/* input-path */
105struct via_input {
106 hda_nid_t pin; /* input-pin or aa-mix */
107 int adc_idx; /* ADC index to be used */
108 int mux_idx; /* MUX index (if any) */
109 const char *label; /* input-source label */
110};
111
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200112#define VIA_MAX_ADCS 3
113
Takashi Iwai3b607e32011-07-18 16:54:40 +0200114enum {
115 STREAM_MULTI_OUT = (1 << 0),
116 STREAM_INDEP_HP = (1 << 1),
117};
118
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800119struct via_spec {
120 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200121 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800122 unsigned int num_mixers;
123
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200124 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800125 unsigned int num_iverbs;
126
Takashi Iwai82673bc2011-06-17 16:24:21 +0200127 char stream_name_analog[32];
Takashi Iwai7eb56e842011-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 Wangbb3c6bfc2009-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 Iwai09a9ad692011-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 Wangd69607b32011-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 Wangd69607b32011-07-08 14:02:52 +0800482{
483 int i, num, val;
Lydia Wangd69607b32011-07-08 14:02:52 +0800484
485 if (!path)
486 return;
487 num = snd_hda_get_conn_list(codec, mix_nid, NULL);
Lydia Wangd69607b32011-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 Wangd69607b32011-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 Iwai09a9ad692011-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 Wangd69607b32011-07-08 14:02:52 +0800502 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200503 int i;
Takashi Iwai09a9ad692011-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 Iwai09a9ad692011-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 Iwai09a9ad692011-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 Wangd69607b32011-07-08 14:02:52 +0800546 hda_nid_t pin;
Takashi Iwai5d417622011-06-20 11:32:27 +0200547
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200548 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200549 return;
Takashi Iwai09a9ad692011-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);
553 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
554 if (caps & AC_AMPCAP_MUTE) {
555 unsigned int val;
556 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
557 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
558 AMP_OUT_MUTE | val);
559 }
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200560 activate_output_path(codec, path, true, true); /* force on */
Takashi Iwai5d417622011-06-20 11:32:27 +0200561}
562
Joseph Chanc577b8a2006-11-29 15:29:40 +0100563static void via_auto_init_multi_out(struct hda_codec *codec)
564{
565 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200566 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100567 int i;
568
Takashi Iwai3214b962011-07-18 12:49:25 +0200569 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) {
570 path = &spec->out_path[i];
571 if (!i && spec->aamix_mode && spec->out_mix_path.depth)
572 path = &spec->out_mix_path;
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200573 via_auto_init_output(codec, path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200574 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100575}
576
Takashi Iwai020066d2011-07-21 13:45:56 +0200577/* deactivate the inactive headphone-paths */
578static void deactivate_hp_paths(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100579{
580 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200581 int shared = spec->hp_indep_shared;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100582
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200583 if (spec->hp_independent_mode) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200584 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200585 activate_output_path(codec, &spec->hp_mix_path, false, false);
586 if (shared)
587 activate_output_path(codec, &spec->out_path[shared],
588 false, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200589 } else if (spec->aamix_mode || !spec->hp_path.depth) {
590 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200591 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200592 } else {
Takashi Iwai020066d2011-07-21 13:45:56 +0200593 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200594 activate_output_path(codec, &spec->hp_mix_path, false, false);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200595 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100596}
597
Takashi Iwai020066d2011-07-21 13:45:56 +0200598static void via_auto_init_hp_out(struct hda_codec *codec)
599{
600 struct via_spec *spec = codec->spec;
601
602 if (!spec->hp_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200603 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200604 return;
605 }
606 deactivate_hp_paths(codec);
607 if (spec->hp_independent_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200608 via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200609 else if (spec->aamix_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200610 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200611 else
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200612 via_auto_init_output(codec, &spec->hp_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200613}
614
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200615static void via_auto_init_speaker_out(struct hda_codec *codec)
616{
617 struct via_spec *spec = codec->spec;
618
Takashi Iwai3214b962011-07-18 12:49:25 +0200619 if (!spec->autocfg.speaker_outs)
620 return;
621 if (!spec->speaker_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200622 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200623 return;
624 }
625 if (!spec->aamix_mode) {
626 activate_output_path(codec, &spec->speaker_mix_path,
627 false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200628 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200629 } else {
630 activate_output_path(codec, &spec->speaker_path, false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200631 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200632 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200633}
634
Takashi Iwaif4a78282011-06-17 18:46:48 +0200635static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200636static void via_hp_automute(struct hda_codec *codec);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200637
Joseph Chanc577b8a2006-11-29 15:29:40 +0100638static void via_auto_init_analog_input(struct hda_codec *codec)
639{
640 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200641 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200642 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200643 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200644 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100645
Takashi Iwai096a8852011-06-20 12:09:02 +0200646 /* init ADCs */
647 for (i = 0; i < spec->num_adc_nids; i++) {
648 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
649 AC_VERB_SET_AMP_GAIN_MUTE,
650 AMP_IN_UNMUTE(0));
651 }
652
653 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200654 for (i = 0; i < cfg->num_inputs; i++) {
655 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200656 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200657 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100658 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200659 ctl = PIN_VREF50;
660 else
661 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100662 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200663 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100664 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200665
666 /* init input-src */
667 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200668 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
Takashi Iwaifc1156c2012-02-13 15:04:06 +0100669 /* secondary ADCs must have the unique MUX */
670 if (i > 0 && !spec->mux_nids[i])
671 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200672 if (spec->mux_nids[adc_idx]) {
673 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
674 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
675 AC_VERB_SET_CONNECT_SEL,
676 mux_idx);
677 }
678 if (spec->dyn_adc_switch)
679 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200680 }
681
682 /* init aa-mixer */
683 if (!spec->aa_mix_nid)
684 return;
685 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
686 ARRAY_SIZE(conn));
687 for (i = 0; i < num_conns; i++) {
688 unsigned int caps = get_wcaps(codec, conn[i]);
689 if (get_wcaps_type(caps) == AC_WID_PIN)
690 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
691 AC_VERB_SET_AMP_GAIN_MUTE,
692 AMP_IN_MUTE(i));
693 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100694}
Lydia Wangf5271102009-10-10 19:07:35 +0800695
Takashi Iwai054d8672012-01-24 12:25:50 +0100696static void update_power_state(struct hda_codec *codec, hda_nid_t nid,
697 unsigned int parm)
698{
699 if (snd_hda_codec_read(codec, nid, 0,
700 AC_VERB_GET_POWER_STATE, 0) == parm)
701 return;
702 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
703}
704
Lydia Wangf5271102009-10-10 19:07:35 +0800705static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
706 unsigned int *affected_parm)
707{
708 unsigned parm;
709 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
710 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
711 >> AC_DEFCFG_MISC_SHIFT
712 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800713 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200714 unsigned present = 0;
715
716 no_presence |= spec->no_pin_power_ctl;
717 if (!no_presence)
718 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200719 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800720 || ((no_presence || present)
721 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800722 *affected_parm = AC_PWRST_D0; /* if it's connected */
723 parm = AC_PWRST_D0;
724 } else
725 parm = AC_PWRST_D3;
726
Takashi Iwai054d8672012-01-24 12:25:50 +0100727 update_power_state(codec, nid, parm);
Lydia Wangf5271102009-10-10 19:07:35 +0800728}
729
Takashi Iwai24088a52011-06-17 16:59:21 +0200730static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
731 struct snd_ctl_elem_info *uinfo)
732{
733 static const char * const texts[] = {
734 "Disabled", "Enabled"
735 };
736
737 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
738 uinfo->count = 1;
739 uinfo->value.enumerated.items = 2;
740 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
741 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
742 strcpy(uinfo->value.enumerated.name,
743 texts[uinfo->value.enumerated.item]);
744 return 0;
745}
746
747static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
748 struct snd_ctl_elem_value *ucontrol)
749{
750 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
751 struct via_spec *spec = codec->spec;
752 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
753 return 0;
754}
755
756static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
757 struct snd_ctl_elem_value *ucontrol)
758{
759 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
760 struct via_spec *spec = codec->spec;
761 unsigned int val = !ucontrol->value.enumerated.item[0];
762
763 if (val == spec->no_pin_power_ctl)
764 return 0;
765 spec->no_pin_power_ctl = val;
766 set_widgets_power_state(codec);
Takashi Iwaie9d010c2012-02-01 10:33:23 +0100767 analog_low_current_mode(codec);
Takashi Iwai24088a52011-06-17 16:59:21 +0200768 return 1;
769}
770
771static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
772 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
773 .name = "Dynamic Power-Control",
774 .info = via_pin_power_ctl_info,
775 .get = via_pin_power_ctl_get,
776 .put = via_pin_power_ctl_put,
777};
778
779
Harald Welte0aa62ae2008-09-09 15:58:27 +0800780static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
781 struct snd_ctl_elem_info *uinfo)
782{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200783 static const char * const texts[] = { "OFF", "ON" };
784
785 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
786 uinfo->count = 1;
787 uinfo->value.enumerated.items = 2;
788 if (uinfo->value.enumerated.item >= 2)
789 uinfo->value.enumerated.item = 1;
790 strcpy(uinfo->value.enumerated.name,
791 texts[uinfo->value.enumerated.item]);
792 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800793}
794
795static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
796 struct snd_ctl_elem_value *ucontrol)
797{
798 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800799 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800800
Takashi Iwaiece8d042011-06-19 16:24:21 +0200801 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800802 return 0;
803}
804
Takashi Iwai3b607e32011-07-18 16:54:40 +0200805/* adjust spec->multiout setup according to the current flags */
806static void setup_playback_multi_pcm(struct via_spec *spec)
807{
808 const struct auto_pin_cfg *cfg = &spec->autocfg;
809 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
810 spec->multiout.hp_nid = 0;
811 if (!spec->hp_independent_mode) {
812 if (!spec->hp_indep_shared)
813 spec->multiout.hp_nid = spec->hp_dac_nid;
814 } else {
815 if (spec->hp_indep_shared)
816 spec->multiout.num_dacs = cfg->line_outs - 1;
817 }
818}
819
820/* update DAC setups according to indep-HP switch;
821 * this function is called only when indep-HP is modified
822 */
823static void switch_indep_hp_dacs(struct hda_codec *codec)
824{
825 struct via_spec *spec = codec->spec;
826 int shared = spec->hp_indep_shared;
827 hda_nid_t shared_dac, hp_dac;
828
829 if (!spec->opened_streams)
830 return;
831
832 shared_dac = shared ? spec->multiout.dac_nids[shared] : 0;
833 hp_dac = spec->hp_dac_nid;
834 if (spec->hp_independent_mode) {
835 /* switch to indep-HP mode */
836 if (spec->active_streams & STREAM_MULTI_OUT) {
837 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
838 __snd_hda_codec_cleanup_stream(codec, shared_dac, 1);
839 }
840 if (spec->active_streams & STREAM_INDEP_HP)
841 snd_hda_codec_setup_stream(codec, hp_dac,
842 spec->cur_hp_stream_tag, 0,
843 spec->cur_hp_format);
844 } else {
845 /* back to HP or shared-DAC */
846 if (spec->active_streams & STREAM_INDEP_HP)
847 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
848 if (spec->active_streams & STREAM_MULTI_OUT) {
849 hda_nid_t dac;
850 int ch;
851 if (shared_dac) { /* reset mutli-ch DAC */
852 dac = shared_dac;
853 ch = shared * 2;
854 } else { /* reset HP DAC */
855 dac = hp_dac;
856 ch = 0;
857 }
858 snd_hda_codec_setup_stream(codec, dac,
859 spec->cur_dac_stream_tag, ch,
860 spec->cur_dac_format);
861 }
862 }
863 setup_playback_multi_pcm(spec);
864}
865
Harald Welte0aa62ae2008-09-09 15:58:27 +0800866static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
867 struct snd_ctl_elem_value *ucontrol)
868{
869 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
870 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200871 int cur, shared;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200872
Takashi Iwai3b607e32011-07-18 16:54:40 +0200873 mutex_lock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200874 cur = !!ucontrol->value.enumerated.item[0];
Takashi Iwai3b607e32011-07-18 16:54:40 +0200875 if (spec->hp_independent_mode == cur) {
876 mutex_unlock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200877 return 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200878 }
Takashi Iwai25250502011-06-30 17:24:47 +0200879 spec->hp_independent_mode = cur;
Takashi Iwai3214b962011-07-18 12:49:25 +0200880 shared = spec->hp_indep_shared;
Takashi Iwai020066d2011-07-21 13:45:56 +0200881 deactivate_hp_paths(codec);
882 if (cur)
883 activate_output_path(codec, &spec->hp_indep_path, true, false);
884 else {
Takashi Iwai3214b962011-07-18 12:49:25 +0200885 if (shared)
886 activate_output_path(codec, &spec->out_path[shared],
Takashi Iwai25250502011-06-30 17:24:47 +0200887 true, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200888 if (spec->aamix_mode || !spec->hp_path.depth)
889 activate_output_path(codec, &spec->hp_mix_path,
890 true, false);
891 else
892 activate_output_path(codec, &spec->hp_path,
893 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200894 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800895
Takashi Iwai3b607e32011-07-18 16:54:40 +0200896 switch_indep_hp_dacs(codec);
897 mutex_unlock(&spec->config_mutex);
898
Lydia Wangce0e5a92011-03-22 16:22:37 +0800899 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800900 set_widgets_power_state(codec);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200901 via_hp_automute(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200902 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800903}
904
Takashi Iwaiece8d042011-06-19 16:24:21 +0200905static const struct snd_kcontrol_new via_hp_mixer = {
906 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
907 .name = "Independent HP",
908 .info = via_independent_hp_info,
909 .get = via_independent_hp_get,
910 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800911};
912
Takashi Iwai3d83e572010-04-14 14:36:23 +0200913static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100914{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200915 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100916 struct snd_kcontrol_new *knew;
917 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100918
Takashi Iwaiece8d042011-06-19 16:24:21 +0200919 nid = spec->autocfg.hp_pins[0];
920 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200921 if (knew == NULL)
922 return -ENOMEM;
923
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100924 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100925
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100926 return 0;
927}
928
Lydia Wang1564b282009-10-10 19:07:52 +0800929static void notify_aa_path_ctls(struct hda_codec *codec)
930{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200931 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800932 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800933
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200934 for (i = 0; i < spec->smart51_nums; i++) {
935 struct snd_kcontrol *ctl;
936 struct snd_ctl_elem_id id;
937 memset(&id, 0, sizeof(id));
938 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
939 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800940 ctl = snd_hda_find_mixer_ctl(codec, id.name);
941 if (ctl)
942 snd_ctl_notify(codec->bus->card,
943 SNDRV_CTL_EVENT_MASK_VALUE,
944 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800945 }
946}
947
948static void mute_aa_path(struct hda_codec *codec, int mute)
949{
950 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200951 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800952 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200953
Lydia Wang1564b282009-10-10 19:07:52 +0800954 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200955 for (i = 0; i < spec->smart51_nums; i++) {
956 if (spec->smart51_idxs[i] < 0)
957 continue;
958 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
959 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800960 HDA_AMP_MUTE, val);
961 }
962}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200963
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200964static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
965{
966 struct via_spec *spec = codec->spec;
967 int i;
968
969 for (i = 0; i < spec->smart51_nums; i++)
970 if (spec->smart51_pins[i] == pin)
971 return true;
972 return false;
973}
974
Lydia Wang1564b282009-10-10 19:07:52 +0800975static int via_smart51_get(struct snd_kcontrol *kcontrol,
976 struct snd_ctl_elem_value *ucontrol)
977{
978 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
979 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800980
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200981 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800982 return 0;
983}
984
985static int via_smart51_put(struct snd_kcontrol *kcontrol,
986 struct snd_ctl_elem_value *ucontrol)
987{
988 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
989 struct via_spec *spec = codec->spec;
990 int out_in = *ucontrol->value.integer.value
991 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800992 int i;
993
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200994 for (i = 0; i < spec->smart51_nums; i++) {
995 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200996 unsigned int parm;
997
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200998 parm = snd_hda_codec_read(codec, nid, 0,
999 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1000 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
1001 parm |= out_in;
1002 snd_hda_codec_write(codec, nid, 0,
1003 AC_VERB_SET_PIN_WIDGET_CONTROL,
1004 parm);
1005 if (out_in == AC_PINCTL_OUT_EN) {
1006 mute_aa_path(codec, 1);
1007 notify_aa_path_ctls(codec);
1008 }
Lydia Wang1564b282009-10-10 19:07:52 +08001009 }
1010 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001011 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +08001012 return 1;
1013}
1014
Takashi Iwai5f4b36d2011-06-17 14:55:02 +02001015static const struct snd_kcontrol_new via_smart51_mixer = {
1016 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1017 .name = "Smart 5.1",
1018 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001019 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +02001020 .get = via_smart51_get,
1021 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +08001022};
1023
Takashi Iwaif4a78282011-06-17 18:46:48 +02001024static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001025{
Takashi Iwaif4a78282011-06-17 18:46:48 +02001026 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001027
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001028 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +08001029 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001030 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001031 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001032 return 0;
1033}
1034
Takashi Iwaiada509e2011-06-20 15:40:19 +02001035/* check AA path's mute status */
1036static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001037{
Lydia Wangf5271102009-10-10 19:07:35 +08001038 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001039 const struct hda_amp_list *p;
1040 int i, ch, v;
1041
1042 for (i = 0; i < spec->num_loopbacks; i++) {
1043 p = &spec->loopback_list[i];
1044 for (ch = 0; ch < 2; ch++) {
1045 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
1046 p->idx);
1047 if (!(v & HDA_AMP_MUTE) && v > 0)
1048 return false;
Lydia Wangf5271102009-10-10 19:07:35 +08001049 }
1050 }
Takashi Iwaiada509e2011-06-20 15:40:19 +02001051 return true;
Lydia Wangf5271102009-10-10 19:07:35 +08001052}
1053
1054/* enter/exit analog low-current mode */
Takashi Iwaie9d010c2012-02-01 10:33:23 +01001055static void __analog_low_current_mode(struct hda_codec *codec, bool force)
Lydia Wangf5271102009-10-10 19:07:35 +08001056{
1057 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001058 bool enable;
1059 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +08001060
Takashi Iwaie9d010c2012-02-01 10:33:23 +01001061 if (spec->no_pin_power_ctl)
1062 enable = false;
1063 else
1064 enable = is_aa_path_mute(codec) && !spec->opened_streams;
1065 if (enable == spec->alc_mode && !force)
1066 return;
1067 spec->alc_mode = enable;
Lydia Wangf5271102009-10-10 19:07:35 +08001068
1069 /* decide low current mode's verb & parameter */
1070 switch (spec->codec_type) {
1071 case VT1708B_8CH:
1072 case VT1708B_4CH:
1073 verb = 0xf70;
1074 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1075 break;
1076 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001077 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001078 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001079 verb = 0xf73;
1080 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1081 break;
1082 case VT1702:
1083 verb = 0xf73;
1084 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1085 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001086 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001087 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +08001088 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +08001089 verb = 0xf93;
1090 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1091 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001092 default:
1093 return; /* other codecs are not supported */
1094 }
1095 /* send verb */
1096 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1097}
1098
Takashi Iwaie9d010c2012-02-01 10:33:23 +01001099static void analog_low_current_mode(struct hda_codec *codec)
1100{
1101 return __analog_low_current_mode(codec, false);
1102}
1103
Joseph Chanc577b8a2006-11-29 15:29:40 +01001104/*
1105 * generic initialization of ADC, input mixers and output mixers
1106 */
Takashi Iwai096a8852011-06-20 12:09:02 +02001107static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001108 /* power down jack detect function */
1109 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001110 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001111};
1112
Takashi Iwai3b607e32011-07-18 16:54:40 +02001113static void set_stream_open(struct hda_codec *codec, int bit, bool active)
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001114{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001115 struct via_spec *spec = codec->spec;
1116
1117 if (active)
Takashi Iwai3b607e32011-07-18 16:54:40 +02001118 spec->opened_streams |= bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001119 else
Takashi Iwai3b607e32011-07-18 16:54:40 +02001120 spec->opened_streams &= ~bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001121 analog_low_current_mode(codec);
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001122}
1123
Takashi Iwaiece8d042011-06-19 16:24:21 +02001124static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001125 struct hda_codec *codec,
1126 struct snd_pcm_substream *substream)
1127{
1128 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001129 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001130 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001131
Takashi Iwai25250502011-06-30 17:24:47 +02001132 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
Takashi Iwai25250502011-06-30 17:24:47 +02001133 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001134 set_stream_open(codec, STREAM_MULTI_OUT, true);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001135 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1136 hinfo);
1137 if (err < 0) {
Takashi Iwai3b607e32011-07-18 16:54:40 +02001138 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001139 return err;
1140 }
1141 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001142}
1143
Takashi Iwaiece8d042011-06-19 16:24:21 +02001144static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001145 struct hda_codec *codec,
1146 struct snd_pcm_substream *substream)
1147{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001148 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001149 return 0;
1150}
1151
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001152static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1153 struct hda_codec *codec,
1154 struct snd_pcm_substream *substream)
1155{
1156 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001157
Takashi Iwaiece8d042011-06-19 16:24:21 +02001158 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001159 return -EINVAL;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001160 set_stream_open(codec, STREAM_INDEP_HP, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001161 return 0;
1162}
1163
1164static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1165 struct hda_codec *codec,
1166 struct snd_pcm_substream *substream)
1167{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001168 set_stream_open(codec, STREAM_INDEP_HP, false);
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001169 return 0;
1170}
1171
1172static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1173 struct hda_codec *codec,
1174 unsigned int stream_tag,
1175 unsigned int format,
1176 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001177{
1178 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001179
Takashi Iwai3b607e32011-07-18 16:54:40 +02001180 mutex_lock(&spec->config_mutex);
1181 setup_playback_multi_pcm(spec);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001182 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1183 format, substream);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001184 /* remember for dynamic DAC switch with indep-HP */
1185 spec->active_streams |= STREAM_MULTI_OUT;
1186 spec->cur_dac_stream_tag = stream_tag;
1187 spec->cur_dac_format = format;
1188 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001189 vt1708_update_hp_work(spec);
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001190 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001191}
1192
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001193static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1194 struct hda_codec *codec,
1195 unsigned int stream_tag,
1196 unsigned int format,
1197 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001198{
1199 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001200
Takashi Iwai3b607e32011-07-18 16:54:40 +02001201 mutex_lock(&spec->config_mutex);
1202 if (spec->hp_independent_mode)
1203 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1204 stream_tag, 0, format);
1205 spec->active_streams |= STREAM_INDEP_HP;
1206 spec->cur_hp_stream_tag = stream_tag;
1207 spec->cur_hp_format = format;
1208 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001209 vt1708_update_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001210 return 0;
1211}
1212
1213static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1214 struct hda_codec *codec,
1215 struct snd_pcm_substream *substream)
1216{
1217 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001218
Takashi Iwai3b607e32011-07-18 16:54:40 +02001219 mutex_lock(&spec->config_mutex);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001220 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001221 spec->active_streams &= ~STREAM_MULTI_OUT;
1222 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001223 vt1708_update_hp_work(spec);
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001224 return 0;
1225}
1226
1227static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1228 struct hda_codec *codec,
1229 struct snd_pcm_substream *substream)
1230{
1231 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001232
Takashi Iwai3b607e32011-07-18 16:54:40 +02001233 mutex_lock(&spec->config_mutex);
1234 if (spec->hp_independent_mode)
1235 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
1236 spec->active_streams &= ~STREAM_INDEP_HP;
1237 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001238 vt1708_update_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001239 return 0;
1240}
1241
Joseph Chanc577b8a2006-11-29 15:29:40 +01001242/*
1243 * Digital out
1244 */
1245static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1246 struct hda_codec *codec,
1247 struct snd_pcm_substream *substream)
1248{
1249 struct via_spec *spec = codec->spec;
1250 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1251}
1252
1253static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1254 struct hda_codec *codec,
1255 struct snd_pcm_substream *substream)
1256{
1257 struct via_spec *spec = codec->spec;
1258 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1259}
1260
Harald Welte5691ec72008-09-15 22:42:26 +08001261static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001262 struct hda_codec *codec,
1263 unsigned int stream_tag,
1264 unsigned int format,
1265 struct snd_pcm_substream *substream)
1266{
1267 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001268 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1269 stream_tag, format, substream);
1270}
Harald Welte5691ec72008-09-15 22:42:26 +08001271
Takashi Iwai9da29272009-05-07 16:31:14 +02001272static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1273 struct hda_codec *codec,
1274 struct snd_pcm_substream *substream)
1275{
1276 struct via_spec *spec = codec->spec;
1277 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001278 return 0;
1279}
1280
Joseph Chanc577b8a2006-11-29 15:29:40 +01001281/*
1282 * Analog capture
1283 */
1284static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1285 struct hda_codec *codec,
1286 unsigned int stream_tag,
1287 unsigned int format,
1288 struct snd_pcm_substream *substream)
1289{
1290 struct via_spec *spec = codec->spec;
1291
1292 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1293 stream_tag, 0, format);
1294 return 0;
1295}
1296
1297static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1298 struct hda_codec *codec,
1299 struct snd_pcm_substream *substream)
1300{
1301 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001302 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001303 return 0;
1304}
1305
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001306/* analog capture with dynamic ADC switching */
1307static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1308 struct hda_codec *codec,
1309 unsigned int stream_tag,
1310 unsigned int format,
1311 struct snd_pcm_substream *substream)
1312{
1313 struct via_spec *spec = codec->spec;
1314 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1315
Takashi Iwai3b607e32011-07-18 16:54:40 +02001316 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001317 spec->cur_adc = spec->adc_nids[adc_idx];
1318 spec->cur_adc_stream_tag = stream_tag;
1319 spec->cur_adc_format = format;
1320 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001321 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001322 return 0;
1323}
1324
1325static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1326 struct hda_codec *codec,
1327 struct snd_pcm_substream *substream)
1328{
1329 struct via_spec *spec = codec->spec;
1330
Takashi Iwai3b607e32011-07-18 16:54:40 +02001331 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001332 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1333 spec->cur_adc = 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001334 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001335 return 0;
1336}
1337
1338/* re-setup the stream if running; called from input-src put */
1339static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1340{
1341 struct via_spec *spec = codec->spec;
1342 int adc_idx = spec->inputs[cur].adc_idx;
1343 hda_nid_t adc = spec->adc_nids[adc_idx];
Takashi Iwai3b607e32011-07-18 16:54:40 +02001344 bool ret = false;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001345
Takashi Iwai3b607e32011-07-18 16:54:40 +02001346 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001347 if (spec->cur_adc && spec->cur_adc != adc) {
1348 /* stream is running, let's swap the current ADC */
1349 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1350 spec->cur_adc = adc;
1351 snd_hda_codec_setup_stream(codec, adc,
1352 spec->cur_adc_stream_tag, 0,
1353 spec->cur_adc_format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001354 ret = true;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001355 }
Takashi Iwai3b607e32011-07-18 16:54:40 +02001356 mutex_unlock(&spec->config_mutex);
1357 return ret;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001358}
1359
Takashi Iwai9af74212011-06-18 16:17:45 +02001360static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001361 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001362 .channels_min = 2,
1363 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001364 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001365 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001366 .open = via_playback_multi_pcm_open,
1367 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001368 .prepare = via_playback_multi_pcm_prepare,
1369 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001370 },
1371};
1372
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001373static const struct hda_pcm_stream via_pcm_hp_playback = {
1374 .substreams = 1,
1375 .channels_min = 2,
1376 .channels_max = 2,
1377 /* NID is set in via_build_pcms */
1378 .ops = {
1379 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001380 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001381 .prepare = via_playback_hp_pcm_prepare,
1382 .cleanup = via_playback_hp_pcm_cleanup
1383 },
1384};
1385
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001386static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001387 .substreams = 1,
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001388 .channels_min = 2,
1389 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001390 /* NID is set in via_build_pcms */
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001391 /* We got noisy outputs on the right channel on VT1708 when
1392 * 24bit samples are used. Until any workaround is found,
1393 * disable the 24bit format, so far.
1394 */
1395 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1396 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001397 .open = via_playback_multi_pcm_open,
1398 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001399 .prepare = via_playback_multi_pcm_prepare,
1400 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001401 },
1402};
1403
Takashi Iwai9af74212011-06-18 16:17:45 +02001404static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001405 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001406 .channels_min = 2,
1407 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001408 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001409 .ops = {
1410 .prepare = via_capture_pcm_prepare,
1411 .cleanup = via_capture_pcm_cleanup
1412 },
1413};
1414
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001415static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1416 .substreams = 1,
1417 .channels_min = 2,
1418 .channels_max = 2,
1419 /* NID is set in via_build_pcms */
1420 .ops = {
1421 .prepare = via_dyn_adc_capture_pcm_prepare,
1422 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1423 },
1424};
1425
Takashi Iwai9af74212011-06-18 16:17:45 +02001426static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001427 .substreams = 1,
1428 .channels_min = 2,
1429 .channels_max = 2,
1430 /* NID is set in via_build_pcms */
1431 .ops = {
1432 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001433 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001434 .prepare = via_dig_playback_pcm_prepare,
1435 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001436 },
1437};
1438
Takashi Iwai9af74212011-06-18 16:17:45 +02001439static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001440 .substreams = 1,
1441 .channels_min = 2,
1442 .channels_max = 2,
1443};
1444
Takashi Iwai370bafb2011-06-20 12:47:45 +02001445/*
1446 * slave controls for virtual master
1447 */
1448static const char * const via_slave_vols[] = {
1449 "Front Playback Volume",
1450 "Surround Playback Volume",
1451 "Center Playback Volume",
1452 "LFE Playback Volume",
1453 "Side Playback Volume",
1454 "Headphone Playback Volume",
1455 "Speaker Playback Volume",
1456 NULL,
1457};
1458
1459static const char * const via_slave_sws[] = {
1460 "Front Playback Switch",
1461 "Surround Playback Switch",
1462 "Center Playback Switch",
1463 "LFE Playback Switch",
1464 "Side Playback Switch",
1465 "Headphone Playback Switch",
1466 "Speaker Playback Switch",
1467 NULL,
1468};
1469
Joseph Chanc577b8a2006-11-29 15:29:40 +01001470static int via_build_controls(struct hda_codec *codec)
1471{
1472 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001473 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001474 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001475
Takashi Iwaib5bcc182012-02-02 10:30:17 +01001476 spec->no_pin_power_ctl = 1;
Takashi Iwai24088a52011-06-17 16:59:21 +02001477 if (spec->set_widgets_power_state)
1478 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1479 return -ENOMEM;
1480
Joseph Chanc577b8a2006-11-29 15:29:40 +01001481 for (i = 0; i < spec->num_mixers; i++) {
1482 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1483 if (err < 0)
1484 return err;
1485 }
1486
1487 if (spec->multiout.dig_out_nid) {
1488 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001489 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001490 spec->multiout.dig_out_nid);
1491 if (err < 0)
1492 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001493 err = snd_hda_create_spdif_share_sw(codec,
1494 &spec->multiout);
1495 if (err < 0)
1496 return err;
1497 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001498 }
1499 if (spec->dig_in_nid) {
1500 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1501 if (err < 0)
1502 return err;
1503 }
Lydia Wang17314372009-10-10 19:07:37 +08001504
Takashi Iwai370bafb2011-06-20 12:47:45 +02001505 /* if we have no master control, let's create it */
1506 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1507 unsigned int vmaster_tlv[4];
1508 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1509 HDA_OUTPUT, vmaster_tlv);
1510 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1511 vmaster_tlv, via_slave_vols);
1512 if (err < 0)
1513 return err;
1514 }
1515 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1516 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1517 NULL, via_slave_sws);
1518 if (err < 0)
1519 return err;
1520 }
1521
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001522 /* assign Capture Source enums to NID */
1523 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1524 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001525 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001526 if (err < 0)
1527 return err;
1528 }
1529
Takashi Iwai603c4012008-07-30 15:01:44 +02001530 via_free_kctls(codec); /* no longer needed */
Takashi Iwai01a61e12011-10-28 00:03:22 +02001531
1532 err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
1533 if (err < 0)
1534 return err;
1535
Joseph Chanc577b8a2006-11-29 15:29:40 +01001536 return 0;
1537}
1538
1539static int via_build_pcms(struct hda_codec *codec)
1540{
1541 struct via_spec *spec = codec->spec;
1542 struct hda_pcm *info = spec->pcm_rec;
1543
Takashi Iwaia5973102011-09-28 16:43:36 +02001544 codec->num_pcms = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001545 codec->pcm_info = info;
1546
Takashi Iwaia5973102011-09-28 16:43:36 +02001547 if (spec->multiout.num_dacs || spec->num_adc_nids) {
1548 snprintf(spec->stream_name_analog,
1549 sizeof(spec->stream_name_analog),
1550 "%s Analog", codec->chip_name);
1551 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001552
Takashi Iwaia5973102011-09-28 16:43:36 +02001553 if (spec->multiout.num_dacs) {
1554 if (!spec->stream_analog_playback)
1555 spec->stream_analog_playback =
1556 &via_pcm_analog_playback;
1557 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1558 *spec->stream_analog_playback;
1559 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1560 spec->multiout.dac_nids[0];
1561 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1562 spec->multiout.max_channels;
1563 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001564
Takashi Iwaia5973102011-09-28 16:43:36 +02001565 if (!spec->stream_analog_capture) {
1566 if (spec->dyn_adc_switch)
1567 spec->stream_analog_capture =
1568 &via_pcm_dyn_adc_analog_capture;
1569 else
1570 spec->stream_analog_capture =
1571 &via_pcm_analog_capture;
1572 }
1573 if (spec->num_adc_nids) {
1574 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1575 *spec->stream_analog_capture;
1576 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1577 spec->adc_nids[0];
1578 if (!spec->dyn_adc_switch)
1579 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1580 spec->num_adc_nids;
1581 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001582 codec->num_pcms++;
1583 info++;
Takashi Iwaia5973102011-09-28 16:43:36 +02001584 }
1585
1586 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
Takashi Iwai82673bc2011-06-17 16:24:21 +02001587 snprintf(spec->stream_name_digital,
1588 sizeof(spec->stream_name_digital),
1589 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001590 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001591 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001592 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001593 if (!spec->stream_digital_playback)
1594 spec->stream_digital_playback =
1595 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001596 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001597 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001598 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1599 spec->multiout.dig_out_nid;
1600 }
1601 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001602 if (!spec->stream_digital_capture)
1603 spec->stream_digital_capture =
1604 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001605 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001606 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001607 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1608 spec->dig_in_nid;
1609 }
Takashi Iwaia5973102011-09-28 16:43:36 +02001610 codec->num_pcms++;
1611 info++;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001612 }
1613
Takashi Iwaiece8d042011-06-19 16:24:21 +02001614 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001615 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1616 "%s HP", codec->chip_name);
1617 info->name = spec->stream_name_hp;
1618 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1619 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001620 spec->hp_dac_nid;
Takashi Iwaia5973102011-09-28 16:43:36 +02001621 codec->num_pcms++;
1622 info++;
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001623 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001624 return 0;
1625}
1626
1627static void via_free(struct hda_codec *codec)
1628{
1629 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001630
1631 if (!spec)
1632 return;
1633
Takashi Iwai603c4012008-07-30 15:01:44 +02001634 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001635 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001636 kfree(spec->bind_cap_vol);
1637 kfree(spec->bind_cap_sw);
1638 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001639}
1640
Takashi Iwai64be2852011-06-17 16:51:39 +02001641/* mute/unmute outputs */
1642static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1643 hda_nid_t *pins, bool mute)
1644{
1645 int i;
Takashi Iwai94994732011-07-11 11:36:44 +02001646 for (i = 0; i < num_pins; i++) {
1647 unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
1648 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1649 if (parm & AC_PINCTL_IN_EN)
1650 continue;
1651 if (mute)
1652 parm &= ~AC_PINCTL_OUT_EN;
1653 else
1654 parm |= AC_PINCTL_OUT_EN;
Takashi Iwai64be2852011-06-17 16:51:39 +02001655 snd_hda_codec_write(codec, pins[i], 0,
Takashi Iwai94994732011-07-11 11:36:44 +02001656 AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
1657 }
Takashi Iwai64be2852011-06-17 16:51:39 +02001658}
1659
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001660/* mute internal speaker if line-out is plugged */
1661static void via_line_automute(struct hda_codec *codec, int present)
1662{
1663 struct via_spec *spec = codec->spec;
1664
1665 if (!spec->autocfg.speaker_outs)
1666 return;
1667 if (!present)
1668 present = snd_hda_jack_detect(codec,
1669 spec->autocfg.line_out_pins[0]);
1670 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1671 spec->autocfg.speaker_pins,
1672 present);
1673}
1674
Harald Welte69e52a82008-09-09 15:57:32 +08001675/* mute internal speaker if HP is plugged */
1676static void via_hp_automute(struct hda_codec *codec)
1677{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001678 int present = 0;
Takashi Iwai6e969d92011-07-11 11:28:13 +02001679 int nums;
Harald Welte69e52a82008-09-09 15:57:32 +08001680 struct via_spec *spec = codec->spec;
1681
Takashi Iwai187d3332011-11-24 16:33:09 +01001682 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0] &&
1683 (spec->codec_type != VT1708 || spec->vt1708_jack_detect))
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001684 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai6e969d92011-07-11 11:28:13 +02001685
1686 if (spec->smart51_enabled)
1687 nums = spec->autocfg.line_outs + spec->smart51_nums;
1688 else
1689 nums = spec->autocfg.line_outs;
1690 toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
1691
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001692 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001693}
1694
Harald Welte69e52a82008-09-09 15:57:32 +08001695static void via_gpio_control(struct hda_codec *codec)
1696{
1697 unsigned int gpio_data;
1698 unsigned int vol_counter;
1699 unsigned int vol;
1700 unsigned int master_vol;
1701
1702 struct via_spec *spec = codec->spec;
1703
1704 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1705 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1706
1707 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1708 0xF84, 0) & 0x3F0000) >> 16;
1709
1710 vol = vol_counter & 0x1F;
1711 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1712 AC_VERB_GET_AMP_GAIN_MUTE,
1713 AC_AMP_GET_INPUT);
1714
1715 if (gpio_data == 0x02) {
1716 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001717 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1718 AC_VERB_SET_PIN_WIDGET_CONTROL,
1719 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001720 if (vol_counter & 0x20) {
1721 /* decrease volume */
1722 if (vol > master_vol)
1723 vol = master_vol;
1724 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1725 0, HDA_AMP_VOLMASK,
1726 master_vol-vol);
1727 } else {
1728 /* increase volume */
1729 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1730 HDA_AMP_VOLMASK,
1731 ((master_vol+vol) > 0x2A) ? 0x2A :
1732 (master_vol+vol));
1733 }
1734 } else if (!(gpio_data & 0x02)) {
1735 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001736 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1737 AC_VERB_SET_PIN_WIDGET_CONTROL,
1738 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001739 }
1740}
1741
1742/* unsolicited event for jack sensing */
1743static void via_unsol_event(struct hda_codec *codec,
1744 unsigned int res)
1745{
1746 res >>= 26;
Takashi Iwai3a938972011-10-28 01:16:55 +02001747 res = snd_hda_jack_get_action(codec, res);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001748
Lydia Wanga34df192009-10-10 19:08:01 +08001749 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001750 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001751
1752 res &= ~VIA_JACK_EVENT;
1753
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001754 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001755 via_hp_automute(codec);
1756 else if (res == VIA_GPIO_EVENT)
1757 via_gpio_control(codec);
Takashi Iwai01a61e12011-10-28 00:03:22 +02001758 snd_hda_jack_report_sync(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001759}
1760
Takashi Iwai2a439522011-07-26 09:52:50 +02001761#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001762static int via_suspend(struct hda_codec *codec, pm_message_t state)
1763{
1764 struct via_spec *spec = codec->spec;
1765 vt1708_stop_hp_work(spec);
1766 return 0;
1767}
1768#endif
1769
Takashi Iwaicb53c622007-08-10 17:21:45 +02001770#ifdef CONFIG_SND_HDA_POWER_SAVE
1771static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1772{
1773 struct via_spec *spec = codec->spec;
1774 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1775}
1776#endif
1777
Joseph Chanc577b8a2006-11-29 15:29:40 +01001778/*
1779 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001780
1781static int via_init(struct hda_codec *codec);
1782
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001783static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001784 .build_controls = via_build_controls,
1785 .build_pcms = via_build_pcms,
1786 .init = via_init,
1787 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001788 .unsol_event = via_unsol_event,
Takashi Iwai2a439522011-07-26 09:52:50 +02001789#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001790 .suspend = via_suspend,
1791#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001792#ifdef CONFIG_SND_HDA_POWER_SAVE
1793 .check_power_status = via_check_power_status,
1794#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001795};
1796
Takashi Iwai4a796162011-06-17 17:53:38 +02001797static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001798{
Takashi Iwai4a796162011-06-17 17:53:38 +02001799 struct via_spec *spec = codec->spec;
1800 int i;
1801
1802 for (i = 0; i < spec->multiout.num_dacs; i++) {
1803 if (spec->multiout.dac_nids[i] == dac)
1804 return false;
1805 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001806 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001807 return false;
1808 return true;
1809}
1810
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001811static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001812 hda_nid_t target_dac, int with_aa_mix,
1813 struct nid_path *path, int depth)
Takashi Iwai4a796162011-06-17 17:53:38 +02001814{
Takashi Iwai3214b962011-07-18 12:49:25 +02001815 struct via_spec *spec = codec->spec;
Takashi Iwai4a796162011-06-17 17:53:38 +02001816 hda_nid_t conn[8];
1817 int i, nums;
1818
Takashi Iwai3214b962011-07-18 12:49:25 +02001819 if (nid == spec->aa_mix_nid) {
1820 if (!with_aa_mix)
1821 return false;
1822 with_aa_mix = 2; /* mark aa-mix is included */
1823 }
1824
Takashi Iwai4a796162011-06-17 17:53:38 +02001825 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1826 for (i = 0; i < nums; i++) {
1827 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1828 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001829 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1830 /* aa-mix is requested but not included? */
1831 if (!(spec->aa_mix_nid && with_aa_mix == 1))
1832 goto found;
1833 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001834 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001835 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001836 return false;
1837 for (i = 0; i < nums; i++) {
1838 unsigned int type;
1839 type = get_wcaps_type(get_wcaps(codec, conn[i]));
Takashi Iwai3214b962011-07-18 12:49:25 +02001840 if (type == AC_WID_AUD_OUT)
Takashi Iwai4a796162011-06-17 17:53:38 +02001841 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001842 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai3214b962011-07-18 12:49:25 +02001843 with_aa_mix, path, depth + 1))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001844 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001845 }
1846 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001847
1848 found:
1849 path->path[path->depth] = conn[i];
1850 path->idx[path->depth] = i;
1851 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1852 path->multi[path->depth] = 1;
1853 path->depth++;
1854 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001855}
1856
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001857static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001858 hda_nid_t target_dac, int with_aa_mix,
1859 struct nid_path *path)
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001860{
Takashi Iwai3214b962011-07-18 12:49:25 +02001861 if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001862 path->path[path->depth] = nid;
1863 path->depth++;
Takashi Iwai3214b962011-07-18 12:49:25 +02001864 snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
1865 path->depth, path->path[0], path->path[1],
1866 path->path[2], path->path[3], path->path[4]);
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001867 return true;
1868 }
1869 return false;
1870}
1871
Takashi Iwai4a796162011-06-17 17:53:38 +02001872static int via_auto_fill_dac_nids(struct hda_codec *codec)
1873{
1874 struct via_spec *spec = codec->spec;
1875 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001876 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001877 hda_nid_t nid;
1878
Joseph Chanc577b8a2006-11-29 15:29:40 +01001879 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001880 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001881 for (i = 0; i < cfg->line_outs; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001882 hda_nid_t dac = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001883 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001884 if (!nid)
1885 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001886 if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
1887 dac = spec->out_path[i].path[0];
1888 if (!i && parse_output_path(codec, nid, dac, 1,
1889 &spec->out_mix_path))
1890 dac = spec->out_mix_path.path[0];
1891 if (dac) {
1892 spec->private_dac_nids[i] = dac;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001893 dac_num++;
1894 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001895 }
Takashi Iwai3214b962011-07-18 12:49:25 +02001896 if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
1897 spec->out_path[0] = spec->out_mix_path;
1898 spec->out_mix_path.depth = 0;
1899 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001900 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001901 return 0;
1902}
1903
Takashi Iwai4a796162011-06-17 17:53:38 +02001904static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001905 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001906{
Takashi Iwai4a796162011-06-17 17:53:38 +02001907 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001908 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001909 hda_nid_t dac, pin, sel, nid;
1910 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001911
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001912 dac = check_dac ? path->path[0] : 0;
1913 pin = path->path[path->depth - 1];
1914 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001915
Takashi Iwai8df2a312011-06-21 11:48:29 +02001916 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001917 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001918 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001919 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001920 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1921 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001922 else
1923 nid = 0;
1924 if (nid) {
1925 sprintf(name, "%s Playback Volume", pfx);
1926 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001927 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001928 if (err < 0)
1929 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001930 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001931 }
1932
Takashi Iwai8df2a312011-06-21 11:48:29 +02001933 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001934 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001935 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001936 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001937 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1938 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001939 else
1940 nid = 0;
1941 if (nid) {
1942 sprintf(name, "%s Playback Switch", pfx);
1943 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1944 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1945 if (err < 0)
1946 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001947 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001948 }
1949 return 0;
1950}
1951
Takashi Iwaif4a78282011-06-17 18:46:48 +02001952static void mangle_smart51(struct hda_codec *codec)
1953{
1954 struct via_spec *spec = codec->spec;
1955 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001956 struct auto_pin_cfg_item *ins = cfg->inputs;
1957 int i, j, nums, attr;
1958 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001959
Takashi Iwai0f98c242011-06-21 12:51:33 +02001960 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1961 nums = 0;
1962 for (i = 0; i < cfg->num_inputs; i++) {
1963 unsigned int def;
1964 if (ins[i].type > AUTO_PIN_LINE_IN)
1965 continue;
1966 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1967 if (snd_hda_get_input_pin_attr(def) != attr)
1968 continue;
1969 for (j = 0; j < nums; j++)
1970 if (ins[pins[j]].type < ins[i].type) {
1971 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001972 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001973 break;
1974 }
1975 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001976 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001977 }
1978 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001979 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001980 for (i = 0; i < nums; i++) {
1981 hda_nid_t pin = ins[pins[i]].pin;
1982 spec->smart51_pins[spec->smart51_nums++] = pin;
1983 cfg->line_out_pins[cfg->line_outs++] = pin;
1984 if (cfg->line_outs == 3)
1985 break;
1986 }
1987 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001988 }
1989}
1990
Takashi Iwai020066d2011-07-21 13:45:56 +02001991static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src)
1992{
1993 dst->vol_ctl = src->vol_ctl;
1994 dst->mute_ctl = src->mute_ctl;
1995}
1996
Takashi Iwai4a796162011-06-17 17:53:38 +02001997/* add playback controls from the parsed DAC table */
1998static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1999{
2000 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02002001 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai3214b962011-07-18 12:49:25 +02002002 struct nid_path *path;
Takashi Iwaiea734962011-01-17 11:29:34 +01002003 static const char * const chname[4] = {
2004 "Front", "Surround", "C/LFE", "Side"
2005 };
Takashi Iwai4a796162011-06-17 17:53:38 +02002006 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02002007 int old_line_outs;
2008
2009 /* check smart51 */
2010 old_line_outs = cfg->line_outs;
2011 if (cfg->line_outs == 1)
2012 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002013
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002014 err = via_auto_fill_dac_nids(codec);
2015 if (err < 0)
2016 return err;
2017
Lydia Wang5c9a5612011-07-08 14:03:43 +08002018 if (spec->multiout.num_dacs < 3) {
2019 spec->smart51_nums = 0;
2020 cfg->line_outs = old_line_outs;
2021 }
Takashi Iwai4a796162011-06-17 17:53:38 +02002022 for (i = 0; i < cfg->line_outs; i++) {
2023 hda_nid_t pin, dac;
2024 pin = cfg->line_out_pins[i];
2025 dac = spec->multiout.dac_nids[i];
2026 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002027 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02002028 path = spec->out_path + i;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02002029 if (i == HDA_CLFE) {
Takashi Iwai3214b962011-07-18 12:49:25 +02002030 err = create_ch_ctls(codec, "Center", 1, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002031 if (err < 0)
2032 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002033 err = create_ch_ctls(codec, "LFE", 2, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002034 if (err < 0)
2035 return err;
2036 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02002037 const char *pfx = chname[i];
2038 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
2039 cfg->line_outs == 1)
2040 pfx = "Speaker";
Takashi Iwai3214b962011-07-18 12:49:25 +02002041 err = create_ch_ctls(codec, pfx, 3, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002042 if (err < 0)
2043 return err;
2044 }
Takashi Iwai020066d2011-07-21 13:45:56 +02002045 if (path != spec->out_path + i)
2046 copy_path_mixer_ctls(&spec->out_path[i], path);
2047 if (path == spec->out_path && spec->out_mix_path.depth)
2048 copy_path_mixer_ctls(&spec->out_mix_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002049 }
2050
Takashi Iwai4a796162011-06-17 17:53:38 +02002051 idx = get_connection_index(codec, spec->aa_mix_nid,
2052 spec->multiout.dac_nids[0]);
2053 if (idx >= 0) {
2054 /* add control to mixer */
Takashi Iwai3214b962011-07-18 12:49:25 +02002055 const char *name;
2056 name = spec->out_mix_path.depth ?
2057 "PCM Loopback Playback Volume" : "PCM Playback Volume";
2058 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002059 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2060 idx, HDA_INPUT));
2061 if (err < 0)
2062 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002063 name = spec->out_mix_path.depth ?
2064 "PCM Loopback Playback Switch" : "PCM Playback Switch";
2065 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002066 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2067 idx, HDA_INPUT));
2068 if (err < 0)
2069 return err;
2070 }
2071
Takashi Iwaif4a78282011-06-17 18:46:48 +02002072 cfg->line_outs = old_line_outs;
2073
Joseph Chanc577b8a2006-11-29 15:29:40 +01002074 return 0;
2075}
2076
Takashi Iwai4a796162011-06-17 17:53:38 +02002077static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002078{
Takashi Iwai4a796162011-06-17 17:53:38 +02002079 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002080 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002081 bool check_dac;
Takashi Iwai3214b962011-07-18 12:49:25 +02002082 int i, err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002083
2084 if (!pin)
2085 return 0;
2086
Takashi Iwai3214b962011-07-18 12:49:25 +02002087 if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
2088 for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
2089 if (i < spec->multiout.num_dacs &&
2090 parse_output_path(codec, pin,
2091 spec->multiout.dac_nids[i], 0,
2092 &spec->hp_indep_path)) {
2093 spec->hp_indep_shared = i;
2094 break;
2095 }
2096 }
Takashi Iwai25250502011-06-30 17:24:47 +02002097 }
Takashi Iwai3214b962011-07-18 12:49:25 +02002098 if (spec->hp_indep_path.depth) {
2099 spec->hp_dac_nid = spec->hp_indep_path.path[0];
2100 if (!spec->hp_indep_shared)
2101 spec->hp_path = spec->hp_indep_path;
2102 }
2103 /* optionally check front-path w/o AA-mix */
2104 if (!spec->hp_path.depth)
2105 parse_output_path(codec, pin,
2106 spec->multiout.dac_nids[HDA_FRONT], 0,
2107 &spec->hp_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002108
Takashi Iwaiece8d042011-06-19 16:24:21 +02002109 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai3214b962011-07-18 12:49:25 +02002110 1, &spec->hp_mix_path) && !spec->hp_path.depth)
Takashi Iwaiece8d042011-06-19 16:24:21 +02002111 return 0;
2112
Takashi Iwai3214b962011-07-18 12:49:25 +02002113 if (spec->hp_path.depth) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002114 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002115 check_dac = true;
2116 } else {
Takashi Iwai3214b962011-07-18 12:49:25 +02002117 path = &spec->hp_mix_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002118 check_dac = false;
2119 }
2120 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002121 if (err < 0)
2122 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002123 if (check_dac)
2124 copy_path_mixer_ctls(&spec->hp_mix_path, path);
2125 else
2126 copy_path_mixer_ctls(&spec->hp_path, path);
2127 if (spec->hp_indep_path.depth)
2128 copy_path_mixer_ctls(&spec->hp_indep_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002129 return 0;
2130}
2131
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002132static int via_auto_create_speaker_ctls(struct hda_codec *codec)
2133{
2134 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +02002135 struct nid_path *path;
2136 bool check_dac;
Wang Shaoyan81c0a782011-08-05 18:51:29 +08002137 hda_nid_t pin, dac = 0;
Takashi Iwai3214b962011-07-18 12:49:25 +02002138 int err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002139
2140 pin = spec->autocfg.speaker_pins[0];
2141 if (!spec->autocfg.speaker_outs || !pin)
2142 return 0;
2143
Takashi Iwai3214b962011-07-18 12:49:25 +02002144 if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02002145 dac = spec->speaker_path.path[0];
Takashi Iwai3214b962011-07-18 12:49:25 +02002146 if (!dac)
2147 parse_output_path(codec, pin,
2148 spec->multiout.dac_nids[HDA_FRONT], 0,
2149 &spec->speaker_path);
2150 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
2151 1, &spec->speaker_mix_path) && !dac)
2152 return 0;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002153
Takashi Iwai3214b962011-07-18 12:49:25 +02002154 /* no AA-path for front? */
2155 if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
2156 dac = 0;
2157
2158 spec->speaker_dac_nid = dac;
2159 spec->multiout.extra_out_nid[0] = dac;
2160 if (dac) {
2161 path = &spec->speaker_path;
2162 check_dac = true;
2163 } else {
2164 path = &spec->speaker_mix_path;
2165 check_dac = false;
2166 }
2167 err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
2168 if (err < 0)
2169 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002170 if (check_dac)
2171 copy_path_mixer_ctls(&spec->speaker_mix_path, path);
2172 else
2173 copy_path_mixer_ctls(&spec->speaker_path, path);
Takashi Iwai3214b962011-07-18 12:49:25 +02002174 return 0;
2175}
2176
2177#define via_aamix_ctl_info via_pin_power_ctl_info
2178
2179static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol,
2180 struct snd_ctl_elem_value *ucontrol)
2181{
2182 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2183 struct via_spec *spec = codec->spec;
2184 ucontrol->value.enumerated.item[0] = spec->aamix_mode;
2185 return 0;
2186}
2187
2188static void update_aamix_paths(struct hda_codec *codec, int do_mix,
2189 struct nid_path *nomix, struct nid_path *mix)
2190{
2191 if (do_mix) {
2192 activate_output_path(codec, nomix, false, false);
2193 activate_output_path(codec, mix, true, false);
2194 } else {
2195 activate_output_path(codec, mix, false, false);
2196 activate_output_path(codec, nomix, true, false);
2197 }
2198}
2199
2200static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol,
2201 struct snd_ctl_elem_value *ucontrol)
2202{
2203 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2204 struct via_spec *spec = codec->spec;
2205 unsigned int val = ucontrol->value.enumerated.item[0];
2206
2207 if (val == spec->aamix_mode)
2208 return 0;
2209 spec->aamix_mode = val;
2210 /* update front path */
2211 update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
2212 /* update HP path */
2213 if (!spec->hp_independent_mode) {
2214 update_aamix_paths(codec, val, &spec->hp_path,
2215 &spec->hp_mix_path);
2216 }
2217 /* update speaker path */
2218 update_aamix_paths(codec, val, &spec->speaker_path,
2219 &spec->speaker_mix_path);
2220 return 1;
2221}
2222
2223static const struct snd_kcontrol_new via_aamix_ctl_enum = {
2224 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2225 .name = "Loopback Mixing",
2226 .info = via_aamix_ctl_info,
2227 .get = via_aamix_ctl_get,
2228 .put = via_aamix_ctl_put,
2229};
2230
2231static int via_auto_create_loopback_switch(struct hda_codec *codec)
2232{
2233 struct via_spec *spec = codec->spec;
2234
Takashi Iwai4808d122012-01-10 15:16:02 +01002235 if (!spec->aa_mix_nid)
2236 return 0; /* no loopback switching available */
2237 if (!(spec->out_mix_path.depth || spec->hp_mix_path.depth ||
2238 spec->speaker_path.depth))
Takashi Iwai3214b962011-07-18 12:49:25 +02002239 return 0; /* no loopback switching available */
2240 if (!via_clone_control(spec, &via_aamix_ctl_enum))
2241 return -ENOMEM;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002242 return 0;
2243}
2244
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002245/* look for ADCs */
2246static int via_fill_adcs(struct hda_codec *codec)
2247{
2248 struct via_spec *spec = codec->spec;
2249 hda_nid_t nid = codec->start_nid;
2250 int i;
2251
2252 for (i = 0; i < codec->num_nodes; i++, nid++) {
2253 unsigned int wcaps = get_wcaps(codec, nid);
2254 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2255 continue;
2256 if (wcaps & AC_WCAP_DIGITAL)
2257 continue;
2258 if (!(wcaps & AC_WCAP_CONN_LIST))
2259 continue;
2260 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2261 return -ENOMEM;
2262 spec->adc_nids[spec->num_adc_nids++] = nid;
2263 }
2264 return 0;
2265}
2266
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002267/* input-src control */
2268static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2269 struct snd_ctl_elem_info *uinfo)
2270{
2271 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2272 struct via_spec *spec = codec->spec;
2273
2274 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2275 uinfo->count = 1;
2276 uinfo->value.enumerated.items = spec->num_inputs;
2277 if (uinfo->value.enumerated.item >= spec->num_inputs)
2278 uinfo->value.enumerated.item = spec->num_inputs - 1;
2279 strcpy(uinfo->value.enumerated.name,
2280 spec->inputs[uinfo->value.enumerated.item].label);
2281 return 0;
2282}
2283
2284static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2285 struct snd_ctl_elem_value *ucontrol)
2286{
2287 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2288 struct via_spec *spec = codec->spec;
2289 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2290
2291 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2292 return 0;
2293}
2294
2295static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2296 struct snd_ctl_elem_value *ucontrol)
2297{
2298 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2299 struct via_spec *spec = codec->spec;
2300 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2301 hda_nid_t mux;
2302 int cur;
2303
2304 cur = ucontrol->value.enumerated.item[0];
2305 if (cur < 0 || cur >= spec->num_inputs)
2306 return -EINVAL;
2307 if (spec->cur_mux[idx] == cur)
2308 return 0;
2309 spec->cur_mux[idx] = cur;
2310 if (spec->dyn_adc_switch) {
2311 int adc_idx = spec->inputs[cur].adc_idx;
2312 mux = spec->mux_nids[adc_idx];
2313 via_dyn_adc_pcm_resetup(codec, cur);
2314 } else {
2315 mux = spec->mux_nids[idx];
2316 if (snd_BUG_ON(!mux))
2317 return -EINVAL;
2318 }
2319
2320 if (mux) {
2321 /* switch to D0 beofre change index */
Takashi Iwai054d8672012-01-24 12:25:50 +01002322 update_power_state(codec, mux, AC_PWRST_D0);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002323 snd_hda_codec_write(codec, mux, 0,
2324 AC_VERB_SET_CONNECT_SEL,
2325 spec->inputs[cur].mux_idx);
2326 }
2327
2328 /* update jack power state */
2329 set_widgets_power_state(codec);
2330 return 0;
2331}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002332
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002333static const struct snd_kcontrol_new via_input_src_ctl = {
2334 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2335 /* The multiple "Capture Source" controls confuse alsamixer
2336 * So call somewhat different..
2337 */
2338 /* .name = "Capture Source", */
2339 .name = "Input Source",
2340 .info = via_mux_enum_info,
2341 .get = via_mux_enum_get,
2342 .put = via_mux_enum_put,
2343};
2344
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002345static int create_input_src_ctls(struct hda_codec *codec, int count)
2346{
2347 struct via_spec *spec = codec->spec;
2348 struct snd_kcontrol_new *knew;
2349
2350 if (spec->num_inputs <= 1 || !count)
2351 return 0; /* no need for single src */
2352
2353 knew = via_clone_control(spec, &via_input_src_ctl);
2354 if (!knew)
2355 return -ENOMEM;
2356 knew->count = count;
2357 return 0;
2358}
2359
2360/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002361static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2362{
2363 struct hda_amp_list *list;
2364
2365 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2366 return;
2367 list = spec->loopback_list + spec->num_loopbacks;
2368 list->nid = mix;
2369 list->dir = HDA_INPUT;
2370 list->idx = idx;
2371 spec->num_loopbacks++;
2372 spec->loopback.amplist = spec->loopback_list;
2373}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002374
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002375static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002376 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002377{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002378 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002379}
2380
2381/* add the input-route to the given pin */
2382static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002383{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002384 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002385 int c, idx;
2386
2387 spec->inputs[spec->num_inputs].adc_idx = -1;
2388 spec->inputs[spec->num_inputs].pin = pin;
2389 for (c = 0; c < spec->num_adc_nids; c++) {
2390 if (spec->mux_nids[c]) {
2391 idx = get_connection_index(codec, spec->mux_nids[c],
2392 pin);
2393 if (idx < 0)
2394 continue;
2395 spec->inputs[spec->num_inputs].mux_idx = idx;
2396 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002397 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002398 continue;
2399 }
2400 spec->inputs[spec->num_inputs].adc_idx = c;
2401 /* Can primary ADC satisfy all inputs? */
2402 if (!spec->dyn_adc_switch &&
2403 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2404 snd_printd(KERN_INFO
2405 "via: dynamic ADC switching enabled\n");
2406 spec->dyn_adc_switch = 1;
2407 }
2408 return true;
2409 }
2410 return false;
2411}
2412
2413static int get_mux_nids(struct hda_codec *codec);
2414
2415/* parse input-routes; fill ADCs, MUXs and input-src entries */
2416static int parse_analog_inputs(struct hda_codec *codec)
2417{
2418 struct via_spec *spec = codec->spec;
2419 const struct auto_pin_cfg *cfg = &spec->autocfg;
2420 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002421
2422 err = via_fill_adcs(codec);
2423 if (err < 0)
2424 return err;
2425 err = get_mux_nids(codec);
2426 if (err < 0)
2427 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002428
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002429 /* fill all input-routes */
2430 for (i = 0; i < cfg->num_inputs; i++) {
2431 if (add_input_route(codec, cfg->inputs[i].pin))
2432 spec->inputs[spec->num_inputs++].label =
2433 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002434 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002435
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002436 /* check for internal loopback recording */
2437 if (spec->aa_mix_nid &&
2438 add_input_route(codec, spec->aa_mix_nid))
2439 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2440
2441 return 0;
2442}
2443
2444/* create analog-loopback volume/switch controls */
2445static int create_loopback_ctls(struct hda_codec *codec)
2446{
2447 struct via_spec *spec = codec->spec;
2448 const struct auto_pin_cfg *cfg = &spec->autocfg;
2449 const char *prev_label = NULL;
2450 int type_idx = 0;
2451 int i, j, err, idx;
2452
2453 if (!spec->aa_mix_nid)
2454 return 0;
2455
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002456 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002457 hda_nid_t pin = cfg->inputs[i].pin;
2458 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2459
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002460 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002461 type_idx++;
2462 else
2463 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002464 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002465 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2466 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002467 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002468 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002469 if (err < 0)
2470 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002471 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002472 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002473
2474 /* remember the label for smart51 control */
2475 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002476 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002477 spec->smart51_idxs[j] = idx;
2478 spec->smart51_labels[j] = label;
2479 break;
2480 }
2481 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002482 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002483 return 0;
2484}
2485
2486/* create mic-boost controls (if present) */
2487static int create_mic_boost_ctls(struct hda_codec *codec)
2488{
2489 struct via_spec *spec = codec->spec;
2490 const struct auto_pin_cfg *cfg = &spec->autocfg;
2491 int i, err;
2492
2493 for (i = 0; i < cfg->num_inputs; i++) {
2494 hda_nid_t pin = cfg->inputs[i].pin;
2495 unsigned int caps;
2496 const char *label;
2497 char name[32];
2498
2499 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2500 continue;
2501 caps = query_amp_caps(codec, pin, HDA_INPUT);
2502 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2503 continue;
2504 label = hda_get_autocfg_input_label(codec, cfg, i);
2505 snprintf(name, sizeof(name), "%s Boost Volume", label);
2506 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2507 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2508 if (err < 0)
2509 return err;
2510 }
2511 return 0;
2512}
2513
2514/* create capture and input-src controls for multiple streams */
2515static int create_multi_adc_ctls(struct hda_codec *codec)
2516{
2517 struct via_spec *spec = codec->spec;
2518 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002519
2520 /* create capture mixer elements */
2521 for (i = 0; i < spec->num_adc_nids; i++) {
2522 hda_nid_t adc = spec->adc_nids[i];
2523 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2524 "Capture Volume", i,
2525 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2526 HDA_INPUT));
2527 if (err < 0)
2528 return err;
2529 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2530 "Capture Switch", i,
2531 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2532 HDA_INPUT));
2533 if (err < 0)
2534 return err;
2535 }
2536
2537 /* input-source control */
2538 for (i = 0; i < spec->num_adc_nids; i++)
2539 if (!spec->mux_nids[i])
2540 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002541 err = create_input_src_ctls(codec, i);
2542 if (err < 0)
2543 return err;
2544 return 0;
2545}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002546
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002547/* bind capture volume/switch */
2548static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2549 HDA_BIND_VOL("Capture Volume", 0);
2550static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2551 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002552
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002553static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2554 struct hda_ctl_ops *ops)
2555{
2556 struct hda_bind_ctls *ctl;
2557 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002558
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002559 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2560 if (!ctl)
2561 return -ENOMEM;
2562 ctl->ops = ops;
2563 for (i = 0; i < spec->num_adc_nids; i++)
2564 ctl->values[i] =
2565 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2566 *ctl_ret = ctl;
2567 return 0;
2568}
2569
2570/* create capture and input-src controls for dynamic ADC-switch case */
2571static int create_dyn_adc_ctls(struct hda_codec *codec)
2572{
2573 struct via_spec *spec = codec->spec;
2574 struct snd_kcontrol_new *knew;
2575 int err;
2576
2577 /* set up the bind capture ctls */
2578 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2579 if (err < 0)
2580 return err;
2581 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2582 if (err < 0)
2583 return err;
2584
2585 /* create capture mixer elements */
2586 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2587 if (!knew)
2588 return -ENOMEM;
2589 knew->private_value = (long)spec->bind_cap_vol;
2590
2591 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2592 if (!knew)
2593 return -ENOMEM;
2594 knew->private_value = (long)spec->bind_cap_sw;
2595
2596 /* input-source control */
2597 err = create_input_src_ctls(codec, 1);
2598 if (err < 0)
2599 return err;
2600 return 0;
2601}
2602
2603/* parse and create capture-related stuff */
2604static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2605{
2606 struct via_spec *spec = codec->spec;
2607 int err;
2608
2609 err = parse_analog_inputs(codec);
2610 if (err < 0)
2611 return err;
2612 if (spec->dyn_adc_switch)
2613 err = create_dyn_adc_ctls(codec);
2614 else
2615 err = create_multi_adc_ctls(codec);
2616 if (err < 0)
2617 return err;
2618 err = create_loopback_ctls(codec);
2619 if (err < 0)
2620 return err;
2621 err = create_mic_boost_ctls(codec);
2622 if (err < 0)
2623 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002624 return 0;
2625}
2626
Harald Welte76d9b0d2008-09-09 15:50:37 +08002627static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2628{
2629 unsigned int def_conf;
2630 unsigned char seqassoc;
2631
Takashi Iwai2f334f92009-02-20 14:37:42 +01002632 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002633 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2634 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002635 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2636 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2637 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2638 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002639 }
2640
2641 return;
2642}
2643
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002644static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002645 struct snd_ctl_elem_value *ucontrol)
2646{
2647 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2648 struct via_spec *spec = codec->spec;
2649
2650 if (spec->codec_type != VT1708)
2651 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002652 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002653 return 0;
2654}
2655
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002656static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002657 struct snd_ctl_elem_value *ucontrol)
2658{
2659 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2660 struct via_spec *spec = codec->spec;
Takashi Iwai187d3332011-11-24 16:33:09 +01002661 int val;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002662
2663 if (spec->codec_type != VT1708)
2664 return 0;
Takashi Iwai187d3332011-11-24 16:33:09 +01002665 val = !!ucontrol->value.integer.value[0];
2666 if (spec->vt1708_jack_detect == val)
2667 return 0;
2668 spec->vt1708_jack_detect = val;
2669 if (spec->vt1708_jack_detect &&
2670 snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") != 1) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002671 mute_aa_path(codec, 1);
2672 notify_aa_path_ctls(codec);
2673 }
Takashi Iwai187d3332011-11-24 16:33:09 +01002674 via_hp_automute(codec);
2675 vt1708_update_hp_work(spec);
2676 return 1;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002677}
2678
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002679static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2680 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2681 .name = "Jack Detect",
2682 .count = 1,
2683 .info = snd_ctl_boolean_mono_info,
2684 .get = vt1708_jack_detect_get,
2685 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002686};
2687
Takashi Iwai12daef62011-06-18 17:45:49 +02002688static void fill_dig_outs(struct hda_codec *codec);
2689static void fill_dig_in(struct hda_codec *codec);
2690
2691static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002692{
2693 struct via_spec *spec = codec->spec;
2694 int err;
2695
2696 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2697 if (err < 0)
2698 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002699 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002700 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002701
Takashi Iwai4a796162011-06-17 17:53:38 +02002702 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002703 if (err < 0)
2704 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002705 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002706 if (err < 0)
2707 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002708 err = via_auto_create_speaker_ctls(codec);
2709 if (err < 0)
2710 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002711 err = via_auto_create_loopback_switch(codec);
2712 if (err < 0)
2713 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002714 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002715 if (err < 0)
2716 return err;
2717
2718 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2719
Takashi Iwai12daef62011-06-18 17:45:49 +02002720 fill_dig_outs(codec);
2721 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002722
Takashi Iwai603c4012008-07-30 15:01:44 +02002723 if (spec->kctls.list)
2724 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002725
Joseph Chanc577b8a2006-11-29 15:29:40 +01002726
Takashi Iwai3214b962011-07-18 12:49:25 +02002727 if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002728 err = via_hp_build(codec);
2729 if (err < 0)
2730 return err;
2731 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002732
Takashi Iwaif4a78282011-06-17 18:46:48 +02002733 err = via_smart51_build(codec);
2734 if (err < 0)
2735 return err;
2736
Takashi Iwai5d417622011-06-20 11:32:27 +02002737 /* assign slave outs */
2738 if (spec->slave_dig_outs[0])
2739 codec->slave_dig_outs = spec->slave_dig_outs;
2740
Joseph Chanc577b8a2006-11-29 15:29:40 +01002741 return 1;
2742}
2743
Takashi Iwai5d417622011-06-20 11:32:27 +02002744static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002745{
Lydia Wang25eaba22009-10-10 19:08:43 +08002746 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002747 if (spec->multiout.dig_out_nid)
2748 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2749 if (spec->slave_dig_outs[0])
2750 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2751}
Lydia Wang25eaba22009-10-10 19:08:43 +08002752
Takashi Iwai5d417622011-06-20 11:32:27 +02002753static void via_auto_init_dig_in(struct hda_codec *codec)
2754{
2755 struct via_spec *spec = codec->spec;
2756 if (!spec->dig_in_nid)
2757 return;
2758 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2759 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2760}
2761
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002762/* initialize the unsolicited events */
2763static void via_auto_init_unsol_event(struct hda_codec *codec)
2764{
2765 struct via_spec *spec = codec->spec;
2766 struct auto_pin_cfg *cfg = &spec->autocfg;
2767 unsigned int ev;
2768 int i;
2769
2770 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002771 snd_hda_jack_detect_enable(codec, cfg->hp_pins[0],
2772 VIA_HP_EVENT | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002773
2774 if (cfg->speaker_pins[0])
2775 ev = VIA_LINE_EVENT;
2776 else
2777 ev = 0;
2778 for (i = 0; i < cfg->line_outs; i++) {
2779 if (cfg->line_out_pins[i] &&
2780 is_jack_detectable(codec, cfg->line_out_pins[i]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002781 snd_hda_jack_detect_enable(codec, cfg->line_out_pins[i],
2782 ev | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002783 }
2784
2785 for (i = 0; i < cfg->num_inputs; i++) {
2786 if (is_jack_detectable(codec, cfg->inputs[i].pin))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002787 snd_hda_jack_detect_enable(codec, cfg->inputs[i].pin,
2788 VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002789 }
2790}
2791
Takashi Iwai5d417622011-06-20 11:32:27 +02002792static int via_init(struct hda_codec *codec)
2793{
2794 struct via_spec *spec = codec->spec;
2795 int i;
2796
2797 for (i = 0; i < spec->num_iverbs; i++)
2798 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2799
Takashi Iwaie9d010c2012-02-01 10:33:23 +01002800 /* init power states */
2801 set_widgets_power_state(codec);
2802 __analog_low_current_mode(codec, true);
2803
Joseph Chanc577b8a2006-11-29 15:29:40 +01002804 via_auto_init_multi_out(codec);
2805 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002806 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002807 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002808 via_auto_init_dig_outs(codec);
2809 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002810
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002811 via_auto_init_unsol_event(codec);
2812
2813 via_hp_automute(codec);
Takashi Iwai187d3332011-11-24 16:33:09 +01002814 vt1708_update_hp_work(spec);
Takashi Iwai01a61e12011-10-28 00:03:22 +02002815 snd_hda_jack_report_sync(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002816
Joseph Chanc577b8a2006-11-29 15:29:40 +01002817 return 0;
2818}
2819
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002820static void vt1708_update_hp_jack_state(struct work_struct *work)
2821{
2822 struct via_spec *spec = container_of(work, struct via_spec,
2823 vt1708_hp_work.work);
2824 if (spec->codec_type != VT1708)
2825 return;
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002826 snd_hda_jack_set_dirty_all(spec->codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002827 /* if jack state toggled */
2828 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002829 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002830 spec->vt1708_hp_present ^= 1;
2831 via_hp_automute(spec->codec);
2832 }
Takashi Iwai187d3332011-11-24 16:33:09 +01002833 if (spec->vt1708_jack_detect)
2834 schedule_delayed_work(&spec->vt1708_hp_work,
2835 msecs_to_jiffies(100));
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002836}
2837
Takashi Iwai337b9d02009-07-07 18:18:59 +02002838static int get_mux_nids(struct hda_codec *codec)
2839{
2840 struct via_spec *spec = codec->spec;
2841 hda_nid_t nid, conn[8];
2842 unsigned int type;
2843 int i, n;
2844
2845 for (i = 0; i < spec->num_adc_nids; i++) {
2846 nid = spec->adc_nids[i];
2847 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002848 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002849 if (type == AC_WID_PIN)
2850 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002851 n = snd_hda_get_connections(codec, nid, conn,
2852 ARRAY_SIZE(conn));
2853 if (n <= 0)
2854 break;
2855 if (n > 1) {
2856 spec->mux_nids[i] = nid;
2857 break;
2858 }
2859 nid = conn[0];
2860 }
2861 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002862 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002863}
2864
Joseph Chanc577b8a2006-11-29 15:29:40 +01002865static int patch_vt1708(struct hda_codec *codec)
2866{
2867 struct via_spec *spec;
2868 int err;
2869
2870 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002871 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002872 if (spec == NULL)
2873 return -ENOMEM;
2874
Takashi Iwai620e2b22011-06-17 17:19:19 +02002875 spec->aa_mix_nid = 0x17;
2876
Takashi Iwai12daef62011-06-18 17:45:49 +02002877 /* Add HP and CD pin config connect bit re-config action */
2878 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2879 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2880
Joseph Chanc577b8a2006-11-29 15:29:40 +01002881 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002882 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002883 if (err < 0) {
2884 via_free(codec);
2885 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002886 }
2887
Takashi Iwai12daef62011-06-18 17:45:49 +02002888 /* add jack detect on/off control */
2889 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2890 return -ENOMEM;
2891
Takashi Iwaibc9b562382008-05-23 17:50:27 +02002892 /* disable 32bit format on VT1708 */
2893 if (codec->vendor_id == 0x11061708)
2894 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002895
Lydia Wange322a362011-06-29 13:52:02 +08002896 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2897
Joseph Chanc577b8a2006-11-29 15:29:40 +01002898 codec->patch_ops = via_patch_ops;
2899
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002900 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002901 return 0;
2902}
2903
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002904static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002905{
2906 struct via_spec *spec;
2907 int err;
2908
2909 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002910 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002911 if (spec == NULL)
2912 return -ENOMEM;
2913
Takashi Iwai620e2b22011-06-17 17:19:19 +02002914 spec->aa_mix_nid = 0x18;
2915
Takashi Iwai12daef62011-06-18 17:45:49 +02002916 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002917 if (err < 0) {
2918 via_free(codec);
2919 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002920 }
2921
Joseph Chanc577b8a2006-11-29 15:29:40 +01002922 codec->patch_ops = via_patch_ops;
2923
Josepch Chanf7278fd2007-12-13 16:40:40 +01002924 return 0;
2925}
2926
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002927static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2928{
2929 struct via_spec *spec = codec->spec;
2930 int imux_is_smixer;
2931 unsigned int parm;
2932 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002933 if ((spec->codec_type != VT1708B_4CH) &&
2934 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002935 is_8ch = 1;
2936
2937 /* SW0 (17h) = stereo mixer */
2938 imux_is_smixer =
2939 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2940 == ((spec->codec_type == VT1708S) ? 5 : 0));
2941 /* inputs */
2942 /* PW 1/2/5 (1ah/1bh/1eh) */
2943 parm = AC_PWRST_D3;
2944 set_pin_power_state(codec, 0x1a, &parm);
2945 set_pin_power_state(codec, 0x1b, &parm);
2946 set_pin_power_state(codec, 0x1e, &parm);
2947 if (imux_is_smixer)
2948 parm = AC_PWRST_D0;
2949 /* SW0 (17h), AIW 0/1 (13h/14h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01002950 update_power_state(codec, 0x17, parm);
2951 update_power_state(codec, 0x13, parm);
2952 update_power_state(codec, 0x14, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002953
2954 /* outputs */
2955 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2956 parm = AC_PWRST_D3;
2957 set_pin_power_state(codec, 0x19, &parm);
2958 if (spec->smart51_enabled)
2959 set_pin_power_state(codec, 0x1b, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01002960 update_power_state(codec, 0x18, parm);
2961 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002962
2963 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2964 if (is_8ch) {
2965 parm = AC_PWRST_D3;
2966 set_pin_power_state(codec, 0x22, &parm);
2967 if (spec->smart51_enabled)
2968 set_pin_power_state(codec, 0x1a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01002969 update_power_state(codec, 0x26, parm);
2970 update_power_state(codec, 0x24, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002971 } else if (codec->vendor_id == 0x11064397) {
2972 /* PW7(23h), SW2(27h), AOW2(25h) */
2973 parm = AC_PWRST_D3;
2974 set_pin_power_state(codec, 0x23, &parm);
2975 if (spec->smart51_enabled)
2976 set_pin_power_state(codec, 0x1a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01002977 update_power_state(codec, 0x27, parm);
2978 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002979 }
2980
2981 /* PW 3/4/7 (1ch/1dh/23h) */
2982 parm = AC_PWRST_D3;
2983 /* force to D0 for internal Speaker */
2984 set_pin_power_state(codec, 0x1c, &parm);
2985 set_pin_power_state(codec, 0x1d, &parm);
2986 if (is_8ch)
2987 set_pin_power_state(codec, 0x23, &parm);
2988
2989 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01002990 update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
2991 update_power_state(codec, 0x10, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002992 if (is_8ch) {
Takashi Iwai054d8672012-01-24 12:25:50 +01002993 update_power_state(codec, 0x25, parm);
2994 update_power_state(codec, 0x27, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002995 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01002996 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002997}
2998
Lydia Wang518bf3b2009-10-10 19:07:29 +08002999static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003000static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01003001{
3002 struct via_spec *spec;
3003 int err;
3004
Lydia Wang518bf3b2009-10-10 19:07:29 +08003005 if (get_codec_type(codec) == VT1708BCE)
3006 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003007
Josepch Chanf7278fd2007-12-13 16:40:40 +01003008 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003009 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003010 if (spec == NULL)
3011 return -ENOMEM;
3012
Takashi Iwai620e2b22011-06-17 17:19:19 +02003013 spec->aa_mix_nid = 0x16;
3014
Josepch Chanf7278fd2007-12-13 16:40:40 +01003015 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003016 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003017 if (err < 0) {
3018 via_free(codec);
3019 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003020 }
3021
Josepch Chanf7278fd2007-12-13 16:40:40 +01003022 codec->patch_ops = via_patch_ops;
3023
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003024 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
3025
Josepch Chanf7278fd2007-12-13 16:40:40 +01003026 return 0;
3027}
3028
Harald Welted949cac2008-09-09 15:56:01 +08003029/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02003030static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08003031 /* Enable Mic Boost Volume backdoor */
3032 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003033 /* don't bybass mixer */
3034 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08003035 { }
3036};
3037
Takashi Iwai9da29272009-05-07 16:31:14 +02003038/* fill out digital output widgets; one for master and one for slave outputs */
3039static void fill_dig_outs(struct hda_codec *codec)
3040{
3041 struct via_spec *spec = codec->spec;
3042 int i;
3043
3044 for (i = 0; i < spec->autocfg.dig_outs; i++) {
3045 hda_nid_t nid;
3046 int conn;
3047
3048 nid = spec->autocfg.dig_out_pins[i];
3049 if (!nid)
3050 continue;
3051 conn = snd_hda_get_connections(codec, nid, &nid, 1);
3052 if (conn < 1)
3053 continue;
3054 if (!spec->multiout.dig_out_nid)
3055 spec->multiout.dig_out_nid = nid;
3056 else {
3057 spec->slave_dig_outs[0] = nid;
3058 break; /* at most two dig outs */
3059 }
3060 }
3061}
3062
Takashi Iwai12daef62011-06-18 17:45:49 +02003063static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08003064{
3065 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02003066 hda_nid_t dig_nid;
3067 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08003068
Takashi Iwai12daef62011-06-18 17:45:49 +02003069 if (!spec->autocfg.dig_in_pin)
3070 return;
Harald Welted949cac2008-09-09 15:56:01 +08003071
Takashi Iwai12daef62011-06-18 17:45:49 +02003072 dig_nid = codec->start_nid;
3073 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
3074 unsigned int wcaps = get_wcaps(codec, dig_nid);
3075 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
3076 continue;
3077 if (!(wcaps & AC_WCAP_DIGITAL))
3078 continue;
3079 if (!(wcaps & AC_WCAP_CONN_LIST))
3080 continue;
3081 err = get_connection_index(codec, dig_nid,
3082 spec->autocfg.dig_in_pin);
3083 if (err >= 0) {
3084 spec->dig_in_nid = dig_nid;
3085 break;
3086 }
3087 }
Harald Welted949cac2008-09-09 15:56:01 +08003088}
3089
Lydia Wang6369bcf2009-10-10 19:08:31 +08003090static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
3091 int offset, int num_steps, int step_size)
3092{
3093 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
3094 (offset << AC_AMPCAP_OFFSET_SHIFT) |
3095 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
3096 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
3097 (0 << AC_AMPCAP_MUTE_SHIFT));
3098}
3099
Harald Welted949cac2008-09-09 15:56:01 +08003100static int patch_vt1708S(struct hda_codec *codec)
3101{
3102 struct via_spec *spec;
3103 int err;
3104
3105 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003106 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003107 if (spec == NULL)
3108 return -ENOMEM;
3109
Takashi Iwai620e2b22011-06-17 17:19:19 +02003110 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003111 override_mic_boost(codec, 0x1a, 0, 3, 40);
3112 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003113
Harald Welted949cac2008-09-09 15:56:01 +08003114 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003115 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003116 if (err < 0) {
3117 via_free(codec);
3118 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003119 }
3120
Takashi Iwai096a8852011-06-20 12:09:02 +02003121 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003122
Harald Welted949cac2008-09-09 15:56:01 +08003123 codec->patch_ops = via_patch_ops;
3124
Lydia Wang518bf3b2009-10-10 19:07:29 +08003125 /* correct names for VT1708BCE */
3126 if (get_codec_type(codec) == VT1708BCE) {
3127 kfree(codec->chip_name);
3128 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3129 snprintf(codec->bus->card->mixername,
3130 sizeof(codec->bus->card->mixername),
3131 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f630f2011-03-22 16:25:56 +08003132 }
Lydia Wangbc92df72011-03-23 17:56:05 +08003133 /* correct names for VT1705 */
3134 if (codec->vendor_id == 0x11064397) {
3135 kfree(codec->chip_name);
3136 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3137 snprintf(codec->bus->card->mixername,
3138 sizeof(codec->bus->card->mixername),
3139 "%s %s", codec->vendor_name, codec->chip_name);
3140 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003141 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08003142 return 0;
3143}
3144
3145/* Patch for VT1702 */
3146
Takashi Iwai096a8852011-06-20 12:09:02 +02003147static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003148 /* mixer enable */
3149 {0x1, 0xF88, 0x3},
3150 /* GPIO 0~2 */
3151 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003152 { }
3153};
3154
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003155static void set_widgets_power_state_vt1702(struct hda_codec *codec)
3156{
3157 int imux_is_smixer =
3158 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3159 unsigned int parm;
3160 /* inputs */
3161 /* PW 1/2/5 (14h/15h/18h) */
3162 parm = AC_PWRST_D3;
3163 set_pin_power_state(codec, 0x14, &parm);
3164 set_pin_power_state(codec, 0x15, &parm);
3165 set_pin_power_state(codec, 0x18, &parm);
3166 if (imux_is_smixer)
3167 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
3168 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003169 update_power_state(codec, 0x13, parm);
3170 update_power_state(codec, 0x12, parm);
3171 update_power_state(codec, 0x1f, parm);
3172 update_power_state(codec, 0x20, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003173
3174 /* outputs */
3175 /* PW 3/4 (16h/17h) */
3176 parm = AC_PWRST_D3;
3177 set_pin_power_state(codec, 0x17, &parm);
3178 set_pin_power_state(codec, 0x16, &parm);
3179 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003180 update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm);
3181 update_power_state(codec, 0x10, parm);
3182 update_power_state(codec, 0x1d, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003183}
3184
Harald Welted949cac2008-09-09 15:56:01 +08003185static int patch_vt1702(struct hda_codec *codec)
3186{
3187 struct via_spec *spec;
3188 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003189
3190 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003191 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003192 if (spec == NULL)
3193 return -ENOMEM;
3194
Takashi Iwai620e2b22011-06-17 17:19:19 +02003195 spec->aa_mix_nid = 0x1a;
3196
Takashi Iwai12daef62011-06-18 17:45:49 +02003197 /* limit AA path volume to 0 dB */
3198 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3199 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3200 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3201 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3202 (1 << AC_AMPCAP_MUTE_SHIFT));
3203
Harald Welted949cac2008-09-09 15:56:01 +08003204 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003205 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003206 if (err < 0) {
3207 via_free(codec);
3208 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003209 }
3210
Takashi Iwai096a8852011-06-20 12:09:02 +02003211 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003212
Harald Welted949cac2008-09-09 15:56:01 +08003213 codec->patch_ops = via_patch_ops;
3214
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003215 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08003216 return 0;
3217}
3218
Lydia Wangeb7188c2009-10-10 19:08:34 +08003219/* Patch for VT1718S */
3220
Takashi Iwai096a8852011-06-20 12:09:02 +02003221static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08003222 /* Enable MW0 adjust Gain 5 */
3223 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003224 /* Enable Boost Volume backdoor */
3225 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02003226
Lydia Wangeb7188c2009-10-10 19:08:34 +08003227 { }
3228};
3229
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003230static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
3231{
3232 struct via_spec *spec = codec->spec;
3233 int imux_is_smixer;
3234 unsigned int parm;
3235 /* MUX6 (1eh) = stereo mixer */
3236 imux_is_smixer =
3237 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3238 /* inputs */
3239 /* PW 5/6/7 (29h/2ah/2bh) */
3240 parm = AC_PWRST_D3;
3241 set_pin_power_state(codec, 0x29, &parm);
3242 set_pin_power_state(codec, 0x2a, &parm);
3243 set_pin_power_state(codec, 0x2b, &parm);
3244 if (imux_is_smixer)
3245 parm = AC_PWRST_D0;
3246 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003247 update_power_state(codec, 0x1e, parm);
3248 update_power_state(codec, 0x1f, parm);
3249 update_power_state(codec, 0x10, parm);
3250 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003251
3252 /* outputs */
3253 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
3254 parm = AC_PWRST_D3;
3255 set_pin_power_state(codec, 0x27, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003256 update_power_state(codec, 0x1a, parm);
3257 update_power_state(codec, 0xb, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003258
3259 /* PW2 (26h), AOW2 (ah) */
3260 parm = AC_PWRST_D3;
3261 set_pin_power_state(codec, 0x26, &parm);
3262 if (spec->smart51_enabled)
3263 set_pin_power_state(codec, 0x2b, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003264 update_power_state(codec, 0xa, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003265
3266 /* PW0 (24h), AOW0 (8h) */
3267 parm = AC_PWRST_D3;
3268 set_pin_power_state(codec, 0x24, &parm);
3269 if (!spec->hp_independent_mode) /* check for redirected HP */
3270 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003271 update_power_state(codec, 0x8, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003272 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003273 update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003274
3275 /* PW1 (25h), AOW1 (9h) */
3276 parm = AC_PWRST_D3;
3277 set_pin_power_state(codec, 0x25, &parm);
3278 if (spec->smart51_enabled)
3279 set_pin_power_state(codec, 0x2a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003280 update_power_state(codec, 0x9, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003281
3282 if (spec->hp_independent_mode) {
3283 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3284 parm = AC_PWRST_D3;
3285 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003286 update_power_state(codec, 0x1b, parm);
3287 update_power_state(codec, 0x34, parm);
3288 update_power_state(codec, 0xc, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003289 }
3290}
3291
Takashi Iwai30b45032011-07-11 17:05:04 +02003292/* Add a connection to the primary DAC from AA-mixer for some codecs
3293 * This isn't listed from the raw info, but the chip has a secret connection.
3294 */
3295static int add_secret_dac_path(struct hda_codec *codec)
3296{
3297 struct via_spec *spec = codec->spec;
3298 int i, nums;
3299 hda_nid_t conn[8];
3300 hda_nid_t nid;
3301
3302 if (!spec->aa_mix_nid)
3303 return 0;
3304 nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
3305 ARRAY_SIZE(conn) - 1);
3306 for (i = 0; i < nums; i++) {
3307 if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
3308 return 0;
3309 }
3310
3311 /* find the primary DAC and add to the connection list */
3312 nid = codec->start_nid;
3313 for (i = 0; i < codec->num_nodes; i++, nid++) {
3314 unsigned int caps = get_wcaps(codec, nid);
3315 if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
3316 !(caps & AC_WCAP_DIGITAL)) {
3317 conn[nums++] = nid;
3318 return snd_hda_override_conn_list(codec,
3319 spec->aa_mix_nid,
3320 nums, conn);
3321 }
3322 }
3323 return 0;
3324}
3325
3326
Lydia Wangeb7188c2009-10-10 19:08:34 +08003327static int patch_vt1718S(struct hda_codec *codec)
3328{
3329 struct via_spec *spec;
3330 int err;
3331
3332 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003333 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003334 if (spec == NULL)
3335 return -ENOMEM;
3336
Takashi Iwai620e2b22011-06-17 17:19:19 +02003337 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003338 override_mic_boost(codec, 0x2b, 0, 3, 40);
3339 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003340 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003341
Lydia Wangeb7188c2009-10-10 19:08:34 +08003342 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003343 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003344 if (err < 0) {
3345 via_free(codec);
3346 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003347 }
3348
Takashi Iwai096a8852011-06-20 12:09:02 +02003349 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003350
Lydia Wangeb7188c2009-10-10 19:08:34 +08003351 codec->patch_ops = via_patch_ops;
3352
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003353 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3354
Lydia Wangeb7188c2009-10-10 19:08:34 +08003355 return 0;
3356}
Lydia Wangf3db4232009-10-10 19:08:41 +08003357
3358/* Patch for VT1716S */
3359
3360static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3361 struct snd_ctl_elem_info *uinfo)
3362{
3363 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3364 uinfo->count = 1;
3365 uinfo->value.integer.min = 0;
3366 uinfo->value.integer.max = 1;
3367 return 0;
3368}
3369
3370static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3371 struct snd_ctl_elem_value *ucontrol)
3372{
3373 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3374 int index = 0;
3375
3376 index = snd_hda_codec_read(codec, 0x26, 0,
3377 AC_VERB_GET_CONNECT_SEL, 0);
3378 if (index != -1)
3379 *ucontrol->value.integer.value = index;
3380
3381 return 0;
3382}
3383
3384static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3385 struct snd_ctl_elem_value *ucontrol)
3386{
3387 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3388 struct via_spec *spec = codec->spec;
3389 int index = *ucontrol->value.integer.value;
3390
3391 snd_hda_codec_write(codec, 0x26, 0,
3392 AC_VERB_SET_CONNECT_SEL, index);
3393 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003394 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003395 return 1;
3396}
3397
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003398static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003399 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3400 {
3401 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3402 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003403 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003404 .count = 1,
3405 .info = vt1716s_dmic_info,
3406 .get = vt1716s_dmic_get,
3407 .put = vt1716s_dmic_put,
3408 },
3409 {} /* end */
3410};
3411
3412
3413/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003414static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003415 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3416 { } /* end */
3417};
3418
Takashi Iwai096a8852011-06-20 12:09:02 +02003419static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003420 /* Enable Boost Volume backdoor */
3421 {0x1, 0xf8a, 0x80},
3422 /* don't bybass mixer */
3423 {0x1, 0xf88, 0xc0},
3424 /* Enable mono output */
3425 {0x1, 0xf90, 0x08},
3426 { }
3427};
3428
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003429static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3430{
3431 struct via_spec *spec = codec->spec;
3432 int imux_is_smixer;
3433 unsigned int parm;
3434 unsigned int mono_out, present;
3435 /* SW0 (17h) = stereo mixer */
3436 imux_is_smixer =
3437 (snd_hda_codec_read(codec, 0x17, 0,
3438 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3439 /* inputs */
3440 /* PW 1/2/5 (1ah/1bh/1eh) */
3441 parm = AC_PWRST_D3;
3442 set_pin_power_state(codec, 0x1a, &parm);
3443 set_pin_power_state(codec, 0x1b, &parm);
3444 set_pin_power_state(codec, 0x1e, &parm);
3445 if (imux_is_smixer)
3446 parm = AC_PWRST_D0;
3447 /* SW0 (17h), AIW0(13h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003448 update_power_state(codec, 0x17, parm);
3449 update_power_state(codec, 0x13, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003450
3451 parm = AC_PWRST_D3;
3452 set_pin_power_state(codec, 0x1e, &parm);
3453 /* PW11 (22h) */
3454 if (spec->dmic_enabled)
3455 set_pin_power_state(codec, 0x22, &parm);
3456 else
Takashi Iwai054d8672012-01-24 12:25:50 +01003457 update_power_state(codec, 0x22, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003458
3459 /* SW2(26h), AIW1(14h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003460 update_power_state(codec, 0x26, parm);
3461 update_power_state(codec, 0x14, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003462
3463 /* outputs */
3464 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3465 parm = AC_PWRST_D3;
3466 set_pin_power_state(codec, 0x19, &parm);
3467 /* Smart 5.1 PW2(1bh) */
3468 if (spec->smart51_enabled)
3469 set_pin_power_state(codec, 0x1b, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003470 update_power_state(codec, 0x18, parm);
3471 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003472
3473 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3474 parm = AC_PWRST_D3;
3475 set_pin_power_state(codec, 0x23, &parm);
3476 /* Smart 5.1 PW1(1ah) */
3477 if (spec->smart51_enabled)
3478 set_pin_power_state(codec, 0x1a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003479 update_power_state(codec, 0x27, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003480
3481 /* Smart 5.1 PW5(1eh) */
3482 if (spec->smart51_enabled)
3483 set_pin_power_state(codec, 0x1e, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003484 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003485
3486 /* Mono out */
3487 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3488 present = snd_hda_jack_detect(codec, 0x1c);
3489
3490 if (present)
3491 mono_out = 0;
3492 else {
3493 present = snd_hda_jack_detect(codec, 0x1d);
3494 if (!spec->hp_independent_mode && present)
3495 mono_out = 0;
3496 else
3497 mono_out = 1;
3498 }
3499 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
Takashi Iwai054d8672012-01-24 12:25:50 +01003500 update_power_state(codec, 0x28, parm);
3501 update_power_state(codec, 0x29, parm);
3502 update_power_state(codec, 0x2a, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003503
3504 /* PW 3/4 (1ch/1dh) */
3505 parm = AC_PWRST_D3;
3506 set_pin_power_state(codec, 0x1c, &parm);
3507 set_pin_power_state(codec, 0x1d, &parm);
3508 /* HP Independent Mode, power on AOW3 */
3509 if (spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01003510 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003511
3512 /* force to D0 for internal Speaker */
3513 /* MW0 (16h), AOW0 (10h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003514 update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
3515 update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003516}
3517
Lydia Wangf3db4232009-10-10 19:08:41 +08003518static int patch_vt1716S(struct hda_codec *codec)
3519{
3520 struct via_spec *spec;
3521 int err;
3522
3523 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003524 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003525 if (spec == NULL)
3526 return -ENOMEM;
3527
Takashi Iwai620e2b22011-06-17 17:19:19 +02003528 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003529 override_mic_boost(codec, 0x1a, 0, 3, 40);
3530 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003531
Lydia Wangf3db4232009-10-10 19:08:41 +08003532 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003533 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003534 if (err < 0) {
3535 via_free(codec);
3536 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003537 }
3538
Takashi Iwai096a8852011-06-20 12:09:02 +02003539 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003540
Lydia Wangf3db4232009-10-10 19:08:41 +08003541 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3542 spec->num_mixers++;
3543
3544 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3545
3546 codec->patch_ops = via_patch_ops;
3547
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003548 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003549 return 0;
3550}
Lydia Wang25eaba22009-10-10 19:08:43 +08003551
3552/* for vt2002P */
3553
Takashi Iwai096a8852011-06-20 12:09:02 +02003554static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003555 /* Class-D speaker related verbs */
3556 {0x1, 0xfe0, 0x4},
3557 {0x1, 0xfe9, 0x80},
3558 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003559 /* Enable Boost Volume backdoor */
3560 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003561 /* Enable AOW0 to MW9 */
3562 {0x1, 0xfb8, 0x88},
3563 { }
3564};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003565
Takashi Iwai096a8852011-06-20 12:09:02 +02003566static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003567 /* Enable Boost Volume backdoor */
3568 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003569 /* Enable AOW0 to MW9 */
3570 {0x1, 0xfb8, 0x88},
3571 { }
3572};
Lydia Wang25eaba22009-10-10 19:08:43 +08003573
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003574static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3575{
3576 struct via_spec *spec = codec->spec;
3577 int imux_is_smixer;
3578 unsigned int parm;
3579 unsigned int present;
3580 /* MUX9 (1eh) = stereo mixer */
3581 imux_is_smixer =
3582 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3583 /* inputs */
3584 /* PW 5/6/7 (29h/2ah/2bh) */
3585 parm = AC_PWRST_D3;
3586 set_pin_power_state(codec, 0x29, &parm);
3587 set_pin_power_state(codec, 0x2a, &parm);
3588 set_pin_power_state(codec, 0x2b, &parm);
3589 parm = AC_PWRST_D0;
3590 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003591 update_power_state(codec, 0x1e, parm);
3592 update_power_state(codec, 0x1f, parm);
3593 update_power_state(codec, 0x10, parm);
3594 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003595
3596 /* outputs */
3597 /* AOW0 (8h)*/
Takashi Iwai054d8672012-01-24 12:25:50 +01003598 update_power_state(codec, 0x8, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003599
Lydia Wang118909562011-03-23 17:57:34 +08003600 if (spec->codec_type == VT1802) {
3601 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3602 parm = AC_PWRST_D3;
3603 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003604 update_power_state(codec, 0x18, parm);
3605 update_power_state(codec, 0x38, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003606 } else {
3607 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3608 parm = AC_PWRST_D3;
3609 set_pin_power_state(codec, 0x26, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003610 update_power_state(codec, 0x1c, parm);
3611 update_power_state(codec, 0x37, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003612 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003613
Lydia Wang118909562011-03-23 17:57:34 +08003614 if (spec->codec_type == VT1802) {
3615 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3616 parm = AC_PWRST_D3;
3617 set_pin_power_state(codec, 0x25, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003618 update_power_state(codec, 0x15, parm);
3619 update_power_state(codec, 0x35, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003620 } else {
3621 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3622 parm = AC_PWRST_D3;
3623 set_pin_power_state(codec, 0x25, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003624 update_power_state(codec, 0x19, parm);
3625 update_power_state(codec, 0x35, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003626 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003627
3628 if (spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01003629 update_power_state(codec, 0x9, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003630
3631 /* Class-D */
3632 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3633 present = snd_hda_jack_detect(codec, 0x25);
3634
3635 parm = AC_PWRST_D3;
3636 set_pin_power_state(codec, 0x24, &parm);
3637 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003638 if (spec->codec_type == VT1802)
Takashi Iwai054d8672012-01-24 12:25:50 +01003639 update_power_state(codec, 0x14, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003640 else
Takashi Iwai054d8672012-01-24 12:25:50 +01003641 update_power_state(codec, 0x18, parm);
3642 update_power_state(codec, 0x34, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003643
3644 /* Mono Out */
3645 present = snd_hda_jack_detect(codec, 0x26);
3646
3647 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003648 if (spec->codec_type == VT1802) {
3649 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003650 update_power_state(codec, 0x33, parm);
3651 update_power_state(codec, 0x1c, parm);
3652 update_power_state(codec, 0x3c, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003653 } else {
3654 /* PW15 (31h), MW8(17h), MUX8(3bh) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003655 update_power_state(codec, 0x31, parm);
3656 update_power_state(codec, 0x17, parm);
3657 update_power_state(codec, 0x3b, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003658 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003659 /* MW9 (21h) */
3660 if (imux_is_smixer || !is_aa_path_mute(codec))
Takashi Iwai054d8672012-01-24 12:25:50 +01003661 update_power_state(codec, 0x21, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003662 else
Takashi Iwai054d8672012-01-24 12:25:50 +01003663 update_power_state(codec, 0x21, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003664}
Lydia Wang25eaba22009-10-10 19:08:43 +08003665
3666/* patch for vt2002P */
3667static int patch_vt2002P(struct hda_codec *codec)
3668{
3669 struct via_spec *spec;
3670 int err;
3671
3672 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003673 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003674 if (spec == NULL)
3675 return -ENOMEM;
3676
Takashi Iwai620e2b22011-06-17 17:19:19 +02003677 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003678 override_mic_boost(codec, 0x2b, 0, 3, 40);
3679 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003680 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003681
Lydia Wang25eaba22009-10-10 19:08:43 +08003682 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003683 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003684 if (err < 0) {
3685 via_free(codec);
3686 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003687 }
3688
Lydia Wang118909562011-03-23 17:57:34 +08003689 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003690 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003691 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003692 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003693
Lydia Wang25eaba22009-10-10 19:08:43 +08003694 codec->patch_ops = via_patch_ops;
3695
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003696 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003697 return 0;
3698}
Lydia Wangab6734e2009-10-10 19:08:46 +08003699
3700/* for vt1812 */
3701
Takashi Iwai096a8852011-06-20 12:09:02 +02003702static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003703 /* Enable Boost Volume backdoor */
3704 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003705 /* Enable AOW0 to MW9 */
3706 {0x1, 0xfb8, 0xa8},
3707 { }
3708};
3709
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003710static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3711{
3712 struct via_spec *spec = codec->spec;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003713 unsigned int parm;
3714 unsigned int present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003715 /* inputs */
3716 /* PW 5/6/7 (29h/2ah/2bh) */
3717 parm = AC_PWRST_D3;
3718 set_pin_power_state(codec, 0x29, &parm);
3719 set_pin_power_state(codec, 0x2a, &parm);
3720 set_pin_power_state(codec, 0x2b, &parm);
3721 parm = AC_PWRST_D0;
3722 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003723 update_power_state(codec, 0x1e, parm);
3724 update_power_state(codec, 0x1f, parm);
3725 update_power_state(codec, 0x10, parm);
3726 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003727
3728 /* outputs */
3729 /* AOW0 (8h)*/
Takashi Iwai054d8672012-01-24 12:25:50 +01003730 update_power_state(codec, 0x8, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003731
3732 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3733 parm = AC_PWRST_D3;
3734 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003735 update_power_state(codec, 0x18, parm);
3736 update_power_state(codec, 0x38, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003737
3738 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3739 parm = AC_PWRST_D3;
3740 set_pin_power_state(codec, 0x25, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003741 update_power_state(codec, 0x15, parm);
3742 update_power_state(codec, 0x35, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003743 if (spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01003744 update_power_state(codec, 0x9, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003745
3746 /* Internal Speaker */
3747 /* PW0 (24h), MW0(14h), MUX0(34h) */
3748 present = snd_hda_jack_detect(codec, 0x25);
3749
3750 parm = AC_PWRST_D3;
3751 set_pin_power_state(codec, 0x24, &parm);
3752 if (present) {
Takashi Iwai054d8672012-01-24 12:25:50 +01003753 update_power_state(codec, 0x14, AC_PWRST_D3);
3754 update_power_state(codec, 0x34, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003755 } else {
Takashi Iwai054d8672012-01-24 12:25:50 +01003756 update_power_state(codec, 0x14, AC_PWRST_D0);
3757 update_power_state(codec, 0x34, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003758 }
3759
3760
3761 /* Mono Out */
3762 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3763 present = snd_hda_jack_detect(codec, 0x28);
3764
3765 parm = AC_PWRST_D3;
3766 set_pin_power_state(codec, 0x31, &parm);
3767 if (present) {
Takashi Iwai054d8672012-01-24 12:25:50 +01003768 update_power_state(codec, 0x1c, AC_PWRST_D3);
3769 update_power_state(codec, 0x3c, AC_PWRST_D3);
3770 update_power_state(codec, 0x3e, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003771 } else {
Takashi Iwai054d8672012-01-24 12:25:50 +01003772 update_power_state(codec, 0x1c, AC_PWRST_D0);
3773 update_power_state(codec, 0x3c, AC_PWRST_D0);
3774 update_power_state(codec, 0x3e, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003775 }
3776
3777 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3778 parm = AC_PWRST_D3;
3779 set_pin_power_state(codec, 0x33, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003780 update_power_state(codec, 0x1d, parm);
3781 update_power_state(codec, 0x3d, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003782
3783}
Lydia Wangab6734e2009-10-10 19:08:46 +08003784
3785/* patch for vt1812 */
3786static int patch_vt1812(struct hda_codec *codec)
3787{
3788 struct via_spec *spec;
3789 int err;
3790
3791 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003792 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003793 if (spec == NULL)
3794 return -ENOMEM;
3795
Takashi Iwai620e2b22011-06-17 17:19:19 +02003796 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003797 override_mic_boost(codec, 0x2b, 0, 3, 40);
3798 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003799 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003800
Lydia Wangab6734e2009-10-10 19:08:46 +08003801 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003802 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003803 if (err < 0) {
3804 via_free(codec);
3805 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003806 }
3807
Takashi Iwai096a8852011-06-20 12:09:02 +02003808 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003809
Lydia Wangab6734e2009-10-10 19:08:46 +08003810 codec->patch_ops = via_patch_ops;
3811
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003812 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003813 return 0;
3814}
3815
Joseph Chanc577b8a2006-11-29 15:29:40 +01003816/*
3817 * patch entries
3818 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003819static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003820 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3821 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3822 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3823 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3824 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003825 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003826 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003827 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003828 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003829 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003830 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003831 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003832 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003833 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003834 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003835 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003836 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003837 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003838 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003839 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003840 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003841 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003842 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003843 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003844 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003845 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003846 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003847 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003848 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003849 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003850 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003851 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003852 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003853 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003854 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003855 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003856 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003857 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003858 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003859 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003860 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003861 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003862 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003863 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003864 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003865 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003866 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003867 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003868 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003869 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003870 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003871 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003872 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003873 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003874 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003875 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003876 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003877 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003878 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003879 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003880 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003881 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003882 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003883 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003884 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003885 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003886 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003887 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003888 { .id = 0x11060428, .name = "VT1718S",
3889 .patch = patch_vt1718S},
3890 { .id = 0x11064428, .name = "VT1718S",
3891 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003892 { .id = 0x11060441, .name = "VT2020",
3893 .patch = patch_vt1718S},
3894 { .id = 0x11064441, .name = "VT1828S",
3895 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003896 { .id = 0x11060433, .name = "VT1716S",
3897 .patch = patch_vt1716S},
3898 { .id = 0x1106a721, .name = "VT1716S",
3899 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003900 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3901 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003902 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003903 { .id = 0x11060440, .name = "VT1818S",
3904 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003905 { .id = 0x11060446, .name = "VT1802",
3906 .patch = patch_vt2002P},
3907 { .id = 0x11068446, .name = "VT1802",
3908 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003909 {} /* terminator */
3910};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003911
3912MODULE_ALIAS("snd-hda-codec-id:1106*");
3913
3914static struct hda_codec_preset_list via_list = {
3915 .preset = snd_hda_preset_via,
3916 .owner = THIS_MODULE,
3917};
3918
3919MODULE_LICENSE("GPL");
3920MODULE_DESCRIPTION("VIA HD-audio codec");
3921
3922static int __init patch_via_init(void)
3923{
3924 return snd_hda_add_codec_preset(&via_list);
3925}
3926
3927static void __exit patch_via_exit(void)
3928{
3929 snd_hda_delete_codec_preset(&via_list);
3930}
3931
3932module_init(patch_via_init)
3933module_exit(patch_via_exit)