blob: 9dafacdb519521c02c348212d604a6d61a544768 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Paul Gortmakerda155d52011-07-15 12:38:28 -040052#include <linux/module.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010053#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080054#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010055#include "hda_codec.h"
56#include "hda_local.h"
Takashi Iwai1835a0f2011-10-27 22:12:46 +020057#include "hda_jack.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010058
Joseph Chanc577b8a2006-11-29 15:29:40 +010059/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080060#define VT1708_HP_PIN_NID 0x20
61#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010062
Harald Welted7426322008-09-15 22:43:23 +080063enum VIA_HDA_CODEC {
64 UNKNOWN = -1,
65 VT1708,
66 VT1709_10CH,
67 VT1709_6CH,
68 VT1708B_8CH,
69 VT1708B_4CH,
70 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080071 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080072 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080073 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080074 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080075 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080076 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080077 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080078 CODEC_TYPES,
79};
80
Lydia Wang118909562011-03-23 17:57:34 +080081#define VT2002P_COMPATIBLE(spec) \
82 ((spec)->codec_type == VT2002P ||\
83 (spec)->codec_type == VT1812 ||\
84 (spec)->codec_type == VT1802)
85
Takashi Iwai8e3679d2011-06-21 09:01:36 +020086#define MAX_NID_PATH_DEPTH 5
87
Takashi Iwai09a9ad62011-06-21 15:57:44 +020088/* output-path: DAC -> ... -> pin
89 * idx[] contains the source index number of the next widget;
90 * e.g. idx[0] is the index of the DAC selected by path[1] widget
91 * multi[] indicates whether it's a selector widget with multi-connectors
92 * (i.e. the connection selection is mandatory)
93 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
94 */
Takashi Iwai4a796162011-06-17 17:53:38 +020095struct nid_path {
96 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020097 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad62011-06-21 15:57:44 +020098 unsigned char idx[MAX_NID_PATH_DEPTH];
99 unsigned char multi[MAX_NID_PATH_DEPTH];
100 unsigned int vol_ctl;
101 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200102};
103
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200104/* input-path */
105struct via_input {
106 hda_nid_t pin; /* input-pin or aa-mix */
107 int adc_idx; /* ADC index to be used */
108 int mux_idx; /* MUX index (if any) */
109 const char *label; /* input-source label */
110};
111
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200112#define VIA_MAX_ADCS 3
113
Takashi Iwai3b607e32011-07-18 16:54:40 +0200114enum {
115 STREAM_MULTI_OUT = (1 << 0),
116 STREAM_INDEP_HP = (1 << 1),
117};
118
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800119struct via_spec {
120 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200121 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800122 unsigned int num_mixers;
123
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200124 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800125 unsigned int num_iverbs;
126
Takashi Iwai82673bc2011-06-17 16:24:21 +0200127 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200128 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200129 const struct hda_pcm_stream *stream_analog_playback;
130 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800131
Takashi Iwai82673bc2011-06-17 16:24:21 +0200132 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200133 const struct hda_pcm_stream *stream_digital_playback;
134 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800135
136 /* playback */
137 struct hda_multi_out multiout;
138 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200139 hda_nid_t hp_dac_nid;
Takashi Iwai3214b962011-07-18 12:49:25 +0200140 hda_nid_t speaker_dac_nid;
141 int hp_indep_shared; /* indep HP-DAC is shared with side ch */
Takashi Iwai3b607e32011-07-18 16:54:40 +0200142 int opened_streams; /* STREAM_* bits */
143 int active_streams; /* STREAM_* bits */
Takashi Iwai3214b962011-07-18 12:49:25 +0200144 int aamix_mode; /* loopback is enabled for output-path? */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800145
Takashi Iwai3214b962011-07-18 12:49:25 +0200146 /* Output-paths:
147 * There are different output-paths depending on the setup.
148 * out_path, hp_path and speaker_path are primary paths. If both
149 * direct DAC and aa-loopback routes are available, these contain
150 * the former paths. Meanwhile *_mix_path contain the paths with
151 * loopback mixer. (Since the loopback is only for front channel,
152 * no out_mix_path for surround channels.)
153 * The HP output has another path, hp_indep_path, which is used in
154 * the independent-HP mode.
155 */
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200156 struct nid_path out_path[HDA_SIDE + 1];
Takashi Iwai3214b962011-07-18 12:49:25 +0200157 struct nid_path out_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200158 struct nid_path hp_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200159 struct nid_path hp_mix_path;
160 struct nid_path hp_indep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200161 struct nid_path speaker_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200162 struct nid_path speaker_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200163
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800164 /* capture */
165 unsigned int num_adc_nids;
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200166 hda_nid_t adc_nids[VIA_MAX_ADCS];
167 hda_nid_t mux_nids[VIA_MAX_ADCS];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200168 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800169 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800170
171 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200172 bool dyn_adc_switch;
173 int num_inputs;
174 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200175 unsigned int cur_mux[VIA_MAX_ADCS];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800176
Takashi Iwai3b607e32011-07-18 16:54:40 +0200177 /* dynamic DAC switching */
178 unsigned int cur_dac_stream_tag;
179 unsigned int cur_dac_format;
180 unsigned int cur_hp_stream_tag;
181 unsigned int cur_hp_format;
182
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200183 /* dynamic ADC switching */
184 hda_nid_t cur_adc;
185 unsigned int cur_adc_stream_tag;
186 unsigned int cur_adc_format;
187
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800188 /* PCM information */
189 struct hda_pcm pcm_rec[3];
190
191 /* dynamic controls, init_verbs and input_mux */
192 struct auto_pin_cfg autocfg;
193 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800194 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
195
196 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800197 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800198 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200199 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800200 enum VIA_HDA_CODEC codec_type;
201
Takashi Iwaie9d010c2012-02-01 10:33:23 +0100202 /* analog low-power control */
203 bool alc_mode;
204
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200205 /* smart51 setup */
206 unsigned int smart51_nums;
207 hda_nid_t smart51_pins[2];
208 int smart51_idxs[2];
209 const char *smart51_labels[2];
210 unsigned int smart51_enabled;
211
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800212 /* work to check hp jack state */
213 struct hda_codec *codec;
214 struct delayed_work vt1708_hp_work;
Takashi Iwai187d3332011-11-24 16:33:09 +0100215 int hp_work_active;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200216 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800217 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800218
219 void (*set_widgets_power_state)(struct hda_codec *codec);
220
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800221 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200222 int num_loopbacks;
223 struct hda_amp_list loopback_list[8];
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200224
225 /* bind capture-volume */
226 struct hda_bind_ctls *bind_cap_vol;
227 struct hda_bind_ctls *bind_cap_sw;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200228
229 struct mutex config_mutex;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800230};
231
Lydia Wang0341ccd2011-03-22 16:25:03 +0800232static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100233static struct via_spec * via_new_spec(struct hda_codec *codec)
234{
235 struct via_spec *spec;
236
237 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
238 if (spec == NULL)
239 return NULL;
240
Takashi Iwai3b607e32011-07-18 16:54:40 +0200241 mutex_init(&spec->config_mutex);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100242 codec->spec = spec;
243 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800244 spec->codec_type = get_codec_type(codec);
245 /* VT1708BCE & VT1708S are almost same */
246 if (spec->codec_type == VT1708BCE)
247 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100248 return spec;
249}
250
Lydia Wang744ff5f2009-10-10 19:07:26 +0800251static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800252{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800253 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800254 u16 ven_id = vendor_id >> 16;
255 u16 dev_id = vendor_id & 0xffff;
256 enum VIA_HDA_CODEC codec_type;
257
258 /* get codec type */
259 if (ven_id != 0x1106)
260 codec_type = UNKNOWN;
261 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
262 codec_type = VT1708;
263 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
264 codec_type = VT1709_10CH;
265 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
266 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800267 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800268 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800269 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
270 codec_type = VT1708BCE;
271 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800272 codec_type = VT1708B_4CH;
273 else if ((dev_id & 0xfff) == 0x397
274 && (dev_id >> 12) < 8)
275 codec_type = VT1708S;
276 else if ((dev_id & 0xfff) == 0x398
277 && (dev_id >> 12) < 8)
278 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800279 else if ((dev_id & 0xfff) == 0x428
280 && (dev_id >> 12) < 8)
281 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800282 else if (dev_id == 0x0433 || dev_id == 0xa721)
283 codec_type = VT1716S;
Lydia Wangbb3c6bf2009-10-10 19:08:39 +0800284 else if (dev_id == 0x0441 || dev_id == 0x4441)
285 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800286 else if (dev_id == 0x0438 || dev_id == 0x4438)
287 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800288 else if (dev_id == 0x0448)
289 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800290 else if (dev_id == 0x0440)
291 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800292 else if ((dev_id & 0xfff) == 0x446)
293 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800294 else
295 codec_type = UNKNOWN;
296 return codec_type;
297};
298
Lydia Wangec7e7e42011-03-24 12:43:44 +0800299#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800300#define VIA_HP_EVENT 0x01
301#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200302#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800303
Joseph Chanc577b8a2006-11-29 15:29:40 +0100304enum {
305 VIA_CTL_WIDGET_VOL,
306 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800307 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100308};
309
Takashi Iwaiada509e2011-06-20 15:40:19 +0200310static void analog_low_current_mode(struct hda_codec *codec);
311static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800312
Takashi Iwai187d3332011-11-24 16:33:09 +0100313#define hp_detect_with_aa(codec) \
314 (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
315 !is_aa_path_mute(codec))
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800316
317static void vt1708_stop_hp_work(struct via_spec *spec)
318{
319 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
320 return;
Takashi Iwai187d3332011-11-24 16:33:09 +0100321 if (spec->hp_work_active) {
322 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 1);
323 cancel_delayed_work_sync(&spec->vt1708_hp_work);
324 spec->hp_work_active = 0;
325 }
326}
327
328static void vt1708_update_hp_work(struct via_spec *spec)
329{
330 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800331 return;
Takashi Iwai187d3332011-11-24 16:33:09 +0100332 if (spec->vt1708_jack_detect &&
333 (spec->active_streams || hp_detect_with_aa(spec->codec))) {
334 if (!spec->hp_work_active) {
335 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 0);
336 schedule_delayed_work(&spec->vt1708_hp_work,
337 msecs_to_jiffies(100));
338 spec->hp_work_active = 1;
339 }
340 } else if (!hp_detect_with_aa(spec->codec))
341 vt1708_stop_hp_work(spec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800342}
Lydia Wangf5271102009-10-10 19:07:35 +0800343
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800344static void set_widgets_power_state(struct hda_codec *codec)
345{
346 struct via_spec *spec = codec->spec;
347 if (spec->set_widgets_power_state)
348 spec->set_widgets_power_state(codec);
349}
Lydia Wang25eaba22009-10-10 19:08:43 +0800350
Lydia Wangf5271102009-10-10 19:07:35 +0800351static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
352 struct snd_ctl_elem_value *ucontrol)
353{
354 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
355 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
356
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800357 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200358 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Takashi Iwai187d3332011-11-24 16:33:09 +0100359 vt1708_update_hp_work(codec->spec);
Lydia Wangf5271102009-10-10 19:07:35 +0800360 return change;
361}
362
363/* modify .put = snd_hda_mixer_amp_switch_put */
364#define ANALOG_INPUT_MUTE \
365 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
366 .name = NULL, \
367 .index = 0, \
368 .info = snd_hda_mixer_amp_switch_info, \
369 .get = snd_hda_mixer_amp_switch_get, \
370 .put = analog_input_switch_put, \
371 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
372
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200373static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100374 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
375 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800376 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100377};
378
Lydia Wangab6734e2009-10-10 19:08:46 +0800379
Joseph Chanc577b8a2006-11-29 15:29:40 +0100380/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200381static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
382 const struct snd_kcontrol_new *tmpl,
383 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100384{
385 struct snd_kcontrol_new *knew;
386
Takashi Iwai603c4012008-07-30 15:01:44 +0200387 snd_array_init(&spec->kctls, sizeof(*knew), 32);
388 knew = snd_array_new(&spec->kctls);
389 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200390 return NULL;
391 *knew = *tmpl;
392 if (!name)
393 name = tmpl->name;
394 if (name) {
395 knew->name = kstrdup(name, GFP_KERNEL);
396 if (!knew->name)
397 return NULL;
398 }
399 return knew;
400}
401
402static int __via_add_control(struct via_spec *spec, int type, const char *name,
403 int idx, unsigned long val)
404{
405 struct snd_kcontrol_new *knew;
406
407 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
408 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100409 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200410 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100411 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100412 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100413 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100414 return 0;
415}
416
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200417#define via_add_control(spec, type, name, val) \
418 __via_add_control(spec, type, name, 0, val)
419
Takashi Iwai291c9e32011-06-17 16:15:26 +0200420#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100421
Takashi Iwai603c4012008-07-30 15:01:44 +0200422static void via_free_kctls(struct hda_codec *codec)
423{
424 struct via_spec *spec = codec->spec;
425
426 if (spec->kctls.list) {
427 struct snd_kcontrol_new *kctl = spec->kctls.list;
428 int i;
429 for (i = 0; i < spec->kctls.used; i++)
430 kfree(kctl[i].name);
431 }
432 snd_array_free(&spec->kctls);
433}
434
Joseph Chanc577b8a2006-11-29 15:29:40 +0100435/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800436static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200437 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100438{
439 char name[32];
440 int err;
441
442 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200443 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100444 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
445 if (err < 0)
446 return err;
447 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200448 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100449 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
450 if (err < 0)
451 return err;
452 return 0;
453}
454
Takashi Iwai5d417622011-06-20 11:32:27 +0200455#define get_connection_index(codec, mux, nid) \
Takashi Iwai8d087c72011-06-28 12:45:47 +0200456 snd_hda_get_conn_index(codec, mux, nid, 0)
Takashi Iwai5d417622011-06-20 11:32:27 +0200457
Takashi Iwai8df2a312011-06-21 11:48:29 +0200458static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
459 unsigned int mask)
460{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200461 unsigned int caps;
462 if (!nid)
463 return false;
464 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200465 if (dir == HDA_INPUT)
466 caps &= AC_WCAP_IN_AMP;
467 else
468 caps &= AC_WCAP_OUT_AMP;
469 if (!caps)
470 return false;
471 if (query_amp_caps(codec, nid, dir) & mask)
472 return true;
473 return false;
474}
475
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200476#define have_mute(codec, nid, dir) \
477 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200478
Lydia Wangd69607b2011-07-08 14:02:52 +0800479/* enable/disable the output-route mixers */
480static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
Takashi Iwai3214b962011-07-18 12:49:25 +0200481 hda_nid_t mix_nid, int idx, bool enable)
Lydia Wangd69607b2011-07-08 14:02:52 +0800482{
483 int i, num, val;
Lydia Wangd69607b2011-07-08 14:02:52 +0800484
485 if (!path)
486 return;
487 num = snd_hda_get_conn_list(codec, mix_nid, NULL);
Lydia Wangd69607b2011-07-08 14:02:52 +0800488 for (i = 0; i < num; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +0200489 if (i == idx)
490 val = AMP_IN_UNMUTE(i);
491 else
492 val = AMP_IN_MUTE(i);
Lydia Wangd69607b2011-07-08 14:02:52 +0800493 snd_hda_codec_write(codec, mix_nid, 0,
494 AC_VERB_SET_AMP_GAIN_MUTE, val);
495 }
496}
497
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200498/* enable/disable the output-route */
499static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
500 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200501{
Lydia Wangd69607b2011-07-08 14:02:52 +0800502 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200503 int i;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200504 for (i = 0; i < path->depth; i++) {
505 hda_nid_t src, dst;
506 int idx = path->idx[i];
507 src = path->path[i];
508 if (i < path->depth - 1)
509 dst = path->path[i + 1];
510 else
511 dst = 0;
512 if (enable && path->multi[i])
513 snd_hda_codec_write(codec, dst, 0,
514 AC_VERB_SET_CONNECT_SEL, idx);
Takashi Iwai3214b962011-07-18 12:49:25 +0200515 if (!force && (dst == spec->aa_mix_nid))
Lydia Wange5e14682011-07-01 10:55:07 +0800516 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +0200517 if (have_mute(codec, dst, HDA_INPUT))
518 activate_output_mix(codec, path, dst, idx, enable);
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200519 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
520 continue;
521 if (have_mute(codec, src, HDA_OUTPUT)) {
522 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
523 snd_hda_codec_write(codec, src, 0,
524 AC_VERB_SET_AMP_GAIN_MUTE, val);
525 }
526 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200527}
528
529/* set the given pin as output */
530static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
531 int pin_type)
532{
533 if (!pin)
534 return;
535 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
536 pin_type);
537 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
538 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200539 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100540}
541
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200542static void via_auto_init_output(struct hda_codec *codec,
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200543 struct nid_path *path, int pin_type)
Takashi Iwai5d417622011-06-20 11:32:27 +0200544{
Takashi Iwai5d417622011-06-20 11:32:27 +0200545 unsigned int caps;
Lydia Wangd69607b2011-07-08 14:02:52 +0800546 hda_nid_t pin;
Takashi Iwai5d417622011-06-20 11:32:27 +0200547
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200548 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200549 return;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200550 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200551
552 init_output_pin(codec, pin, pin_type);
Takashi Iwai77e314f2012-02-22 12:34:08 +0100553 if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
554 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
555 else
556 caps = 0;
Takashi Iwai5d417622011-06-20 11:32:27 +0200557 if (caps & AC_AMPCAP_MUTE) {
558 unsigned int val;
559 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
560 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
561 AMP_OUT_MUTE | val);
562 }
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200563 activate_output_path(codec, path, true, true); /* force on */
Takashi Iwai5d417622011-06-20 11:32:27 +0200564}
565
Joseph Chanc577b8a2006-11-29 15:29:40 +0100566static void via_auto_init_multi_out(struct hda_codec *codec)
567{
568 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200569 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100570 int i;
571
Takashi Iwai3214b962011-07-18 12:49:25 +0200572 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) {
573 path = &spec->out_path[i];
574 if (!i && spec->aamix_mode && spec->out_mix_path.depth)
575 path = &spec->out_mix_path;
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200576 via_auto_init_output(codec, path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200577 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100578}
579
Takashi Iwai020066d2011-07-21 13:45:56 +0200580/* deactivate the inactive headphone-paths */
581static void deactivate_hp_paths(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100582{
583 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200584 int shared = spec->hp_indep_shared;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100585
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200586 if (spec->hp_independent_mode) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200587 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200588 activate_output_path(codec, &spec->hp_mix_path, false, false);
589 if (shared)
590 activate_output_path(codec, &spec->out_path[shared],
591 false, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200592 } else if (spec->aamix_mode || !spec->hp_path.depth) {
593 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200594 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200595 } else {
Takashi Iwai020066d2011-07-21 13:45:56 +0200596 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200597 activate_output_path(codec, &spec->hp_mix_path, false, false);
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200598 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100599}
600
Takashi Iwai020066d2011-07-21 13:45:56 +0200601static void via_auto_init_hp_out(struct hda_codec *codec)
602{
603 struct via_spec *spec = codec->spec;
604
605 if (!spec->hp_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200606 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200607 return;
608 }
609 deactivate_hp_paths(codec);
610 if (spec->hp_independent_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200611 via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200612 else if (spec->aamix_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200613 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200614 else
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200615 via_auto_init_output(codec, &spec->hp_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200616}
617
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200618static void via_auto_init_speaker_out(struct hda_codec *codec)
619{
620 struct via_spec *spec = codec->spec;
621
Takashi Iwai3214b962011-07-18 12:49:25 +0200622 if (!spec->autocfg.speaker_outs)
623 return;
624 if (!spec->speaker_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200625 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200626 return;
627 }
628 if (!spec->aamix_mode) {
629 activate_output_path(codec, &spec->speaker_mix_path,
630 false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200631 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200632 } else {
633 activate_output_path(codec, &spec->speaker_path, false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200634 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200635 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200636}
637
Takashi Iwaif4a78282011-06-17 18:46:48 +0200638static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200639static void via_hp_automute(struct hda_codec *codec);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200640
Joseph Chanc577b8a2006-11-29 15:29:40 +0100641static void via_auto_init_analog_input(struct hda_codec *codec)
642{
643 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200644 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200645 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200646 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200647 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100648
Takashi Iwai096a8852011-06-20 12:09:02 +0200649 /* init ADCs */
650 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwai77e314f2012-02-22 12:34:08 +0100651 hda_nid_t nid = spec->adc_nids[i];
652 if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP) ||
653 !(query_amp_caps(codec, nid, HDA_INPUT) & AC_AMPCAP_MUTE))
654 continue;
Takashi Iwai096a8852011-06-20 12:09:02 +0200655 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
656 AC_VERB_SET_AMP_GAIN_MUTE,
657 AMP_IN_UNMUTE(0));
658 }
659
660 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200661 for (i = 0; i < cfg->num_inputs; i++) {
662 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200663 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200664 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100665 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200666 ctl = PIN_VREF50;
667 else
668 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100669 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200670 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100671 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200672
673 /* init input-src */
674 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200675 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
Takashi Iwaifc1156c2012-02-13 15:04:06 +0100676 /* secondary ADCs must have the unique MUX */
677 if (i > 0 && !spec->mux_nids[i])
678 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200679 if (spec->mux_nids[adc_idx]) {
680 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
681 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
682 AC_VERB_SET_CONNECT_SEL,
683 mux_idx);
684 }
685 if (spec->dyn_adc_switch)
686 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200687 }
688
689 /* init aa-mixer */
690 if (!spec->aa_mix_nid)
691 return;
692 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
693 ARRAY_SIZE(conn));
694 for (i = 0; i < num_conns; i++) {
695 unsigned int caps = get_wcaps(codec, conn[i]);
696 if (get_wcaps_type(caps) == AC_WID_PIN)
697 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
698 AC_VERB_SET_AMP_GAIN_MUTE,
699 AMP_IN_MUTE(i));
700 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100701}
Lydia Wangf5271102009-10-10 19:07:35 +0800702
Takashi Iwai054d8672012-01-24 12:25:50 +0100703static void update_power_state(struct hda_codec *codec, hda_nid_t nid,
704 unsigned int parm)
705{
706 if (snd_hda_codec_read(codec, nid, 0,
707 AC_VERB_GET_POWER_STATE, 0) == parm)
708 return;
709 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
710}
711
Lydia Wangf5271102009-10-10 19:07:35 +0800712static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
713 unsigned int *affected_parm)
714{
715 unsigned parm;
716 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
717 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
718 >> AC_DEFCFG_MISC_SHIFT
719 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800720 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200721 unsigned present = 0;
722
723 no_presence |= spec->no_pin_power_ctl;
724 if (!no_presence)
725 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200726 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800727 || ((no_presence || present)
728 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800729 *affected_parm = AC_PWRST_D0; /* if it's connected */
730 parm = AC_PWRST_D0;
731 } else
732 parm = AC_PWRST_D3;
733
Takashi Iwai054d8672012-01-24 12:25:50 +0100734 update_power_state(codec, nid, parm);
Lydia Wangf5271102009-10-10 19:07:35 +0800735}
736
Takashi Iwai24088a52011-06-17 16:59:21 +0200737static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
738 struct snd_ctl_elem_info *uinfo)
739{
740 static const char * const texts[] = {
741 "Disabled", "Enabled"
742 };
743
744 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
745 uinfo->count = 1;
746 uinfo->value.enumerated.items = 2;
747 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
748 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
749 strcpy(uinfo->value.enumerated.name,
750 texts[uinfo->value.enumerated.item]);
751 return 0;
752}
753
754static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
755 struct snd_ctl_elem_value *ucontrol)
756{
757 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
758 struct via_spec *spec = codec->spec;
759 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
760 return 0;
761}
762
763static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
764 struct snd_ctl_elem_value *ucontrol)
765{
766 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
767 struct via_spec *spec = codec->spec;
768 unsigned int val = !ucontrol->value.enumerated.item[0];
769
770 if (val == spec->no_pin_power_ctl)
771 return 0;
772 spec->no_pin_power_ctl = val;
773 set_widgets_power_state(codec);
Takashi Iwaie9d010c2012-02-01 10:33:23 +0100774 analog_low_current_mode(codec);
Takashi Iwai24088a52011-06-17 16:59:21 +0200775 return 1;
776}
777
778static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
779 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
780 .name = "Dynamic Power-Control",
781 .info = via_pin_power_ctl_info,
782 .get = via_pin_power_ctl_get,
783 .put = via_pin_power_ctl_put,
784};
785
786
Harald Welte0aa62ae2008-09-09 15:58:27 +0800787static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
788 struct snd_ctl_elem_info *uinfo)
789{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200790 static const char * const texts[] = { "OFF", "ON" };
791
792 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
793 uinfo->count = 1;
794 uinfo->value.enumerated.items = 2;
795 if (uinfo->value.enumerated.item >= 2)
796 uinfo->value.enumerated.item = 1;
797 strcpy(uinfo->value.enumerated.name,
798 texts[uinfo->value.enumerated.item]);
799 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800800}
801
802static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
803 struct snd_ctl_elem_value *ucontrol)
804{
805 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800806 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800807
Takashi Iwaiece8d042011-06-19 16:24:21 +0200808 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800809 return 0;
810}
811
Takashi Iwai3b607e32011-07-18 16:54:40 +0200812/* adjust spec->multiout setup according to the current flags */
813static void setup_playback_multi_pcm(struct via_spec *spec)
814{
815 const struct auto_pin_cfg *cfg = &spec->autocfg;
816 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
817 spec->multiout.hp_nid = 0;
818 if (!spec->hp_independent_mode) {
819 if (!spec->hp_indep_shared)
820 spec->multiout.hp_nid = spec->hp_dac_nid;
821 } else {
822 if (spec->hp_indep_shared)
823 spec->multiout.num_dacs = cfg->line_outs - 1;
824 }
825}
826
827/* update DAC setups according to indep-HP switch;
828 * this function is called only when indep-HP is modified
829 */
830static void switch_indep_hp_dacs(struct hda_codec *codec)
831{
832 struct via_spec *spec = codec->spec;
833 int shared = spec->hp_indep_shared;
834 hda_nid_t shared_dac, hp_dac;
835
836 if (!spec->opened_streams)
837 return;
838
839 shared_dac = shared ? spec->multiout.dac_nids[shared] : 0;
840 hp_dac = spec->hp_dac_nid;
841 if (spec->hp_independent_mode) {
842 /* switch to indep-HP mode */
843 if (spec->active_streams & STREAM_MULTI_OUT) {
844 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
845 __snd_hda_codec_cleanup_stream(codec, shared_dac, 1);
846 }
847 if (spec->active_streams & STREAM_INDEP_HP)
848 snd_hda_codec_setup_stream(codec, hp_dac,
849 spec->cur_hp_stream_tag, 0,
850 spec->cur_hp_format);
851 } else {
852 /* back to HP or shared-DAC */
853 if (spec->active_streams & STREAM_INDEP_HP)
854 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
855 if (spec->active_streams & STREAM_MULTI_OUT) {
856 hda_nid_t dac;
857 int ch;
858 if (shared_dac) { /* reset mutli-ch DAC */
859 dac = shared_dac;
860 ch = shared * 2;
861 } else { /* reset HP DAC */
862 dac = hp_dac;
863 ch = 0;
864 }
865 snd_hda_codec_setup_stream(codec, dac,
866 spec->cur_dac_stream_tag, ch,
867 spec->cur_dac_format);
868 }
869 }
870 setup_playback_multi_pcm(spec);
871}
872
Harald Welte0aa62ae2008-09-09 15:58:27 +0800873static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
874 struct snd_ctl_elem_value *ucontrol)
875{
876 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
877 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200878 int cur, shared;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200879
Takashi Iwai3b607e32011-07-18 16:54:40 +0200880 mutex_lock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200881 cur = !!ucontrol->value.enumerated.item[0];
Takashi Iwai3b607e32011-07-18 16:54:40 +0200882 if (spec->hp_independent_mode == cur) {
883 mutex_unlock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200884 return 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200885 }
Takashi Iwai25250502011-06-30 17:24:47 +0200886 spec->hp_independent_mode = cur;
Takashi Iwai3214b962011-07-18 12:49:25 +0200887 shared = spec->hp_indep_shared;
Takashi Iwai020066d2011-07-21 13:45:56 +0200888 deactivate_hp_paths(codec);
889 if (cur)
890 activate_output_path(codec, &spec->hp_indep_path, true, false);
891 else {
Takashi Iwai3214b962011-07-18 12:49:25 +0200892 if (shared)
893 activate_output_path(codec, &spec->out_path[shared],
Takashi Iwai25250502011-06-30 17:24:47 +0200894 true, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200895 if (spec->aamix_mode || !spec->hp_path.depth)
896 activate_output_path(codec, &spec->hp_mix_path,
897 true, false);
898 else
899 activate_output_path(codec, &spec->hp_path,
900 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200901 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800902
Takashi Iwai3b607e32011-07-18 16:54:40 +0200903 switch_indep_hp_dacs(codec);
904 mutex_unlock(&spec->config_mutex);
905
Lydia Wangce0e5a92011-03-22 16:22:37 +0800906 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800907 set_widgets_power_state(codec);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200908 via_hp_automute(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200909 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800910}
911
Takashi Iwaiece8d042011-06-19 16:24:21 +0200912static const struct snd_kcontrol_new via_hp_mixer = {
913 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
914 .name = "Independent HP",
915 .info = via_independent_hp_info,
916 .get = via_independent_hp_get,
917 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800918};
919
Takashi Iwai3d83e572010-04-14 14:36:23 +0200920static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100921{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200922 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100923 struct snd_kcontrol_new *knew;
924 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100925
Takashi Iwaiece8d042011-06-19 16:24:21 +0200926 nid = spec->autocfg.hp_pins[0];
927 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200928 if (knew == NULL)
929 return -ENOMEM;
930
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100931 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100932
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100933 return 0;
934}
935
Lydia Wang1564b282009-10-10 19:07:52 +0800936static void notify_aa_path_ctls(struct hda_codec *codec)
937{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200938 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800939 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800940
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200941 for (i = 0; i < spec->smart51_nums; i++) {
942 struct snd_kcontrol *ctl;
943 struct snd_ctl_elem_id id;
944 memset(&id, 0, sizeof(id));
945 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
946 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800947 ctl = snd_hda_find_mixer_ctl(codec, id.name);
948 if (ctl)
949 snd_ctl_notify(codec->bus->card,
950 SNDRV_CTL_EVENT_MASK_VALUE,
951 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800952 }
953}
954
955static void mute_aa_path(struct hda_codec *codec, int mute)
956{
957 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200958 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800959 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200960
Lydia Wang1564b282009-10-10 19:07:52 +0800961 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200962 for (i = 0; i < spec->smart51_nums; i++) {
963 if (spec->smart51_idxs[i] < 0)
964 continue;
965 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
966 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800967 HDA_AMP_MUTE, val);
968 }
969}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200970
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200971static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
972{
973 struct via_spec *spec = codec->spec;
974 int i;
975
976 for (i = 0; i < spec->smart51_nums; i++)
977 if (spec->smart51_pins[i] == pin)
978 return true;
979 return false;
980}
981
Lydia Wang1564b282009-10-10 19:07:52 +0800982static int via_smart51_get(struct snd_kcontrol *kcontrol,
983 struct snd_ctl_elem_value *ucontrol)
984{
985 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
986 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800987
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200988 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800989 return 0;
990}
991
992static int via_smart51_put(struct snd_kcontrol *kcontrol,
993 struct snd_ctl_elem_value *ucontrol)
994{
995 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
996 struct via_spec *spec = codec->spec;
997 int out_in = *ucontrol->value.integer.value
998 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800999 int i;
1000
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001001 for (i = 0; i < spec->smart51_nums; i++) {
1002 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001003 unsigned int parm;
1004
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001005 parm = snd_hda_codec_read(codec, nid, 0,
1006 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1007 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
1008 parm |= out_in;
1009 snd_hda_codec_write(codec, nid, 0,
1010 AC_VERB_SET_PIN_WIDGET_CONTROL,
1011 parm);
1012 if (out_in == AC_PINCTL_OUT_EN) {
1013 mute_aa_path(codec, 1);
1014 notify_aa_path_ctls(codec);
1015 }
Lydia Wang1564b282009-10-10 19:07:52 +08001016 }
1017 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001018 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +08001019 return 1;
1020}
1021
Takashi Iwai5f4b36d2011-06-17 14:55:02 +02001022static const struct snd_kcontrol_new via_smart51_mixer = {
1023 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1024 .name = "Smart 5.1",
1025 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001026 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +02001027 .get = via_smart51_get,
1028 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +08001029};
1030
Takashi Iwaif4a78282011-06-17 18:46:48 +02001031static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001032{
Takashi Iwaif4a78282011-06-17 18:46:48 +02001033 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001034
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001035 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +08001036 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001037 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001038 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001039 return 0;
1040}
1041
Takashi Iwaiada509e2011-06-20 15:40:19 +02001042/* check AA path's mute status */
1043static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001044{
Lydia Wangf5271102009-10-10 19:07:35 +08001045 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001046 const struct hda_amp_list *p;
1047 int i, ch, v;
1048
1049 for (i = 0; i < spec->num_loopbacks; i++) {
1050 p = &spec->loopback_list[i];
1051 for (ch = 0; ch < 2; ch++) {
1052 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
1053 p->idx);
1054 if (!(v & HDA_AMP_MUTE) && v > 0)
1055 return false;
Lydia Wangf5271102009-10-10 19:07:35 +08001056 }
1057 }
Takashi Iwaiada509e2011-06-20 15:40:19 +02001058 return true;
Lydia Wangf5271102009-10-10 19:07:35 +08001059}
1060
1061/* enter/exit analog low-current mode */
Takashi Iwaie9d010c2012-02-01 10:33:23 +01001062static void __analog_low_current_mode(struct hda_codec *codec, bool force)
Lydia Wangf5271102009-10-10 19:07:35 +08001063{
1064 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001065 bool enable;
1066 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +08001067
Takashi Iwaie9d010c2012-02-01 10:33:23 +01001068 if (spec->no_pin_power_ctl)
1069 enable = false;
1070 else
1071 enable = is_aa_path_mute(codec) && !spec->opened_streams;
1072 if (enable == spec->alc_mode && !force)
1073 return;
1074 spec->alc_mode = enable;
Lydia Wangf5271102009-10-10 19:07:35 +08001075
1076 /* decide low current mode's verb & parameter */
1077 switch (spec->codec_type) {
1078 case VT1708B_8CH:
1079 case VT1708B_4CH:
1080 verb = 0xf70;
1081 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1082 break;
1083 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001084 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001085 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001086 verb = 0xf73;
1087 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1088 break;
1089 case VT1702:
1090 verb = 0xf73;
1091 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1092 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001093 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001094 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +08001095 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +08001096 verb = 0xf93;
1097 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1098 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001099 default:
1100 return; /* other codecs are not supported */
1101 }
1102 /* send verb */
1103 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1104}
1105
Takashi Iwaie9d010c2012-02-01 10:33:23 +01001106static void analog_low_current_mode(struct hda_codec *codec)
1107{
1108 return __analog_low_current_mode(codec, false);
1109}
1110
Joseph Chanc577b8a2006-11-29 15:29:40 +01001111/*
1112 * generic initialization of ADC, input mixers and output mixers
1113 */
Takashi Iwai096a8852011-06-20 12:09:02 +02001114static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001115 /* power down jack detect function */
1116 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001117 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001118};
1119
Takashi Iwai3b607e32011-07-18 16:54:40 +02001120static void set_stream_open(struct hda_codec *codec, int bit, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001121{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001122 struct via_spec *spec = codec->spec;
1123
1124 if (active)
Takashi Iwai3b607e32011-07-18 16:54:40 +02001125 spec->opened_streams |= bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001126 else
Takashi Iwai3b607e32011-07-18 16:54:40 +02001127 spec->opened_streams &= ~bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001128 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001129}
1130
Takashi Iwaiece8d042011-06-19 16:24:21 +02001131static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001132 struct hda_codec *codec,
1133 struct snd_pcm_substream *substream)
1134{
1135 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001136 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001137 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001138
Takashi Iwai25250502011-06-30 17:24:47 +02001139 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
Takashi Iwai25250502011-06-30 17:24:47 +02001140 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001141 set_stream_open(codec, STREAM_MULTI_OUT, true);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001142 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1143 hinfo);
1144 if (err < 0) {
Takashi Iwai3b607e32011-07-18 16:54:40 +02001145 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001146 return err;
1147 }
1148 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001149}
1150
Takashi Iwaiece8d042011-06-19 16:24:21 +02001151static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001152 struct hda_codec *codec,
1153 struct snd_pcm_substream *substream)
1154{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001155 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001156 return 0;
1157}
1158
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001159static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1160 struct hda_codec *codec,
1161 struct snd_pcm_substream *substream)
1162{
1163 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001164
Takashi Iwaiece8d042011-06-19 16:24:21 +02001165 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001166 return -EINVAL;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001167 set_stream_open(codec, STREAM_INDEP_HP, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001168 return 0;
1169}
1170
1171static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1172 struct hda_codec *codec,
1173 struct snd_pcm_substream *substream)
1174{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001175 set_stream_open(codec, STREAM_INDEP_HP, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001176 return 0;
1177}
1178
1179static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1180 struct hda_codec *codec,
1181 unsigned int stream_tag,
1182 unsigned int format,
1183 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001184{
1185 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001186
Takashi Iwai3b607e32011-07-18 16:54:40 +02001187 mutex_lock(&spec->config_mutex);
1188 setup_playback_multi_pcm(spec);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001189 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1190 format, substream);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001191 /* remember for dynamic DAC switch with indep-HP */
1192 spec->active_streams |= STREAM_MULTI_OUT;
1193 spec->cur_dac_stream_tag = stream_tag;
1194 spec->cur_dac_format = format;
1195 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001196 vt1708_update_hp_work(spec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001197 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001198}
1199
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001200static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1201 struct hda_codec *codec,
1202 unsigned int stream_tag,
1203 unsigned int format,
1204 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001205{
1206 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001207
Takashi Iwai3b607e32011-07-18 16:54:40 +02001208 mutex_lock(&spec->config_mutex);
1209 if (spec->hp_independent_mode)
1210 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1211 stream_tag, 0, format);
1212 spec->active_streams |= STREAM_INDEP_HP;
1213 spec->cur_hp_stream_tag = stream_tag;
1214 spec->cur_hp_format = format;
1215 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001216 vt1708_update_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001217 return 0;
1218}
1219
1220static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1221 struct hda_codec *codec,
1222 struct snd_pcm_substream *substream)
1223{
1224 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001225
Takashi Iwai3b607e32011-07-18 16:54:40 +02001226 mutex_lock(&spec->config_mutex);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001227 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001228 spec->active_streams &= ~STREAM_MULTI_OUT;
1229 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001230 vt1708_update_hp_work(spec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001231 return 0;
1232}
1233
1234static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1235 struct hda_codec *codec,
1236 struct snd_pcm_substream *substream)
1237{
1238 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001239
Takashi Iwai3b607e32011-07-18 16:54:40 +02001240 mutex_lock(&spec->config_mutex);
1241 if (spec->hp_independent_mode)
1242 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
1243 spec->active_streams &= ~STREAM_INDEP_HP;
1244 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001245 vt1708_update_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001246 return 0;
1247}
1248
Joseph Chanc577b8a2006-11-29 15:29:40 +01001249/*
1250 * Digital out
1251 */
1252static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1253 struct hda_codec *codec,
1254 struct snd_pcm_substream *substream)
1255{
1256 struct via_spec *spec = codec->spec;
1257 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1258}
1259
1260static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1261 struct hda_codec *codec,
1262 struct snd_pcm_substream *substream)
1263{
1264 struct via_spec *spec = codec->spec;
1265 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1266}
1267
Harald Welte5691ec72008-09-15 22:42:26 +08001268static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001269 struct hda_codec *codec,
1270 unsigned int stream_tag,
1271 unsigned int format,
1272 struct snd_pcm_substream *substream)
1273{
1274 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001275 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1276 stream_tag, format, substream);
1277}
Harald Welte5691ec72008-09-15 22:42:26 +08001278
Takashi Iwai9da29272009-05-07 16:31:14 +02001279static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1280 struct hda_codec *codec,
1281 struct snd_pcm_substream *substream)
1282{
1283 struct via_spec *spec = codec->spec;
1284 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001285 return 0;
1286}
1287
Joseph Chanc577b8a2006-11-29 15:29:40 +01001288/*
1289 * Analog capture
1290 */
1291static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1292 struct hda_codec *codec,
1293 unsigned int stream_tag,
1294 unsigned int format,
1295 struct snd_pcm_substream *substream)
1296{
1297 struct via_spec *spec = codec->spec;
1298
1299 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1300 stream_tag, 0, format);
1301 return 0;
1302}
1303
1304static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1305 struct hda_codec *codec,
1306 struct snd_pcm_substream *substream)
1307{
1308 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001309 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001310 return 0;
1311}
1312
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001313/* analog capture with dynamic ADC switching */
1314static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1315 struct hda_codec *codec,
1316 unsigned int stream_tag,
1317 unsigned int format,
1318 struct snd_pcm_substream *substream)
1319{
1320 struct via_spec *spec = codec->spec;
1321 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1322
Takashi Iwai3b607e32011-07-18 16:54:40 +02001323 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001324 spec->cur_adc = spec->adc_nids[adc_idx];
1325 spec->cur_adc_stream_tag = stream_tag;
1326 spec->cur_adc_format = format;
1327 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001328 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001329 return 0;
1330}
1331
1332static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1333 struct hda_codec *codec,
1334 struct snd_pcm_substream *substream)
1335{
1336 struct via_spec *spec = codec->spec;
1337
Takashi Iwai3b607e32011-07-18 16:54:40 +02001338 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001339 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1340 spec->cur_adc = 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001341 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001342 return 0;
1343}
1344
1345/* re-setup the stream if running; called from input-src put */
1346static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1347{
1348 struct via_spec *spec = codec->spec;
1349 int adc_idx = spec->inputs[cur].adc_idx;
1350 hda_nid_t adc = spec->adc_nids[adc_idx];
Takashi Iwai3b607e32011-07-18 16:54:40 +02001351 bool ret = false;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001352
Takashi Iwai3b607e32011-07-18 16:54:40 +02001353 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001354 if (spec->cur_adc && spec->cur_adc != adc) {
1355 /* stream is running, let's swap the current ADC */
1356 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1357 spec->cur_adc = adc;
1358 snd_hda_codec_setup_stream(codec, adc,
1359 spec->cur_adc_stream_tag, 0,
1360 spec->cur_adc_format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001361 ret = true;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001362 }
Takashi Iwai3b607e32011-07-18 16:54:40 +02001363 mutex_unlock(&spec->config_mutex);
1364 return ret;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001365}
1366
Takashi Iwai9af74212011-06-18 16:17:45 +02001367static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001368 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001369 .channels_min = 2,
1370 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001371 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001372 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001373 .open = via_playback_multi_pcm_open,
1374 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001375 .prepare = via_playback_multi_pcm_prepare,
1376 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001377 },
1378};
1379
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001380static const struct hda_pcm_stream via_pcm_hp_playback = {
1381 .substreams = 1,
1382 .channels_min = 2,
1383 .channels_max = 2,
1384 /* NID is set in via_build_pcms */
1385 .ops = {
1386 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001387 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001388 .prepare = via_playback_hp_pcm_prepare,
1389 .cleanup = via_playback_hp_pcm_cleanup
1390 },
1391};
1392
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001393static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001394 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001395 .channels_min = 2,
1396 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001397 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001398 /* We got noisy outputs on the right channel on VT1708 when
1399 * 24bit samples are used. Until any workaround is found,
1400 * disable the 24bit format, so far.
1401 */
1402 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1403 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001404 .open = via_playback_multi_pcm_open,
1405 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001406 .prepare = via_playback_multi_pcm_prepare,
1407 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001408 },
1409};
1410
Takashi Iwai9af74212011-06-18 16:17:45 +02001411static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001412 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001413 .channels_min = 2,
1414 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001415 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001416 .ops = {
1417 .prepare = via_capture_pcm_prepare,
1418 .cleanup = via_capture_pcm_cleanup
1419 },
1420};
1421
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001422static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1423 .substreams = 1,
1424 .channels_min = 2,
1425 .channels_max = 2,
1426 /* NID is set in via_build_pcms */
1427 .ops = {
1428 .prepare = via_dyn_adc_capture_pcm_prepare,
1429 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1430 },
1431};
1432
Takashi Iwai9af74212011-06-18 16:17:45 +02001433static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001434 .substreams = 1,
1435 .channels_min = 2,
1436 .channels_max = 2,
1437 /* NID is set in via_build_pcms */
1438 .ops = {
1439 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001440 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001441 .prepare = via_dig_playback_pcm_prepare,
1442 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001443 },
1444};
1445
Takashi Iwai9af74212011-06-18 16:17:45 +02001446static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001447 .substreams = 1,
1448 .channels_min = 2,
1449 .channels_max = 2,
1450};
1451
Takashi Iwai370bafb2011-06-20 12:47:45 +02001452/*
1453 * slave controls for virtual master
1454 */
Takashi Iwai9322ca52012-02-03 14:28:01 +01001455static const char * const via_slave_pfxs[] = {
1456 "Front", "Surround", "Center", "LFE", "Side",
1457 "Headphone", "Speaker",
Takashi Iwai370bafb2011-06-20 12:47:45 +02001458 NULL,
1459};
1460
Joseph Chanc577b8a2006-11-29 15:29:40 +01001461static int via_build_controls(struct hda_codec *codec)
1462{
1463 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001464 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001465 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001466
Takashi Iwaib5bcc182012-02-02 10:30:17 +01001467 spec->no_pin_power_ctl = 1;
Takashi Iwai24088a52011-06-17 16:59:21 +02001468 if (spec->set_widgets_power_state)
1469 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1470 return -ENOMEM;
1471
Joseph Chanc577b8a2006-11-29 15:29:40 +01001472 for (i = 0; i < spec->num_mixers; i++) {
1473 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1474 if (err < 0)
1475 return err;
1476 }
1477
1478 if (spec->multiout.dig_out_nid) {
1479 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001480 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001481 spec->multiout.dig_out_nid);
1482 if (err < 0)
1483 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001484 err = snd_hda_create_spdif_share_sw(codec,
1485 &spec->multiout);
1486 if (err < 0)
1487 return err;
1488 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001489 }
1490 if (spec->dig_in_nid) {
1491 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1492 if (err < 0)
1493 return err;
1494 }
Lydia Wang17314372009-10-10 19:07:37 +08001495
Takashi Iwai370bafb2011-06-20 12:47:45 +02001496 /* if we have no master control, let's create it */
1497 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1498 unsigned int vmaster_tlv[4];
1499 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1500 HDA_OUTPUT, vmaster_tlv);
1501 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
Takashi Iwai9322ca52012-02-03 14:28:01 +01001502 vmaster_tlv, via_slave_pfxs,
1503 "Playback Volume");
Takashi Iwai370bafb2011-06-20 12:47:45 +02001504 if (err < 0)
1505 return err;
1506 }
1507 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1508 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
Takashi Iwai9322ca52012-02-03 14:28:01 +01001509 NULL, via_slave_pfxs,
1510 "Playback Switch");
Takashi Iwai370bafb2011-06-20 12:47:45 +02001511 if (err < 0)
1512 return err;
1513 }
1514
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001515 /* assign Capture Source enums to NID */
1516 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1517 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai77e314f2012-02-22 12:34:08 +01001518 if (!spec->mux_nids[i])
1519 continue;
Takashi Iwai21949f02009-12-23 08:31:59 +01001520 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001521 if (err < 0)
1522 return err;
1523 }
1524
Takashi Iwai603c4012008-07-30 15:01:44 +02001525 via_free_kctls(codec); /* no longer needed */
Takashi Iwai01a61e12011-10-28 00:03:22 +02001526
1527 err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
1528 if (err < 0)
1529 return err;
1530
Joseph Chanc577b8a2006-11-29 15:29:40 +01001531 return 0;
1532}
1533
1534static int via_build_pcms(struct hda_codec *codec)
1535{
1536 struct via_spec *spec = codec->spec;
1537 struct hda_pcm *info = spec->pcm_rec;
1538
Takashi Iwaia5973102011-09-28 16:43:36 +02001539 codec->num_pcms = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001540 codec->pcm_info = info;
1541
Takashi Iwaia5973102011-09-28 16:43:36 +02001542 if (spec->multiout.num_dacs || spec->num_adc_nids) {
1543 snprintf(spec->stream_name_analog,
1544 sizeof(spec->stream_name_analog),
1545 "%s Analog", codec->chip_name);
1546 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001547
Takashi Iwaia5973102011-09-28 16:43:36 +02001548 if (spec->multiout.num_dacs) {
1549 if (!spec->stream_analog_playback)
1550 spec->stream_analog_playback =
1551 &via_pcm_analog_playback;
1552 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1553 *spec->stream_analog_playback;
1554 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1555 spec->multiout.dac_nids[0];
1556 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1557 spec->multiout.max_channels;
1558 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001559
Takashi Iwaia5973102011-09-28 16:43:36 +02001560 if (!spec->stream_analog_capture) {
1561 if (spec->dyn_adc_switch)
1562 spec->stream_analog_capture =
1563 &via_pcm_dyn_adc_analog_capture;
1564 else
1565 spec->stream_analog_capture =
1566 &via_pcm_analog_capture;
1567 }
1568 if (spec->num_adc_nids) {
1569 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1570 *spec->stream_analog_capture;
1571 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1572 spec->adc_nids[0];
1573 if (!spec->dyn_adc_switch)
1574 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1575 spec->num_adc_nids;
1576 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001577 codec->num_pcms++;
1578 info++;
Takashi Iwaia5973102011-09-28 16:43:36 +02001579 }
1580
1581 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
Takashi Iwai82673bc2011-06-17 16:24:21 +02001582 snprintf(spec->stream_name_digital,
1583 sizeof(spec->stream_name_digital),
1584 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001585 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001586 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001587 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001588 if (!spec->stream_digital_playback)
1589 spec->stream_digital_playback =
1590 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001591 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001592 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001593 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1594 spec->multiout.dig_out_nid;
1595 }
1596 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001597 if (!spec->stream_digital_capture)
1598 spec->stream_digital_capture =
1599 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001600 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001601 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001602 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1603 spec->dig_in_nid;
1604 }
Takashi Iwaia5973102011-09-28 16:43:36 +02001605 codec->num_pcms++;
1606 info++;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001607 }
1608
Takashi Iwaiece8d042011-06-19 16:24:21 +02001609 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001610 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1611 "%s HP", codec->chip_name);
1612 info->name = spec->stream_name_hp;
1613 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1614 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001615 spec->hp_dac_nid;
Takashi Iwaia5973102011-09-28 16:43:36 +02001616 codec->num_pcms++;
1617 info++;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001618 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001619 return 0;
1620}
1621
1622static void via_free(struct hda_codec *codec)
1623{
1624 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001625
1626 if (!spec)
1627 return;
1628
Takashi Iwai603c4012008-07-30 15:01:44 +02001629 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001630 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001631 kfree(spec->bind_cap_vol);
1632 kfree(spec->bind_cap_sw);
1633 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001634}
1635
Takashi Iwai64be2852011-06-17 16:51:39 +02001636/* mute/unmute outputs */
1637static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1638 hda_nid_t *pins, bool mute)
1639{
1640 int i;
Takashi Iwai94994732011-07-11 11:36:44 +02001641 for (i = 0; i < num_pins; i++) {
1642 unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
1643 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1644 if (parm & AC_PINCTL_IN_EN)
1645 continue;
1646 if (mute)
1647 parm &= ~AC_PINCTL_OUT_EN;
1648 else
1649 parm |= AC_PINCTL_OUT_EN;
Takashi Iwai64be2852011-06-17 16:51:39 +02001650 snd_hda_codec_write(codec, pins[i], 0,
Takashi Iwai94994732011-07-11 11:36:44 +02001651 AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
1652 }
Takashi Iwai64be2852011-06-17 16:51:39 +02001653}
1654
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001655/* mute internal speaker if line-out is plugged */
1656static void via_line_automute(struct hda_codec *codec, int present)
1657{
1658 struct via_spec *spec = codec->spec;
1659
1660 if (!spec->autocfg.speaker_outs)
1661 return;
1662 if (!present)
1663 present = snd_hda_jack_detect(codec,
1664 spec->autocfg.line_out_pins[0]);
1665 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1666 spec->autocfg.speaker_pins,
1667 present);
1668}
1669
Harald Welte69e52a82008-09-09 15:57:32 +08001670/* mute internal speaker if HP is plugged */
1671static void via_hp_automute(struct hda_codec *codec)
1672{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001673 int present = 0;
Takashi Iwai6e969d92011-07-11 11:28:13 +02001674 int nums;
Harald Welte69e52a82008-09-09 15:57:32 +08001675 struct via_spec *spec = codec->spec;
1676
Takashi Iwai187d3332011-11-24 16:33:09 +01001677 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0] &&
1678 (spec->codec_type != VT1708 || spec->vt1708_jack_detect))
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001679 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai6e969d92011-07-11 11:28:13 +02001680
1681 if (spec->smart51_enabled)
1682 nums = spec->autocfg.line_outs + spec->smart51_nums;
1683 else
1684 nums = spec->autocfg.line_outs;
1685 toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
1686
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001687 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001688}
1689
Harald Welte69e52a82008-09-09 15:57:32 +08001690static void via_gpio_control(struct hda_codec *codec)
1691{
1692 unsigned int gpio_data;
1693 unsigned int vol_counter;
1694 unsigned int vol;
1695 unsigned int master_vol;
1696
1697 struct via_spec *spec = codec->spec;
1698
1699 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1700 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1701
1702 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1703 0xF84, 0) & 0x3F0000) >> 16;
1704
1705 vol = vol_counter & 0x1F;
1706 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1707 AC_VERB_GET_AMP_GAIN_MUTE,
1708 AC_AMP_GET_INPUT);
1709
1710 if (gpio_data == 0x02) {
1711 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001712 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1713 AC_VERB_SET_PIN_WIDGET_CONTROL,
1714 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001715 if (vol_counter & 0x20) {
1716 /* decrease volume */
1717 if (vol > master_vol)
1718 vol = master_vol;
1719 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1720 0, HDA_AMP_VOLMASK,
1721 master_vol-vol);
1722 } else {
1723 /* increase volume */
1724 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1725 HDA_AMP_VOLMASK,
1726 ((master_vol+vol) > 0x2A) ? 0x2A :
1727 (master_vol+vol));
1728 }
1729 } else if (!(gpio_data & 0x02)) {
1730 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001731 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1732 AC_VERB_SET_PIN_WIDGET_CONTROL,
1733 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001734 }
1735}
1736
1737/* unsolicited event for jack sensing */
1738static void via_unsol_event(struct hda_codec *codec,
1739 unsigned int res)
1740{
1741 res >>= 26;
Takashi Iwai3a938972011-10-28 01:16:55 +02001742 res = snd_hda_jack_get_action(codec, res);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001743
Lydia Wanga34df192009-10-10 19:08:01 +08001744 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001745 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001746
1747 res &= ~VIA_JACK_EVENT;
1748
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001749 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001750 via_hp_automute(codec);
1751 else if (res == VIA_GPIO_EVENT)
1752 via_gpio_control(codec);
Takashi Iwai01a61e12011-10-28 00:03:22 +02001753 snd_hda_jack_report_sync(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001754}
1755
Takashi Iwai2a439522011-07-26 09:52:50 +02001756#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001757static int via_suspend(struct hda_codec *codec, pm_message_t state)
1758{
1759 struct via_spec *spec = codec->spec;
1760 vt1708_stop_hp_work(spec);
1761 return 0;
1762}
1763#endif
1764
Takashi Iwaicb53c622007-08-10 17:21:45 +02001765#ifdef CONFIG_SND_HDA_POWER_SAVE
1766static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1767{
1768 struct via_spec *spec = codec->spec;
1769 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1770}
1771#endif
1772
Joseph Chanc577b8a2006-11-29 15:29:40 +01001773/*
1774 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001775
1776static int via_init(struct hda_codec *codec);
1777
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001778static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001779 .build_controls = via_build_controls,
1780 .build_pcms = via_build_pcms,
1781 .init = via_init,
1782 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001783 .unsol_event = via_unsol_event,
Takashi Iwai2a439522011-07-26 09:52:50 +02001784#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001785 .suspend = via_suspend,
1786#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001787#ifdef CONFIG_SND_HDA_POWER_SAVE
1788 .check_power_status = via_check_power_status,
1789#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001790};
1791
Takashi Iwai4a796162011-06-17 17:53:38 +02001792static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001793{
Takashi Iwai4a796162011-06-17 17:53:38 +02001794 struct via_spec *spec = codec->spec;
1795 int i;
1796
1797 for (i = 0; i < spec->multiout.num_dacs; i++) {
1798 if (spec->multiout.dac_nids[i] == dac)
1799 return false;
1800 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001801 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001802 return false;
1803 return true;
1804}
1805
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001806static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001807 hda_nid_t target_dac, int with_aa_mix,
1808 struct nid_path *path, int depth)
Takashi Iwai4a796162011-06-17 17:53:38 +02001809{
Takashi Iwai3214b962011-07-18 12:49:25 +02001810 struct via_spec *spec = codec->spec;
Takashi Iwai4a796162011-06-17 17:53:38 +02001811 hda_nid_t conn[8];
1812 int i, nums;
1813
Takashi Iwai3214b962011-07-18 12:49:25 +02001814 if (nid == spec->aa_mix_nid) {
1815 if (!with_aa_mix)
1816 return false;
1817 with_aa_mix = 2; /* mark aa-mix is included */
1818 }
1819
Takashi Iwai4a796162011-06-17 17:53:38 +02001820 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1821 for (i = 0; i < nums; i++) {
1822 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1823 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001824 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1825 /* aa-mix is requested but not included? */
1826 if (!(spec->aa_mix_nid && with_aa_mix == 1))
1827 goto found;
1828 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001829 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001830 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001831 return false;
1832 for (i = 0; i < nums; i++) {
1833 unsigned int type;
1834 type = get_wcaps_type(get_wcaps(codec, conn[i]));
Takashi Iwai3214b962011-07-18 12:49:25 +02001835 if (type == AC_WID_AUD_OUT)
Takashi Iwai4a796162011-06-17 17:53:38 +02001836 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001837 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai3214b962011-07-18 12:49:25 +02001838 with_aa_mix, path, depth + 1))
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001839 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001840 }
1841 return false;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001842
1843 found:
1844 path->path[path->depth] = conn[i];
1845 path->idx[path->depth] = i;
1846 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1847 path->multi[path->depth] = 1;
1848 path->depth++;
1849 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001850}
1851
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001852static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001853 hda_nid_t target_dac, int with_aa_mix,
1854 struct nid_path *path)
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001855{
Takashi Iwai3214b962011-07-18 12:49:25 +02001856 if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001857 path->path[path->depth] = nid;
1858 path->depth++;
Takashi Iwai3214b962011-07-18 12:49:25 +02001859 snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
1860 path->depth, path->path[0], path->path[1],
1861 path->path[2], path->path[3], path->path[4]);
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001862 return true;
1863 }
1864 return false;
1865}
1866
Takashi Iwai4a796162011-06-17 17:53:38 +02001867static int via_auto_fill_dac_nids(struct hda_codec *codec)
1868{
1869 struct via_spec *spec = codec->spec;
1870 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai9562c792012-11-07 10:32:47 +01001871 int i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001872 hda_nid_t nid;
1873
Takashi Iwai9562c792012-11-07 10:32:47 +01001874 spec->multiout.num_dacs = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001875 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001876 for (i = 0; i < cfg->line_outs; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001877 hda_nid_t dac = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001878 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001879 if (!nid)
1880 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001881 if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
1882 dac = spec->out_path[i].path[0];
1883 if (!i && parse_output_path(codec, nid, dac, 1,
1884 &spec->out_mix_path))
1885 dac = spec->out_mix_path.path[0];
Takashi Iwai9562c792012-11-07 10:32:47 +01001886 if (dac)
1887 spec->private_dac_nids[spec->multiout.num_dacs++] = dac;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001888 }
Takashi Iwai3214b962011-07-18 12:49:25 +02001889 if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
1890 spec->out_path[0] = spec->out_mix_path;
1891 spec->out_mix_path.depth = 0;
1892 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001893 return 0;
1894}
1895
Takashi Iwai4a796162011-06-17 17:53:38 +02001896static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001897 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001898{
Takashi Iwai4a796162011-06-17 17:53:38 +02001899 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001900 char name[32];
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001901 hda_nid_t dac, pin, sel, nid;
1902 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001903
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001904 dac = check_dac ? path->path[0] : 0;
1905 pin = path->path[path->depth - 1];
1906 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001907
Takashi Iwai8df2a312011-06-21 11:48:29 +02001908 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001909 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001910 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001911 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001912 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1913 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001914 else
1915 nid = 0;
1916 if (nid) {
1917 sprintf(name, "%s Playback Volume", pfx);
1918 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001919 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001920 if (err < 0)
1921 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001922 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001923 }
1924
Takashi Iwai8df2a312011-06-21 11:48:29 +02001925 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001926 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001927 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001928 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001929 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1930 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001931 else
1932 nid = 0;
1933 if (nid) {
1934 sprintf(name, "%s Playback Switch", pfx);
1935 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1936 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1937 if (err < 0)
1938 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001939 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001940 }
1941 return 0;
1942}
1943
Takashi Iwaif4a78282011-06-17 18:46:48 +02001944static void mangle_smart51(struct hda_codec *codec)
1945{
1946 struct via_spec *spec = codec->spec;
1947 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001948 struct auto_pin_cfg_item *ins = cfg->inputs;
1949 int i, j, nums, attr;
1950 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001951
Takashi Iwai0f98c242011-06-21 12:51:33 +02001952 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1953 nums = 0;
1954 for (i = 0; i < cfg->num_inputs; i++) {
1955 unsigned int def;
1956 if (ins[i].type > AUTO_PIN_LINE_IN)
1957 continue;
1958 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1959 if (snd_hda_get_input_pin_attr(def) != attr)
1960 continue;
1961 for (j = 0; j < nums; j++)
1962 if (ins[pins[j]].type < ins[i].type) {
1963 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001964 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001965 break;
1966 }
1967 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001968 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001969 }
1970 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001971 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001972 for (i = 0; i < nums; i++) {
1973 hda_nid_t pin = ins[pins[i]].pin;
1974 spec->smart51_pins[spec->smart51_nums++] = pin;
1975 cfg->line_out_pins[cfg->line_outs++] = pin;
1976 if (cfg->line_outs == 3)
1977 break;
1978 }
1979 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001980 }
1981}
1982
Takashi Iwai020066d2011-07-21 13:45:56 +02001983static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src)
1984{
1985 dst->vol_ctl = src->vol_ctl;
1986 dst->mute_ctl = src->mute_ctl;
1987}
1988
Takashi Iwai4a796162011-06-17 17:53:38 +02001989/* add playback controls from the parsed DAC table */
1990static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1991{
1992 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001993 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai3214b962011-07-18 12:49:25 +02001994 struct nid_path *path;
Takashi Iwaiea734962011-01-17 11:29:34 +01001995 static const char * const chname[4] = {
1996 "Front", "Surround", "C/LFE", "Side"
1997 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001998 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001999 int old_line_outs;
2000
2001 /* check smart51 */
2002 old_line_outs = cfg->line_outs;
2003 if (cfg->line_outs == 1)
2004 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002005
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002006 err = via_auto_fill_dac_nids(codec);
2007 if (err < 0)
2008 return err;
2009
Lydia Wang5c9a5612011-07-08 14:03:43 +08002010 if (spec->multiout.num_dacs < 3) {
2011 spec->smart51_nums = 0;
2012 cfg->line_outs = old_line_outs;
2013 }
Takashi Iwai4a796162011-06-17 17:53:38 +02002014 for (i = 0; i < cfg->line_outs; i++) {
2015 hda_nid_t pin, dac;
2016 pin = cfg->line_out_pins[i];
2017 dac = spec->multiout.dac_nids[i];
2018 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002019 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02002020 path = spec->out_path + i;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02002021 if (i == HDA_CLFE) {
Takashi Iwai3214b962011-07-18 12:49:25 +02002022 err = create_ch_ctls(codec, "Center", 1, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002023 if (err < 0)
2024 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002025 err = create_ch_ctls(codec, "LFE", 2, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002026 if (err < 0)
2027 return err;
2028 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02002029 const char *pfx = chname[i];
2030 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
2031 cfg->line_outs == 1)
2032 pfx = "Speaker";
Takashi Iwai3214b962011-07-18 12:49:25 +02002033 err = create_ch_ctls(codec, pfx, 3, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002034 if (err < 0)
2035 return err;
2036 }
Takashi Iwai020066d2011-07-21 13:45:56 +02002037 if (path != spec->out_path + i)
2038 copy_path_mixer_ctls(&spec->out_path[i], path);
2039 if (path == spec->out_path && spec->out_mix_path.depth)
2040 copy_path_mixer_ctls(&spec->out_mix_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002041 }
2042
Takashi Iwai4a796162011-06-17 17:53:38 +02002043 idx = get_connection_index(codec, spec->aa_mix_nid,
2044 spec->multiout.dac_nids[0]);
2045 if (idx >= 0) {
2046 /* add control to mixer */
Takashi Iwai3214b962011-07-18 12:49:25 +02002047 const char *name;
2048 name = spec->out_mix_path.depth ?
2049 "PCM Loopback Playback Volume" : "PCM Playback Volume";
2050 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002051 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2052 idx, HDA_INPUT));
2053 if (err < 0)
2054 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002055 name = spec->out_mix_path.depth ?
2056 "PCM Loopback Playback Switch" : "PCM Playback Switch";
2057 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002058 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2059 idx, HDA_INPUT));
2060 if (err < 0)
2061 return err;
2062 }
2063
Takashi Iwaif4a78282011-06-17 18:46:48 +02002064 cfg->line_outs = old_line_outs;
2065
Joseph Chanc577b8a2006-11-29 15:29:40 +01002066 return 0;
2067}
2068
Takashi Iwai4a796162011-06-17 17:53:38 +02002069static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002070{
Takashi Iwai4a796162011-06-17 17:53:38 +02002071 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02002072 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002073 bool check_dac;
Takashi Iwai3214b962011-07-18 12:49:25 +02002074 int i, err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002075
2076 if (!pin)
2077 return 0;
2078
Takashi Iwai3214b962011-07-18 12:49:25 +02002079 if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
2080 for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
2081 if (i < spec->multiout.num_dacs &&
2082 parse_output_path(codec, pin,
2083 spec->multiout.dac_nids[i], 0,
2084 &spec->hp_indep_path)) {
2085 spec->hp_indep_shared = i;
2086 break;
2087 }
2088 }
Takashi Iwai25250502011-06-30 17:24:47 +02002089 }
Takashi Iwai3214b962011-07-18 12:49:25 +02002090 if (spec->hp_indep_path.depth) {
2091 spec->hp_dac_nid = spec->hp_indep_path.path[0];
2092 if (!spec->hp_indep_shared)
2093 spec->hp_path = spec->hp_indep_path;
2094 }
2095 /* optionally check front-path w/o AA-mix */
2096 if (!spec->hp_path.depth)
2097 parse_output_path(codec, pin,
2098 spec->multiout.dac_nids[HDA_FRONT], 0,
2099 &spec->hp_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002100
Takashi Iwaiece8d042011-06-19 16:24:21 +02002101 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai3214b962011-07-18 12:49:25 +02002102 1, &spec->hp_mix_path) && !spec->hp_path.depth)
Takashi Iwaiece8d042011-06-19 16:24:21 +02002103 return 0;
2104
Takashi Iwai3214b962011-07-18 12:49:25 +02002105 if (spec->hp_path.depth) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +02002106 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002107 check_dac = true;
2108 } else {
Takashi Iwai3214b962011-07-18 12:49:25 +02002109 path = &spec->hp_mix_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002110 check_dac = false;
2111 }
2112 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002113 if (err < 0)
2114 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002115 if (check_dac)
2116 copy_path_mixer_ctls(&spec->hp_mix_path, path);
2117 else
2118 copy_path_mixer_ctls(&spec->hp_path, path);
2119 if (spec->hp_indep_path.depth)
2120 copy_path_mixer_ctls(&spec->hp_indep_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002121 return 0;
2122}
2123
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002124static int via_auto_create_speaker_ctls(struct hda_codec *codec)
2125{
2126 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +02002127 struct nid_path *path;
2128 bool check_dac;
Wang Shaoyan81c0a782011-08-05 18:51:29 +08002129 hda_nid_t pin, dac = 0;
Takashi Iwai3214b962011-07-18 12:49:25 +02002130 int err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002131
2132 pin = spec->autocfg.speaker_pins[0];
2133 if (!spec->autocfg.speaker_outs || !pin)
2134 return 0;
2135
Takashi Iwai3214b962011-07-18 12:49:25 +02002136 if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02002137 dac = spec->speaker_path.path[0];
Takashi Iwai3214b962011-07-18 12:49:25 +02002138 if (!dac)
2139 parse_output_path(codec, pin,
2140 spec->multiout.dac_nids[HDA_FRONT], 0,
2141 &spec->speaker_path);
2142 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
2143 1, &spec->speaker_mix_path) && !dac)
2144 return 0;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002145
Takashi Iwai3214b962011-07-18 12:49:25 +02002146 /* no AA-path for front? */
2147 if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
2148 dac = 0;
2149
2150 spec->speaker_dac_nid = dac;
2151 spec->multiout.extra_out_nid[0] = dac;
2152 if (dac) {
2153 path = &spec->speaker_path;
2154 check_dac = true;
2155 } else {
2156 path = &spec->speaker_mix_path;
2157 check_dac = false;
2158 }
2159 err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
2160 if (err < 0)
2161 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002162 if (check_dac)
2163 copy_path_mixer_ctls(&spec->speaker_mix_path, path);
2164 else
2165 copy_path_mixer_ctls(&spec->speaker_path, path);
Takashi Iwai3214b962011-07-18 12:49:25 +02002166 return 0;
2167}
2168
2169#define via_aamix_ctl_info via_pin_power_ctl_info
2170
2171static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol,
2172 struct snd_ctl_elem_value *ucontrol)
2173{
2174 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2175 struct via_spec *spec = codec->spec;
2176 ucontrol->value.enumerated.item[0] = spec->aamix_mode;
2177 return 0;
2178}
2179
2180static void update_aamix_paths(struct hda_codec *codec, int do_mix,
2181 struct nid_path *nomix, struct nid_path *mix)
2182{
2183 if (do_mix) {
2184 activate_output_path(codec, nomix, false, false);
2185 activate_output_path(codec, mix, true, false);
2186 } else {
2187 activate_output_path(codec, mix, false, false);
2188 activate_output_path(codec, nomix, true, false);
2189 }
2190}
2191
2192static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol,
2193 struct snd_ctl_elem_value *ucontrol)
2194{
2195 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2196 struct via_spec *spec = codec->spec;
2197 unsigned int val = ucontrol->value.enumerated.item[0];
2198
2199 if (val == spec->aamix_mode)
2200 return 0;
2201 spec->aamix_mode = val;
2202 /* update front path */
2203 update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
2204 /* update HP path */
2205 if (!spec->hp_independent_mode) {
2206 update_aamix_paths(codec, val, &spec->hp_path,
2207 &spec->hp_mix_path);
2208 }
2209 /* update speaker path */
2210 update_aamix_paths(codec, val, &spec->speaker_path,
2211 &spec->speaker_mix_path);
2212 return 1;
2213}
2214
2215static const struct snd_kcontrol_new via_aamix_ctl_enum = {
2216 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2217 .name = "Loopback Mixing",
2218 .info = via_aamix_ctl_info,
2219 .get = via_aamix_ctl_get,
2220 .put = via_aamix_ctl_put,
2221};
2222
2223static int via_auto_create_loopback_switch(struct hda_codec *codec)
2224{
2225 struct via_spec *spec = codec->spec;
2226
Takashi Iwai4808d122012-01-10 15:16:02 +01002227 if (!spec->aa_mix_nid)
2228 return 0; /* no loopback switching available */
2229 if (!(spec->out_mix_path.depth || spec->hp_mix_path.depth ||
2230 spec->speaker_path.depth))
Takashi Iwai3214b962011-07-18 12:49:25 +02002231 return 0; /* no loopback switching available */
2232 if (!via_clone_control(spec, &via_aamix_ctl_enum))
2233 return -ENOMEM;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002234 return 0;
2235}
2236
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002237/* look for ADCs */
2238static int via_fill_adcs(struct hda_codec *codec)
2239{
2240 struct via_spec *spec = codec->spec;
2241 hda_nid_t nid = codec->start_nid;
2242 int i;
2243
2244 for (i = 0; i < codec->num_nodes; i++, nid++) {
2245 unsigned int wcaps = get_wcaps(codec, nid);
2246 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2247 continue;
2248 if (wcaps & AC_WCAP_DIGITAL)
2249 continue;
2250 if (!(wcaps & AC_WCAP_CONN_LIST))
2251 continue;
2252 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2253 return -ENOMEM;
2254 spec->adc_nids[spec->num_adc_nids++] = nid;
2255 }
2256 return 0;
2257}
2258
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002259/* input-src control */
2260static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2261 struct snd_ctl_elem_info *uinfo)
2262{
2263 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2264 struct via_spec *spec = codec->spec;
2265
2266 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2267 uinfo->count = 1;
2268 uinfo->value.enumerated.items = spec->num_inputs;
2269 if (uinfo->value.enumerated.item >= spec->num_inputs)
2270 uinfo->value.enumerated.item = spec->num_inputs - 1;
2271 strcpy(uinfo->value.enumerated.name,
2272 spec->inputs[uinfo->value.enumerated.item].label);
2273 return 0;
2274}
2275
2276static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2277 struct snd_ctl_elem_value *ucontrol)
2278{
2279 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2280 struct via_spec *spec = codec->spec;
2281 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2282
2283 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2284 return 0;
2285}
2286
2287static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2288 struct snd_ctl_elem_value *ucontrol)
2289{
2290 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2291 struct via_spec *spec = codec->spec;
2292 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2293 hda_nid_t mux;
2294 int cur;
2295
2296 cur = ucontrol->value.enumerated.item[0];
2297 if (cur < 0 || cur >= spec->num_inputs)
2298 return -EINVAL;
2299 if (spec->cur_mux[idx] == cur)
2300 return 0;
2301 spec->cur_mux[idx] = cur;
2302 if (spec->dyn_adc_switch) {
2303 int adc_idx = spec->inputs[cur].adc_idx;
2304 mux = spec->mux_nids[adc_idx];
2305 via_dyn_adc_pcm_resetup(codec, cur);
2306 } else {
2307 mux = spec->mux_nids[idx];
2308 if (snd_BUG_ON(!mux))
2309 return -EINVAL;
2310 }
2311
2312 if (mux) {
2313 /* switch to D0 beofre change index */
Takashi Iwai054d8672012-01-24 12:25:50 +01002314 update_power_state(codec, mux, AC_PWRST_D0);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002315 snd_hda_codec_write(codec, mux, 0,
2316 AC_VERB_SET_CONNECT_SEL,
2317 spec->inputs[cur].mux_idx);
2318 }
2319
2320 /* update jack power state */
2321 set_widgets_power_state(codec);
2322 return 0;
2323}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002324
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002325static const struct snd_kcontrol_new via_input_src_ctl = {
2326 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2327 /* The multiple "Capture Source" controls confuse alsamixer
2328 * So call somewhat different..
2329 */
2330 /* .name = "Capture Source", */
2331 .name = "Input Source",
2332 .info = via_mux_enum_info,
2333 .get = via_mux_enum_get,
2334 .put = via_mux_enum_put,
2335};
2336
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002337static int create_input_src_ctls(struct hda_codec *codec, int count)
2338{
2339 struct via_spec *spec = codec->spec;
2340 struct snd_kcontrol_new *knew;
2341
2342 if (spec->num_inputs <= 1 || !count)
2343 return 0; /* no need for single src */
2344
2345 knew = via_clone_control(spec, &via_input_src_ctl);
2346 if (!knew)
2347 return -ENOMEM;
2348 knew->count = count;
2349 return 0;
2350}
2351
2352/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002353static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2354{
2355 struct hda_amp_list *list;
2356
2357 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2358 return;
2359 list = spec->loopback_list + spec->num_loopbacks;
2360 list->nid = mix;
2361 list->dir = HDA_INPUT;
2362 list->idx = idx;
2363 spec->num_loopbacks++;
2364 spec->loopback.amplist = spec->loopback_list;
2365}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002366
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002367static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002368 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002369{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002370 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002371}
2372
2373/* add the input-route to the given pin */
2374static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002375{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002376 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002377 int c, idx;
2378
2379 spec->inputs[spec->num_inputs].adc_idx = -1;
2380 spec->inputs[spec->num_inputs].pin = pin;
2381 for (c = 0; c < spec->num_adc_nids; c++) {
2382 if (spec->mux_nids[c]) {
2383 idx = get_connection_index(codec, spec->mux_nids[c],
2384 pin);
2385 if (idx < 0)
2386 continue;
2387 spec->inputs[spec->num_inputs].mux_idx = idx;
2388 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002389 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002390 continue;
2391 }
2392 spec->inputs[spec->num_inputs].adc_idx = c;
2393 /* Can primary ADC satisfy all inputs? */
2394 if (!spec->dyn_adc_switch &&
2395 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2396 snd_printd(KERN_INFO
2397 "via: dynamic ADC switching enabled\n");
2398 spec->dyn_adc_switch = 1;
2399 }
2400 return true;
2401 }
2402 return false;
2403}
2404
2405static int get_mux_nids(struct hda_codec *codec);
2406
2407/* parse input-routes; fill ADCs, MUXs and input-src entries */
2408static int parse_analog_inputs(struct hda_codec *codec)
2409{
2410 struct via_spec *spec = codec->spec;
2411 const struct auto_pin_cfg *cfg = &spec->autocfg;
2412 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002413
2414 err = via_fill_adcs(codec);
2415 if (err < 0)
2416 return err;
2417 err = get_mux_nids(codec);
2418 if (err < 0)
2419 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002420
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002421 /* fill all input-routes */
2422 for (i = 0; i < cfg->num_inputs; i++) {
2423 if (add_input_route(codec, cfg->inputs[i].pin))
2424 spec->inputs[spec->num_inputs++].label =
2425 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002426 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002427
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002428 /* check for internal loopback recording */
2429 if (spec->aa_mix_nid &&
2430 add_input_route(codec, spec->aa_mix_nid))
2431 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2432
2433 return 0;
2434}
2435
2436/* create analog-loopback volume/switch controls */
2437static int create_loopback_ctls(struct hda_codec *codec)
2438{
2439 struct via_spec *spec = codec->spec;
2440 const struct auto_pin_cfg *cfg = &spec->autocfg;
2441 const char *prev_label = NULL;
2442 int type_idx = 0;
2443 int i, j, err, idx;
2444
2445 if (!spec->aa_mix_nid)
2446 return 0;
2447
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002448 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002449 hda_nid_t pin = cfg->inputs[i].pin;
2450 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2451
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002452 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002453 type_idx++;
2454 else
2455 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002456 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002457 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2458 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002459 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002460 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002461 if (err < 0)
2462 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002463 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002464 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002465
2466 /* remember the label for smart51 control */
2467 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002468 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002469 spec->smart51_idxs[j] = idx;
2470 spec->smart51_labels[j] = label;
2471 break;
2472 }
2473 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002474 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002475 return 0;
2476}
2477
2478/* create mic-boost controls (if present) */
2479static int create_mic_boost_ctls(struct hda_codec *codec)
2480{
2481 struct via_spec *spec = codec->spec;
2482 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai8d8bbc62012-02-22 12:26:38 +01002483 const char *prev_label = NULL;
2484 int type_idx = 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002485 int i, err;
2486
2487 for (i = 0; i < cfg->num_inputs; i++) {
2488 hda_nid_t pin = cfg->inputs[i].pin;
2489 unsigned int caps;
2490 const char *label;
2491 char name[32];
2492
2493 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2494 continue;
2495 caps = query_amp_caps(codec, pin, HDA_INPUT);
2496 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2497 continue;
2498 label = hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwai8d8bbc62012-02-22 12:26:38 +01002499 if (prev_label && !strcmp(label, prev_label))
2500 type_idx++;
2501 else
2502 type_idx = 0;
2503 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002504 snprintf(name, sizeof(name), "%s Boost Volume", label);
Takashi Iwai8d8bbc62012-02-22 12:26:38 +01002505 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002506 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2507 if (err < 0)
2508 return err;
2509 }
2510 return 0;
2511}
2512
2513/* create capture and input-src controls for multiple streams */
2514static int create_multi_adc_ctls(struct hda_codec *codec)
2515{
2516 struct via_spec *spec = codec->spec;
2517 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002518
2519 /* create capture mixer elements */
2520 for (i = 0; i < spec->num_adc_nids; i++) {
2521 hda_nid_t adc = spec->adc_nids[i];
2522 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2523 "Capture Volume", i,
2524 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2525 HDA_INPUT));
2526 if (err < 0)
2527 return err;
2528 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2529 "Capture Switch", i,
2530 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2531 HDA_INPUT));
2532 if (err < 0)
2533 return err;
2534 }
2535
2536 /* input-source control */
2537 for (i = 0; i < spec->num_adc_nids; i++)
2538 if (!spec->mux_nids[i])
2539 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002540 err = create_input_src_ctls(codec, i);
2541 if (err < 0)
2542 return err;
2543 return 0;
2544}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002545
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002546/* bind capture volume/switch */
2547static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2548 HDA_BIND_VOL("Capture Volume", 0);
2549static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2550 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002551
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002552static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2553 struct hda_ctl_ops *ops)
2554{
2555 struct hda_bind_ctls *ctl;
2556 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002557
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002558 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2559 if (!ctl)
2560 return -ENOMEM;
2561 ctl->ops = ops;
2562 for (i = 0; i < spec->num_adc_nids; i++)
2563 ctl->values[i] =
2564 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2565 *ctl_ret = ctl;
2566 return 0;
2567}
2568
2569/* create capture and input-src controls for dynamic ADC-switch case */
2570static int create_dyn_adc_ctls(struct hda_codec *codec)
2571{
2572 struct via_spec *spec = codec->spec;
2573 struct snd_kcontrol_new *knew;
2574 int err;
2575
2576 /* set up the bind capture ctls */
2577 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2578 if (err < 0)
2579 return err;
2580 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2581 if (err < 0)
2582 return err;
2583
2584 /* create capture mixer elements */
2585 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2586 if (!knew)
2587 return -ENOMEM;
2588 knew->private_value = (long)spec->bind_cap_vol;
2589
2590 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2591 if (!knew)
2592 return -ENOMEM;
2593 knew->private_value = (long)spec->bind_cap_sw;
2594
2595 /* input-source control */
2596 err = create_input_src_ctls(codec, 1);
2597 if (err < 0)
2598 return err;
2599 return 0;
2600}
2601
2602/* parse and create capture-related stuff */
2603static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2604{
2605 struct via_spec *spec = codec->spec;
2606 int err;
2607
2608 err = parse_analog_inputs(codec);
2609 if (err < 0)
2610 return err;
2611 if (spec->dyn_adc_switch)
2612 err = create_dyn_adc_ctls(codec);
2613 else
2614 err = create_multi_adc_ctls(codec);
2615 if (err < 0)
2616 return err;
2617 err = create_loopback_ctls(codec);
2618 if (err < 0)
2619 return err;
2620 err = create_mic_boost_ctls(codec);
2621 if (err < 0)
2622 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002623 return 0;
2624}
2625
Harald Welte76d9b0d2008-09-09 15:50:37 +08002626static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2627{
2628 unsigned int def_conf;
2629 unsigned char seqassoc;
2630
Takashi Iwai2f334f92009-02-20 14:37:42 +01002631 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002632 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2633 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002634 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2635 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2636 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2637 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002638 }
2639
2640 return;
2641}
2642
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002643static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002644 struct snd_ctl_elem_value *ucontrol)
2645{
2646 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2647 struct via_spec *spec = codec->spec;
2648
2649 if (spec->codec_type != VT1708)
2650 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002651 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002652 return 0;
2653}
2654
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002655static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002656 struct snd_ctl_elem_value *ucontrol)
2657{
2658 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2659 struct via_spec *spec = codec->spec;
Takashi Iwai187d3332011-11-24 16:33:09 +01002660 int val;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002661
2662 if (spec->codec_type != VT1708)
2663 return 0;
Takashi Iwai187d3332011-11-24 16:33:09 +01002664 val = !!ucontrol->value.integer.value[0];
2665 if (spec->vt1708_jack_detect == val)
2666 return 0;
2667 spec->vt1708_jack_detect = val;
2668 if (spec->vt1708_jack_detect &&
2669 snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") != 1) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002670 mute_aa_path(codec, 1);
2671 notify_aa_path_ctls(codec);
2672 }
Takashi Iwai187d3332011-11-24 16:33:09 +01002673 via_hp_automute(codec);
2674 vt1708_update_hp_work(spec);
2675 return 1;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002676}
2677
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002678static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2679 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2680 .name = "Jack Detect",
2681 .count = 1,
2682 .info = snd_ctl_boolean_mono_info,
2683 .get = vt1708_jack_detect_get,
2684 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002685};
2686
Takashi Iwai12daef62011-06-18 17:45:49 +02002687static void fill_dig_outs(struct hda_codec *codec);
2688static void fill_dig_in(struct hda_codec *codec);
2689
2690static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002691{
2692 struct via_spec *spec = codec->spec;
2693 int err;
2694
2695 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2696 if (err < 0)
2697 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002698 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002699 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002700
Takashi Iwai4a796162011-06-17 17:53:38 +02002701 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002702 if (err < 0)
2703 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002704 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002705 if (err < 0)
2706 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002707 err = via_auto_create_speaker_ctls(codec);
2708 if (err < 0)
2709 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002710 err = via_auto_create_loopback_switch(codec);
2711 if (err < 0)
2712 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002713 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002714 if (err < 0)
2715 return err;
2716
2717 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2718
Takashi Iwai12daef62011-06-18 17:45:49 +02002719 fill_dig_outs(codec);
2720 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002721
Takashi Iwai603c4012008-07-30 15:01:44 +02002722 if (spec->kctls.list)
2723 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002724
Joseph Chanc577b8a2006-11-29 15:29:40 +01002725
Takashi Iwai3214b962011-07-18 12:49:25 +02002726 if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002727 err = via_hp_build(codec);
2728 if (err < 0)
2729 return err;
2730 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002731
Takashi Iwaif4a78282011-06-17 18:46:48 +02002732 err = via_smart51_build(codec);
2733 if (err < 0)
2734 return err;
2735
Takashi Iwai5d417622011-06-20 11:32:27 +02002736 /* assign slave outs */
2737 if (spec->slave_dig_outs[0])
2738 codec->slave_dig_outs = spec->slave_dig_outs;
2739
Joseph Chanc577b8a2006-11-29 15:29:40 +01002740 return 1;
2741}
2742
Takashi Iwai5d417622011-06-20 11:32:27 +02002743static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002744{
Lydia Wang25eaba22009-10-10 19:08:43 +08002745 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002746 if (spec->multiout.dig_out_nid)
2747 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2748 if (spec->slave_dig_outs[0])
2749 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2750}
Lydia Wang25eaba22009-10-10 19:08:43 +08002751
Takashi Iwai5d417622011-06-20 11:32:27 +02002752static void via_auto_init_dig_in(struct hda_codec *codec)
2753{
2754 struct via_spec *spec = codec->spec;
2755 if (!spec->dig_in_nid)
2756 return;
2757 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2758 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2759}
2760
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002761/* initialize the unsolicited events */
2762static void via_auto_init_unsol_event(struct hda_codec *codec)
2763{
2764 struct via_spec *spec = codec->spec;
2765 struct auto_pin_cfg *cfg = &spec->autocfg;
2766 unsigned int ev;
2767 int i;
2768
2769 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002770 snd_hda_jack_detect_enable(codec, cfg->hp_pins[0],
2771 VIA_HP_EVENT | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002772
2773 if (cfg->speaker_pins[0])
2774 ev = VIA_LINE_EVENT;
2775 else
2776 ev = 0;
2777 for (i = 0; i < cfg->line_outs; i++) {
2778 if (cfg->line_out_pins[i] &&
2779 is_jack_detectable(codec, cfg->line_out_pins[i]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002780 snd_hda_jack_detect_enable(codec, cfg->line_out_pins[i],
2781 ev | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002782 }
2783
2784 for (i = 0; i < cfg->num_inputs; i++) {
2785 if (is_jack_detectable(codec, cfg->inputs[i].pin))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002786 snd_hda_jack_detect_enable(codec, cfg->inputs[i].pin,
2787 VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002788 }
2789}
2790
Takashi Iwai5d417622011-06-20 11:32:27 +02002791static int via_init(struct hda_codec *codec)
2792{
2793 struct via_spec *spec = codec->spec;
2794 int i;
2795
2796 for (i = 0; i < spec->num_iverbs; i++)
2797 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2798
Takashi Iwaie9d010c2012-02-01 10:33:23 +01002799 /* init power states */
2800 set_widgets_power_state(codec);
2801 __analog_low_current_mode(codec, true);
2802
Joseph Chanc577b8a2006-11-29 15:29:40 +01002803 via_auto_init_multi_out(codec);
2804 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002805 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002806 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002807 via_auto_init_dig_outs(codec);
2808 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002809
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002810 via_auto_init_unsol_event(codec);
2811
2812 via_hp_automute(codec);
Takashi Iwai187d3332011-11-24 16:33:09 +01002813 vt1708_update_hp_work(spec);
Takashi Iwai01a61e12011-10-28 00:03:22 +02002814 snd_hda_jack_report_sync(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002815
Joseph Chanc577b8a2006-11-29 15:29:40 +01002816 return 0;
2817}
2818
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002819static void vt1708_update_hp_jack_state(struct work_struct *work)
2820{
2821 struct via_spec *spec = container_of(work, struct via_spec,
2822 vt1708_hp_work.work);
2823 if (spec->codec_type != VT1708)
2824 return;
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002825 snd_hda_jack_set_dirty_all(spec->codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002826 /* if jack state toggled */
2827 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002828 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002829 spec->vt1708_hp_present ^= 1;
2830 via_hp_automute(spec->codec);
2831 }
Takashi Iwai187d3332011-11-24 16:33:09 +01002832 if (spec->vt1708_jack_detect)
2833 schedule_delayed_work(&spec->vt1708_hp_work,
2834 msecs_to_jiffies(100));
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002835}
2836
Takashi Iwai337b9d02009-07-07 18:18:59 +02002837static int get_mux_nids(struct hda_codec *codec)
2838{
2839 struct via_spec *spec = codec->spec;
2840 hda_nid_t nid, conn[8];
2841 unsigned int type;
2842 int i, n;
2843
2844 for (i = 0; i < spec->num_adc_nids; i++) {
2845 nid = spec->adc_nids[i];
2846 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002847 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002848 if (type == AC_WID_PIN)
2849 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002850 n = snd_hda_get_connections(codec, nid, conn,
2851 ARRAY_SIZE(conn));
2852 if (n <= 0)
2853 break;
2854 if (n > 1) {
2855 spec->mux_nids[i] = nid;
2856 break;
2857 }
2858 nid = conn[0];
2859 }
2860 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002861 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002862}
2863
Joseph Chanc577b8a2006-11-29 15:29:40 +01002864static int patch_vt1708(struct hda_codec *codec)
2865{
2866 struct via_spec *spec;
2867 int err;
2868
2869 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002870 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002871 if (spec == NULL)
2872 return -ENOMEM;
2873
Takashi Iwai620e2b22011-06-17 17:19:19 +02002874 spec->aa_mix_nid = 0x17;
2875
Takashi Iwai12daef62011-06-18 17:45:49 +02002876 /* Add HP and CD pin config connect bit re-config action */
2877 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2878 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2879
Joseph Chanc577b8a2006-11-29 15:29:40 +01002880 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002881 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002882 if (err < 0) {
2883 via_free(codec);
2884 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002885 }
2886
Takashi Iwai12daef62011-06-18 17:45:49 +02002887 /* add jack detect on/off control */
2888 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2889 return -ENOMEM;
2890
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002891 /* disable 32bit format on VT1708 */
2892 if (codec->vendor_id == 0x11061708)
2893 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002894
Lydia Wange322a362011-06-29 13:52:02 +08002895 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2896
Joseph Chanc577b8a2006-11-29 15:29:40 +01002897 codec->patch_ops = via_patch_ops;
2898
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002899 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002900 return 0;
2901}
2902
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002903static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002904{
2905 struct via_spec *spec;
2906 int err;
2907
2908 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002909 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002910 if (spec == NULL)
2911 return -ENOMEM;
2912
Takashi Iwai620e2b22011-06-17 17:19:19 +02002913 spec->aa_mix_nid = 0x18;
2914
Takashi Iwai12daef62011-06-18 17:45:49 +02002915 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002916 if (err < 0) {
2917 via_free(codec);
2918 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002919 }
2920
Joseph Chanc577b8a2006-11-29 15:29:40 +01002921 codec->patch_ops = via_patch_ops;
2922
Josepch Chanf7278fd2007-12-13 16:40:40 +01002923 return 0;
2924}
2925
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002926static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2927{
2928 struct via_spec *spec = codec->spec;
2929 int imux_is_smixer;
2930 unsigned int parm;
2931 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002932 if ((spec->codec_type != VT1708B_4CH) &&
2933 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002934 is_8ch = 1;
2935
2936 /* SW0 (17h) = stereo mixer */
2937 imux_is_smixer =
2938 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2939 == ((spec->codec_type == VT1708S) ? 5 : 0));
2940 /* inputs */
2941 /* PW 1/2/5 (1ah/1bh/1eh) */
2942 parm = AC_PWRST_D3;
2943 set_pin_power_state(codec, 0x1a, &parm);
2944 set_pin_power_state(codec, 0x1b, &parm);
2945 set_pin_power_state(codec, 0x1e, &parm);
2946 if (imux_is_smixer)
2947 parm = AC_PWRST_D0;
2948 /* SW0 (17h), AIW 0/1 (13h/14h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01002949 update_power_state(codec, 0x17, parm);
2950 update_power_state(codec, 0x13, parm);
2951 update_power_state(codec, 0x14, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002952
2953 /* outputs */
2954 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2955 parm = AC_PWRST_D3;
2956 set_pin_power_state(codec, 0x19, &parm);
2957 if (spec->smart51_enabled)
2958 set_pin_power_state(codec, 0x1b, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01002959 update_power_state(codec, 0x18, parm);
2960 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002961
2962 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2963 if (is_8ch) {
2964 parm = AC_PWRST_D3;
2965 set_pin_power_state(codec, 0x22, &parm);
2966 if (spec->smart51_enabled)
2967 set_pin_power_state(codec, 0x1a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01002968 update_power_state(codec, 0x26, parm);
2969 update_power_state(codec, 0x24, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002970 } else if (codec->vendor_id == 0x11064397) {
2971 /* PW7(23h), SW2(27h), AOW2(25h) */
2972 parm = AC_PWRST_D3;
2973 set_pin_power_state(codec, 0x23, &parm);
2974 if (spec->smart51_enabled)
2975 set_pin_power_state(codec, 0x1a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01002976 update_power_state(codec, 0x27, parm);
2977 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002978 }
2979
2980 /* PW 3/4/7 (1ch/1dh/23h) */
2981 parm = AC_PWRST_D3;
2982 /* force to D0 for internal Speaker */
2983 set_pin_power_state(codec, 0x1c, &parm);
2984 set_pin_power_state(codec, 0x1d, &parm);
2985 if (is_8ch)
2986 set_pin_power_state(codec, 0x23, &parm);
2987
2988 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01002989 update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
2990 update_power_state(codec, 0x10, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002991 if (is_8ch) {
Takashi Iwai054d8672012-01-24 12:25:50 +01002992 update_power_state(codec, 0x25, parm);
2993 update_power_state(codec, 0x27, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002994 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01002995 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002996}
2997
Lydia Wang518bf3b2009-10-10 19:07:29 +08002998static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002999static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01003000{
3001 struct via_spec *spec;
3002 int err;
3003
Lydia Wang518bf3b2009-10-10 19:07:29 +08003004 if (get_codec_type(codec) == VT1708BCE)
3005 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003006
Josepch Chanf7278fd2007-12-13 16:40:40 +01003007 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003008 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003009 if (spec == NULL)
3010 return -ENOMEM;
3011
Takashi Iwai620e2b22011-06-17 17:19:19 +02003012 spec->aa_mix_nid = 0x16;
3013
Josepch Chanf7278fd2007-12-13 16:40:40 +01003014 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003015 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003016 if (err < 0) {
3017 via_free(codec);
3018 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003019 }
3020
Josepch Chanf7278fd2007-12-13 16:40:40 +01003021 codec->patch_ops = via_patch_ops;
3022
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003023 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
3024
Josepch Chanf7278fd2007-12-13 16:40:40 +01003025 return 0;
3026}
3027
Harald Welted949cac2008-09-09 15:56:01 +08003028/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02003029static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08003030 /* Enable Mic Boost Volume backdoor */
3031 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003032 /* don't bybass mixer */
3033 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08003034 { }
3035};
3036
Takashi Iwai9da29272009-05-07 16:31:14 +02003037/* fill out digital output widgets; one for master and one for slave outputs */
3038static void fill_dig_outs(struct hda_codec *codec)
3039{
3040 struct via_spec *spec = codec->spec;
3041 int i;
3042
3043 for (i = 0; i < spec->autocfg.dig_outs; i++) {
3044 hda_nid_t nid;
3045 int conn;
3046
3047 nid = spec->autocfg.dig_out_pins[i];
3048 if (!nid)
3049 continue;
3050 conn = snd_hda_get_connections(codec, nid, &nid, 1);
3051 if (conn < 1)
3052 continue;
3053 if (!spec->multiout.dig_out_nid)
3054 spec->multiout.dig_out_nid = nid;
3055 else {
3056 spec->slave_dig_outs[0] = nid;
3057 break; /* at most two dig outs */
3058 }
3059 }
3060}
3061
Takashi Iwai12daef62011-06-18 17:45:49 +02003062static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08003063{
3064 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02003065 hda_nid_t dig_nid;
3066 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08003067
Takashi Iwai12daef62011-06-18 17:45:49 +02003068 if (!spec->autocfg.dig_in_pin)
3069 return;
Harald Welted949cac2008-09-09 15:56:01 +08003070
Takashi Iwai12daef62011-06-18 17:45:49 +02003071 dig_nid = codec->start_nid;
3072 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
3073 unsigned int wcaps = get_wcaps(codec, dig_nid);
3074 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
3075 continue;
3076 if (!(wcaps & AC_WCAP_DIGITAL))
3077 continue;
3078 if (!(wcaps & AC_WCAP_CONN_LIST))
3079 continue;
3080 err = get_connection_index(codec, dig_nid,
3081 spec->autocfg.dig_in_pin);
3082 if (err >= 0) {
3083 spec->dig_in_nid = dig_nid;
3084 break;
3085 }
3086 }
Harald Welted949cac2008-09-09 15:56:01 +08003087}
3088
Lydia Wang6369bcf2009-10-10 19:08:31 +08003089static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
3090 int offset, int num_steps, int step_size)
3091{
3092 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
3093 (offset << AC_AMPCAP_OFFSET_SHIFT) |
3094 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
3095 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
3096 (0 << AC_AMPCAP_MUTE_SHIFT));
3097}
3098
Harald Welted949cac2008-09-09 15:56:01 +08003099static int patch_vt1708S(struct hda_codec *codec)
3100{
3101 struct via_spec *spec;
3102 int err;
3103
3104 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003105 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003106 if (spec == NULL)
3107 return -ENOMEM;
3108
Takashi Iwai620e2b22011-06-17 17:19:19 +02003109 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003110 override_mic_boost(codec, 0x1a, 0, 3, 40);
3111 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003112
Harald Welted949cac2008-09-09 15:56:01 +08003113 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003114 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003115 if (err < 0) {
3116 via_free(codec);
3117 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003118 }
3119
Takashi Iwai096a8852011-06-20 12:09:02 +02003120 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003121
Harald Welted949cac2008-09-09 15:56:01 +08003122 codec->patch_ops = via_patch_ops;
3123
Lydia Wang518bf3b2009-10-10 19:07:29 +08003124 /* correct names for VT1708BCE */
3125 if (get_codec_type(codec) == VT1708BCE) {
3126 kfree(codec->chip_name);
3127 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3128 snprintf(codec->bus->card->mixername,
3129 sizeof(codec->bus->card->mixername),
3130 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08003131 }
Lydia Wangbc92df72011-03-23 17:56:05 +08003132 /* correct names for VT1705 */
3133 if (codec->vendor_id == 0x11064397) {
3134 kfree(codec->chip_name);
3135 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3136 snprintf(codec->bus->card->mixername,
3137 sizeof(codec->bus->card->mixername),
3138 "%s %s", codec->vendor_name, codec->chip_name);
3139 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003140 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08003141 return 0;
3142}
3143
3144/* Patch for VT1702 */
3145
Takashi Iwai096a8852011-06-20 12:09:02 +02003146static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003147 /* mixer enable */
3148 {0x1, 0xF88, 0x3},
3149 /* GPIO 0~2 */
3150 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003151 { }
3152};
3153
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003154static void set_widgets_power_state_vt1702(struct hda_codec *codec)
3155{
3156 int imux_is_smixer =
3157 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3158 unsigned int parm;
3159 /* inputs */
3160 /* PW 1/2/5 (14h/15h/18h) */
3161 parm = AC_PWRST_D3;
3162 set_pin_power_state(codec, 0x14, &parm);
3163 set_pin_power_state(codec, 0x15, &parm);
3164 set_pin_power_state(codec, 0x18, &parm);
3165 if (imux_is_smixer)
3166 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
3167 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003168 update_power_state(codec, 0x13, parm);
3169 update_power_state(codec, 0x12, parm);
3170 update_power_state(codec, 0x1f, parm);
3171 update_power_state(codec, 0x20, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003172
3173 /* outputs */
3174 /* PW 3/4 (16h/17h) */
3175 parm = AC_PWRST_D3;
3176 set_pin_power_state(codec, 0x17, &parm);
3177 set_pin_power_state(codec, 0x16, &parm);
3178 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003179 update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm);
3180 update_power_state(codec, 0x10, parm);
3181 update_power_state(codec, 0x1d, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003182}
3183
Harald Welted949cac2008-09-09 15:56:01 +08003184static int patch_vt1702(struct hda_codec *codec)
3185{
3186 struct via_spec *spec;
3187 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003188
3189 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003190 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003191 if (spec == NULL)
3192 return -ENOMEM;
3193
Takashi Iwai620e2b22011-06-17 17:19:19 +02003194 spec->aa_mix_nid = 0x1a;
3195
Takashi Iwai12daef62011-06-18 17:45:49 +02003196 /* limit AA path volume to 0 dB */
3197 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3198 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3199 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3200 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3201 (1 << AC_AMPCAP_MUTE_SHIFT));
3202
Harald Welted949cac2008-09-09 15:56:01 +08003203 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003204 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003205 if (err < 0) {
3206 via_free(codec);
3207 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003208 }
3209
Takashi Iwai096a8852011-06-20 12:09:02 +02003210 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003211
Harald Welted949cac2008-09-09 15:56:01 +08003212 codec->patch_ops = via_patch_ops;
3213
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003214 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08003215 return 0;
3216}
3217
Lydia Wangeb7188c2009-10-10 19:08:34 +08003218/* Patch for VT1718S */
3219
Takashi Iwai096a8852011-06-20 12:09:02 +02003220static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08003221 /* Enable MW0 adjust Gain 5 */
3222 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003223 /* Enable Boost Volume backdoor */
3224 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02003225
Lydia Wangeb7188c2009-10-10 19:08:34 +08003226 { }
3227};
3228
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003229static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
3230{
3231 struct via_spec *spec = codec->spec;
3232 int imux_is_smixer;
Takashi Iwaiac1d1b42012-07-25 13:54:55 +02003233 unsigned int parm, parm2;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003234 /* MUX6 (1eh) = stereo mixer */
3235 imux_is_smixer =
3236 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3237 /* inputs */
3238 /* PW 5/6/7 (29h/2ah/2bh) */
3239 parm = AC_PWRST_D3;
3240 set_pin_power_state(codec, 0x29, &parm);
3241 set_pin_power_state(codec, 0x2a, &parm);
3242 set_pin_power_state(codec, 0x2b, &parm);
3243 if (imux_is_smixer)
3244 parm = AC_PWRST_D0;
3245 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003246 update_power_state(codec, 0x1e, parm);
3247 update_power_state(codec, 0x1f, parm);
3248 update_power_state(codec, 0x10, parm);
3249 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003250
3251 /* outputs */
3252 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
3253 parm = AC_PWRST_D3;
3254 set_pin_power_state(codec, 0x27, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003255 update_power_state(codec, 0x1a, parm);
Takashi Iwaiac1d1b42012-07-25 13:54:55 +02003256 parm2 = parm; /* for pin 0x0b */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003257
3258 /* PW2 (26h), AOW2 (ah) */
3259 parm = AC_PWRST_D3;
3260 set_pin_power_state(codec, 0x26, &parm);
3261 if (spec->smart51_enabled)
3262 set_pin_power_state(codec, 0x2b, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003263 update_power_state(codec, 0xa, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003264
3265 /* PW0 (24h), AOW0 (8h) */
3266 parm = AC_PWRST_D3;
3267 set_pin_power_state(codec, 0x24, &parm);
3268 if (!spec->hp_independent_mode) /* check for redirected HP */
3269 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003270 update_power_state(codec, 0x8, parm);
Takashi Iwaiac1d1b42012-07-25 13:54:55 +02003271 if (!spec->hp_independent_mode && parm2 != AC_PWRST_D3)
3272 parm = parm2;
3273 update_power_state(codec, 0xb, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003274 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003275 update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003276
3277 /* PW1 (25h), AOW1 (9h) */
3278 parm = AC_PWRST_D3;
3279 set_pin_power_state(codec, 0x25, &parm);
3280 if (spec->smart51_enabled)
3281 set_pin_power_state(codec, 0x2a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003282 update_power_state(codec, 0x9, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003283
3284 if (spec->hp_independent_mode) {
3285 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3286 parm = AC_PWRST_D3;
3287 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003288 update_power_state(codec, 0x1b, parm);
3289 update_power_state(codec, 0x34, parm);
3290 update_power_state(codec, 0xc, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003291 }
3292}
3293
Takashi Iwai30b45032011-07-11 17:05:04 +02003294/* Add a connection to the primary DAC from AA-mixer for some codecs
3295 * This isn't listed from the raw info, but the chip has a secret connection.
3296 */
3297static int add_secret_dac_path(struct hda_codec *codec)
3298{
3299 struct via_spec *spec = codec->spec;
3300 int i, nums;
3301 hda_nid_t conn[8];
3302 hda_nid_t nid;
3303
3304 if (!spec->aa_mix_nid)
3305 return 0;
3306 nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
3307 ARRAY_SIZE(conn) - 1);
3308 for (i = 0; i < nums; i++) {
3309 if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
3310 return 0;
3311 }
3312
3313 /* find the primary DAC and add to the connection list */
3314 nid = codec->start_nid;
3315 for (i = 0; i < codec->num_nodes; i++, nid++) {
3316 unsigned int caps = get_wcaps(codec, nid);
3317 if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
3318 !(caps & AC_WCAP_DIGITAL)) {
3319 conn[nums++] = nid;
3320 return snd_hda_override_conn_list(codec,
3321 spec->aa_mix_nid,
3322 nums, conn);
3323 }
3324 }
3325 return 0;
3326}
3327
3328
Lydia Wangeb7188c2009-10-10 19:08:34 +08003329static int patch_vt1718S(struct hda_codec *codec)
3330{
3331 struct via_spec *spec;
3332 int err;
3333
3334 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003335 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003336 if (spec == NULL)
3337 return -ENOMEM;
3338
Takashi Iwai620e2b22011-06-17 17:19:19 +02003339 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003340 override_mic_boost(codec, 0x2b, 0, 3, 40);
3341 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003342 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003343
Lydia Wangeb7188c2009-10-10 19:08:34 +08003344 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003345 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003346 if (err < 0) {
3347 via_free(codec);
3348 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003349 }
3350
Takashi Iwai096a8852011-06-20 12:09:02 +02003351 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003352
Lydia Wangeb7188c2009-10-10 19:08:34 +08003353 codec->patch_ops = via_patch_ops;
3354
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003355 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3356
Lydia Wangeb7188c2009-10-10 19:08:34 +08003357 return 0;
3358}
Lydia Wangf3db4232009-10-10 19:08:41 +08003359
3360/* Patch for VT1716S */
3361
3362static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3363 struct snd_ctl_elem_info *uinfo)
3364{
3365 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3366 uinfo->count = 1;
3367 uinfo->value.integer.min = 0;
3368 uinfo->value.integer.max = 1;
3369 return 0;
3370}
3371
3372static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3373 struct snd_ctl_elem_value *ucontrol)
3374{
3375 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3376 int index = 0;
3377
3378 index = snd_hda_codec_read(codec, 0x26, 0,
3379 AC_VERB_GET_CONNECT_SEL, 0);
3380 if (index != -1)
3381 *ucontrol->value.integer.value = index;
3382
3383 return 0;
3384}
3385
3386static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3387 struct snd_ctl_elem_value *ucontrol)
3388{
3389 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3390 struct via_spec *spec = codec->spec;
3391 int index = *ucontrol->value.integer.value;
3392
3393 snd_hda_codec_write(codec, 0x26, 0,
3394 AC_VERB_SET_CONNECT_SEL, index);
3395 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003396 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003397 return 1;
3398}
3399
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003400static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003401 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3402 {
3403 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3404 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003405 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003406 .count = 1,
3407 .info = vt1716s_dmic_info,
3408 .get = vt1716s_dmic_get,
3409 .put = vt1716s_dmic_put,
3410 },
3411 {} /* end */
3412};
3413
3414
3415/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003416static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003417 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3418 { } /* end */
3419};
3420
Takashi Iwai096a8852011-06-20 12:09:02 +02003421static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003422 /* Enable Boost Volume backdoor */
3423 {0x1, 0xf8a, 0x80},
3424 /* don't bybass mixer */
3425 {0x1, 0xf88, 0xc0},
3426 /* Enable mono output */
3427 {0x1, 0xf90, 0x08},
3428 { }
3429};
3430
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003431static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3432{
3433 struct via_spec *spec = codec->spec;
3434 int imux_is_smixer;
3435 unsigned int parm;
3436 unsigned int mono_out, present;
3437 /* SW0 (17h) = stereo mixer */
3438 imux_is_smixer =
3439 (snd_hda_codec_read(codec, 0x17, 0,
3440 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3441 /* inputs */
3442 /* PW 1/2/5 (1ah/1bh/1eh) */
3443 parm = AC_PWRST_D3;
3444 set_pin_power_state(codec, 0x1a, &parm);
3445 set_pin_power_state(codec, 0x1b, &parm);
3446 set_pin_power_state(codec, 0x1e, &parm);
3447 if (imux_is_smixer)
3448 parm = AC_PWRST_D0;
3449 /* SW0 (17h), AIW0(13h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003450 update_power_state(codec, 0x17, parm);
3451 update_power_state(codec, 0x13, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003452
3453 parm = AC_PWRST_D3;
3454 set_pin_power_state(codec, 0x1e, &parm);
3455 /* PW11 (22h) */
3456 if (spec->dmic_enabled)
3457 set_pin_power_state(codec, 0x22, &parm);
3458 else
Takashi Iwai054d8672012-01-24 12:25:50 +01003459 update_power_state(codec, 0x22, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003460
3461 /* SW2(26h), AIW1(14h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003462 update_power_state(codec, 0x26, parm);
3463 update_power_state(codec, 0x14, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003464
3465 /* outputs */
3466 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3467 parm = AC_PWRST_D3;
3468 set_pin_power_state(codec, 0x19, &parm);
3469 /* Smart 5.1 PW2(1bh) */
3470 if (spec->smart51_enabled)
3471 set_pin_power_state(codec, 0x1b, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003472 update_power_state(codec, 0x18, parm);
3473 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003474
3475 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3476 parm = AC_PWRST_D3;
3477 set_pin_power_state(codec, 0x23, &parm);
3478 /* Smart 5.1 PW1(1ah) */
3479 if (spec->smart51_enabled)
3480 set_pin_power_state(codec, 0x1a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003481 update_power_state(codec, 0x27, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003482
3483 /* Smart 5.1 PW5(1eh) */
3484 if (spec->smart51_enabled)
3485 set_pin_power_state(codec, 0x1e, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003486 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003487
3488 /* Mono out */
3489 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3490 present = snd_hda_jack_detect(codec, 0x1c);
3491
3492 if (present)
3493 mono_out = 0;
3494 else {
3495 present = snd_hda_jack_detect(codec, 0x1d);
3496 if (!spec->hp_independent_mode && present)
3497 mono_out = 0;
3498 else
3499 mono_out = 1;
3500 }
3501 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
Takashi Iwai054d8672012-01-24 12:25:50 +01003502 update_power_state(codec, 0x28, parm);
3503 update_power_state(codec, 0x29, parm);
3504 update_power_state(codec, 0x2a, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003505
3506 /* PW 3/4 (1ch/1dh) */
3507 parm = AC_PWRST_D3;
3508 set_pin_power_state(codec, 0x1c, &parm);
3509 set_pin_power_state(codec, 0x1d, &parm);
3510 /* HP Independent Mode, power on AOW3 */
3511 if (spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01003512 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003513
3514 /* force to D0 for internal Speaker */
3515 /* MW0 (16h), AOW0 (10h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003516 update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
3517 update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003518}
3519
Lydia Wangf3db4232009-10-10 19:08:41 +08003520static int patch_vt1716S(struct hda_codec *codec)
3521{
3522 struct via_spec *spec;
3523 int err;
3524
3525 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003526 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003527 if (spec == NULL)
3528 return -ENOMEM;
3529
Takashi Iwai620e2b22011-06-17 17:19:19 +02003530 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003531 override_mic_boost(codec, 0x1a, 0, 3, 40);
3532 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003533
Lydia Wangf3db4232009-10-10 19:08:41 +08003534 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003535 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003536 if (err < 0) {
3537 via_free(codec);
3538 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003539 }
3540
Takashi Iwai096a8852011-06-20 12:09:02 +02003541 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003542
Lydia Wangf3db4232009-10-10 19:08:41 +08003543 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3544 spec->num_mixers++;
3545
3546 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3547
3548 codec->patch_ops = via_patch_ops;
3549
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003550 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003551 return 0;
3552}
Lydia Wang25eaba22009-10-10 19:08:43 +08003553
3554/* for vt2002P */
3555
Takashi Iwai096a8852011-06-20 12:09:02 +02003556static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003557 /* Class-D speaker related verbs */
3558 {0x1, 0xfe0, 0x4},
3559 {0x1, 0xfe9, 0x80},
3560 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003561 /* Enable Boost Volume backdoor */
3562 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003563 /* Enable AOW0 to MW9 */
3564 {0x1, 0xfb8, 0x88},
3565 { }
3566};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003567
Takashi Iwai096a8852011-06-20 12:09:02 +02003568static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003569 /* Enable Boost Volume backdoor */
3570 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003571 /* Enable AOW0 to MW9 */
3572 {0x1, 0xfb8, 0x88},
3573 { }
3574};
Lydia Wang25eaba22009-10-10 19:08:43 +08003575
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003576static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3577{
3578 struct via_spec *spec = codec->spec;
3579 int imux_is_smixer;
3580 unsigned int parm;
3581 unsigned int present;
3582 /* MUX9 (1eh) = stereo mixer */
3583 imux_is_smixer =
3584 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3585 /* inputs */
3586 /* PW 5/6/7 (29h/2ah/2bh) */
3587 parm = AC_PWRST_D3;
3588 set_pin_power_state(codec, 0x29, &parm);
3589 set_pin_power_state(codec, 0x2a, &parm);
3590 set_pin_power_state(codec, 0x2b, &parm);
3591 parm = AC_PWRST_D0;
3592 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003593 update_power_state(codec, 0x1e, parm);
3594 update_power_state(codec, 0x1f, parm);
3595 update_power_state(codec, 0x10, parm);
3596 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003597
3598 /* outputs */
3599 /* AOW0 (8h)*/
Takashi Iwai054d8672012-01-24 12:25:50 +01003600 update_power_state(codec, 0x8, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003601
Lydia Wang118909562011-03-23 17:57:34 +08003602 if (spec->codec_type == VT1802) {
3603 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3604 parm = AC_PWRST_D3;
3605 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003606 update_power_state(codec, 0x18, parm);
3607 update_power_state(codec, 0x38, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003608 } else {
3609 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3610 parm = AC_PWRST_D3;
3611 set_pin_power_state(codec, 0x26, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003612 update_power_state(codec, 0x1c, parm);
3613 update_power_state(codec, 0x37, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003614 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003615
Lydia Wang118909562011-03-23 17:57:34 +08003616 if (spec->codec_type == VT1802) {
3617 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3618 parm = AC_PWRST_D3;
3619 set_pin_power_state(codec, 0x25, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003620 update_power_state(codec, 0x15, parm);
3621 update_power_state(codec, 0x35, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003622 } else {
3623 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3624 parm = AC_PWRST_D3;
3625 set_pin_power_state(codec, 0x25, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003626 update_power_state(codec, 0x19, parm);
3627 update_power_state(codec, 0x35, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003628 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003629
3630 if (spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01003631 update_power_state(codec, 0x9, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003632
3633 /* Class-D */
3634 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3635 present = snd_hda_jack_detect(codec, 0x25);
3636
3637 parm = AC_PWRST_D3;
3638 set_pin_power_state(codec, 0x24, &parm);
3639 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003640 if (spec->codec_type == VT1802)
Takashi Iwai054d8672012-01-24 12:25:50 +01003641 update_power_state(codec, 0x14, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003642 else
Takashi Iwai054d8672012-01-24 12:25:50 +01003643 update_power_state(codec, 0x18, parm);
3644 update_power_state(codec, 0x34, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003645
3646 /* Mono Out */
3647 present = snd_hda_jack_detect(codec, 0x26);
3648
3649 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003650 if (spec->codec_type == VT1802) {
3651 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003652 update_power_state(codec, 0x33, parm);
3653 update_power_state(codec, 0x1c, parm);
3654 update_power_state(codec, 0x3c, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003655 } else {
3656 /* PW15 (31h), MW8(17h), MUX8(3bh) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003657 update_power_state(codec, 0x31, parm);
3658 update_power_state(codec, 0x17, parm);
3659 update_power_state(codec, 0x3b, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003660 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003661 /* MW9 (21h) */
3662 if (imux_is_smixer || !is_aa_path_mute(codec))
Takashi Iwai054d8672012-01-24 12:25:50 +01003663 update_power_state(codec, 0x21, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003664 else
Takashi Iwai054d8672012-01-24 12:25:50 +01003665 update_power_state(codec, 0x21, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003666}
Lydia Wang25eaba22009-10-10 19:08:43 +08003667
Takashi Iwaid2d0adc2012-11-07 10:37:48 +01003668/* NIDs 0x24 and 0x33 on VT1802 have connections to non-existing NID 0x3e
3669 * Replace this with mixer NID 0x1c
3670 */
3671static void fix_vt1802_connections(struct hda_codec *codec)
3672{
3673 static hda_nid_t conn_24[] = { 0x14, 0x1c };
3674 static hda_nid_t conn_33[] = { 0x1c };
3675
3676 snd_hda_override_conn_list(codec, 0x24, ARRAY_SIZE(conn_24), conn_24);
3677 snd_hda_override_conn_list(codec, 0x33, ARRAY_SIZE(conn_33), conn_33);
3678}
3679
Lydia Wang25eaba22009-10-10 19:08:43 +08003680/* patch for vt2002P */
3681static int patch_vt2002P(struct hda_codec *codec)
3682{
3683 struct via_spec *spec;
3684 int err;
3685
3686 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003687 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003688 if (spec == NULL)
3689 return -ENOMEM;
3690
Takashi Iwai620e2b22011-06-17 17:19:19 +02003691 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003692 override_mic_boost(codec, 0x2b, 0, 3, 40);
3693 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwaid2d0adc2012-11-07 10:37:48 +01003694 if (spec->codec_type == VT1802)
3695 fix_vt1802_connections(codec);
Takashi Iwai30b45032011-07-11 17:05:04 +02003696 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003697
Lydia Wang25eaba22009-10-10 19:08:43 +08003698 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003699 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003700 if (err < 0) {
3701 via_free(codec);
3702 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003703 }
3704
Lydia Wang118909562011-03-23 17:57:34 +08003705 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003706 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003707 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003708 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003709
Lydia Wang25eaba22009-10-10 19:08:43 +08003710 codec->patch_ops = via_patch_ops;
3711
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003712 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003713 return 0;
3714}
Lydia Wangab6734e2009-10-10 19:08:46 +08003715
3716/* for vt1812 */
3717
Takashi Iwai096a8852011-06-20 12:09:02 +02003718static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003719 /* Enable Boost Volume backdoor */
3720 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003721 /* Enable AOW0 to MW9 */
3722 {0x1, 0xfb8, 0xa8},
3723 { }
3724};
3725
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003726static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3727{
3728 struct via_spec *spec = codec->spec;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003729 unsigned int parm;
3730 unsigned int present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003731 /* inputs */
3732 /* PW 5/6/7 (29h/2ah/2bh) */
3733 parm = AC_PWRST_D3;
3734 set_pin_power_state(codec, 0x29, &parm);
3735 set_pin_power_state(codec, 0x2a, &parm);
3736 set_pin_power_state(codec, 0x2b, &parm);
3737 parm = AC_PWRST_D0;
3738 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003739 update_power_state(codec, 0x1e, parm);
3740 update_power_state(codec, 0x1f, parm);
3741 update_power_state(codec, 0x10, parm);
3742 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003743
3744 /* outputs */
3745 /* AOW0 (8h)*/
Takashi Iwai054d8672012-01-24 12:25:50 +01003746 update_power_state(codec, 0x8, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003747
3748 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3749 parm = AC_PWRST_D3;
3750 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003751 update_power_state(codec, 0x18, parm);
3752 update_power_state(codec, 0x38, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003753
3754 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3755 parm = AC_PWRST_D3;
3756 set_pin_power_state(codec, 0x25, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003757 update_power_state(codec, 0x15, parm);
3758 update_power_state(codec, 0x35, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003759 if (spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01003760 update_power_state(codec, 0x9, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003761
3762 /* Internal Speaker */
3763 /* PW0 (24h), MW0(14h), MUX0(34h) */
3764 present = snd_hda_jack_detect(codec, 0x25);
3765
3766 parm = AC_PWRST_D3;
3767 set_pin_power_state(codec, 0x24, &parm);
3768 if (present) {
Takashi Iwai054d8672012-01-24 12:25:50 +01003769 update_power_state(codec, 0x14, AC_PWRST_D3);
3770 update_power_state(codec, 0x34, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003771 } else {
Takashi Iwai054d8672012-01-24 12:25:50 +01003772 update_power_state(codec, 0x14, AC_PWRST_D0);
3773 update_power_state(codec, 0x34, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003774 }
3775
3776
3777 /* Mono Out */
3778 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3779 present = snd_hda_jack_detect(codec, 0x28);
3780
3781 parm = AC_PWRST_D3;
3782 set_pin_power_state(codec, 0x31, &parm);
3783 if (present) {
Takashi Iwai054d8672012-01-24 12:25:50 +01003784 update_power_state(codec, 0x1c, AC_PWRST_D3);
3785 update_power_state(codec, 0x3c, AC_PWRST_D3);
3786 update_power_state(codec, 0x3e, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003787 } else {
Takashi Iwai054d8672012-01-24 12:25:50 +01003788 update_power_state(codec, 0x1c, AC_PWRST_D0);
3789 update_power_state(codec, 0x3c, AC_PWRST_D0);
3790 update_power_state(codec, 0x3e, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003791 }
3792
3793 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3794 parm = AC_PWRST_D3;
3795 set_pin_power_state(codec, 0x33, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003796 update_power_state(codec, 0x1d, parm);
3797 update_power_state(codec, 0x3d, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003798
3799}
Lydia Wangab6734e2009-10-10 19:08:46 +08003800
3801/* patch for vt1812 */
3802static int patch_vt1812(struct hda_codec *codec)
3803{
3804 struct via_spec *spec;
3805 int err;
3806
3807 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003808 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003809 if (spec == NULL)
3810 return -ENOMEM;
3811
Takashi Iwai620e2b22011-06-17 17:19:19 +02003812 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003813 override_mic_boost(codec, 0x2b, 0, 3, 40);
3814 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003815 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003816
Lydia Wangab6734e2009-10-10 19:08:46 +08003817 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003818 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003819 if (err < 0) {
3820 via_free(codec);
3821 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003822 }
3823
Takashi Iwai096a8852011-06-20 12:09:02 +02003824 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003825
Lydia Wangab6734e2009-10-10 19:08:46 +08003826 codec->patch_ops = via_patch_ops;
3827
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003828 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003829 return 0;
3830}
3831
Joseph Chanc577b8a2006-11-29 15:29:40 +01003832/*
3833 * patch entries
3834 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003835static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003836 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3837 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3838 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3839 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3840 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003841 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003842 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003843 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003844 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003845 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003846 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003847 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003848 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003849 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003850 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003851 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003852 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003853 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003854 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003855 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003856 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003857 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003858 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003859 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003860 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003861 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003862 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003863 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003864 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003865 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003866 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003867 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003868 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003869 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003870 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003871 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003872 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003873 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003874 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003875 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003876 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003877 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003878 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003879 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003880 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003881 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003882 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003883 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003884 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003885 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003886 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003887 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003888 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003889 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003890 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003891 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003892 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003893 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003894 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003895 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003896 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003897 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003898 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003899 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003900 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003901 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003902 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003903 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003904 { .id = 0x11060428, .name = "VT1718S",
3905 .patch = patch_vt1718S},
3906 { .id = 0x11064428, .name = "VT1718S",
3907 .patch = patch_vt1718S},
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08003908 { .id = 0x11060441, .name = "VT2020",
3909 .patch = patch_vt1718S},
3910 { .id = 0x11064441, .name = "VT1828S",
3911 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003912 { .id = 0x11060433, .name = "VT1716S",
3913 .patch = patch_vt1716S},
3914 { .id = 0x1106a721, .name = "VT1716S",
3915 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003916 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3917 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003918 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003919 { .id = 0x11060440, .name = "VT1818S",
3920 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003921 { .id = 0x11060446, .name = "VT1802",
3922 .patch = patch_vt2002P},
3923 { .id = 0x11068446, .name = "VT1802",
3924 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003925 {} /* terminator */
3926};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003927
3928MODULE_ALIAS("snd-hda-codec-id:1106*");
3929
3930static struct hda_codec_preset_list via_list = {
3931 .preset = snd_hda_preset_via,
3932 .owner = THIS_MODULE,
3933};
3934
3935MODULE_LICENSE("GPL");
3936MODULE_DESCRIPTION("VIA HD-audio codec");
3937
3938static int __init patch_via_init(void)
3939{
3940 return snd_hda_add_codec_preset(&via_list);
3941}
3942
3943static void __exit patch_via_exit(void)
3944{
3945 snd_hda_delete_codec_preset(&via_list);
3946}
3947
3948module_init(patch_via_init)
3949module_exit(patch_via_exit)