blob: 79166fb8b0749d1f38a2e250933939d78aecacb8 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Paul Gortmakerda155d52011-07-15 12:38:28 -040052#include <linux/module.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010053#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080054#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010055#include "hda_codec.h"
56#include "hda_local.h"
Takashi Iwai1835a0f2011-10-27 22:12:46 +020057#include "hda_jack.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010058
Joseph Chanc577b8a2006-11-29 15:29:40 +010059/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080060#define VT1708_HP_PIN_NID 0x20
61#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010062
Harald Welted7426322008-09-15 22:43:23 +080063enum VIA_HDA_CODEC {
64 UNKNOWN = -1,
65 VT1708,
66 VT1709_10CH,
67 VT1709_6CH,
68 VT1708B_8CH,
69 VT1708B_4CH,
70 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080071 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080072 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080073 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080074 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080075 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080076 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080077 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080078 CODEC_TYPES,
79};
80
Lydia Wang118909562011-03-23 17:57:34 +080081#define VT2002P_COMPATIBLE(spec) \
82 ((spec)->codec_type == VT2002P ||\
83 (spec)->codec_type == VT1812 ||\
84 (spec)->codec_type == VT1802)
85
Takashi Iwai8e3679d2011-06-21 09:01:36 +020086#define MAX_NID_PATH_DEPTH 5
87
Takashi Iwai09a9ad692011-06-21 15:57:44 +020088/* output-path: DAC -> ... -> pin
89 * idx[] contains the source index number of the next widget;
90 * e.g. idx[0] is the index of the DAC selected by path[1] widget
91 * multi[] indicates whether it's a selector widget with multi-connectors
92 * (i.e. the connection selection is mandatory)
93 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
94 */
Takashi Iwai4a796162011-06-17 17:53:38 +020095struct nid_path {
96 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020097 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad692011-06-21 15:57:44 +020098 unsigned char idx[MAX_NID_PATH_DEPTH];
99 unsigned char multi[MAX_NID_PATH_DEPTH];
100 unsigned int vol_ctl;
101 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200102};
103
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200104/* input-path */
105struct via_input {
106 hda_nid_t pin; /* input-pin or aa-mix */
107 int adc_idx; /* ADC index to be used */
108 int mux_idx; /* MUX index (if any) */
109 const char *label; /* input-source label */
110};
111
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200112#define VIA_MAX_ADCS 3
113
Takashi Iwai3b607e32011-07-18 16:54:40 +0200114enum {
115 STREAM_MULTI_OUT = (1 << 0),
116 STREAM_INDEP_HP = (1 << 1),
117};
118
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800119struct via_spec {
120 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200121 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800122 unsigned int num_mixers;
123
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200124 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800125 unsigned int num_iverbs;
126
Takashi Iwai82673bc2011-06-17 16:24:21 +0200127 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200128 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200129 const struct hda_pcm_stream *stream_analog_playback;
130 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800131
Takashi Iwai82673bc2011-06-17 16:24:21 +0200132 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200133 const struct hda_pcm_stream *stream_digital_playback;
134 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800135
136 /* playback */
137 struct hda_multi_out multiout;
138 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200139 hda_nid_t hp_dac_nid;
Takashi Iwai3214b962011-07-18 12:49:25 +0200140 hda_nid_t speaker_dac_nid;
141 int hp_indep_shared; /* indep HP-DAC is shared with side ch */
Takashi Iwai3b607e32011-07-18 16:54:40 +0200142 int opened_streams; /* STREAM_* bits */
143 int active_streams; /* STREAM_* bits */
Takashi Iwai3214b962011-07-18 12:49:25 +0200144 int aamix_mode; /* loopback is enabled for output-path? */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800145
Takashi Iwai3214b962011-07-18 12:49:25 +0200146 /* Output-paths:
147 * There are different output-paths depending on the setup.
148 * out_path, hp_path and speaker_path are primary paths. If both
149 * direct DAC and aa-loopback routes are available, these contain
150 * the former paths. Meanwhile *_mix_path contain the paths with
151 * loopback mixer. (Since the loopback is only for front channel,
152 * no out_mix_path for surround channels.)
153 * The HP output has another path, hp_indep_path, which is used in
154 * the independent-HP mode.
155 */
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200156 struct nid_path out_path[HDA_SIDE + 1];
Takashi Iwai3214b962011-07-18 12:49:25 +0200157 struct nid_path out_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200158 struct nid_path hp_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200159 struct nid_path hp_mix_path;
160 struct nid_path hp_indep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200161 struct nid_path speaker_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200162 struct nid_path speaker_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200163
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800164 /* capture */
165 unsigned int num_adc_nids;
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200166 hda_nid_t adc_nids[VIA_MAX_ADCS];
167 hda_nid_t mux_nids[VIA_MAX_ADCS];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200168 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800169 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800170
171 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200172 bool dyn_adc_switch;
173 int num_inputs;
174 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200175 unsigned int cur_mux[VIA_MAX_ADCS];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800176
Takashi Iwai3b607e32011-07-18 16:54:40 +0200177 /* dynamic DAC switching */
178 unsigned int cur_dac_stream_tag;
179 unsigned int cur_dac_format;
180 unsigned int cur_hp_stream_tag;
181 unsigned int cur_hp_format;
182
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200183 /* dynamic ADC switching */
184 hda_nid_t cur_adc;
185 unsigned int cur_adc_stream_tag;
186 unsigned int cur_adc_format;
187
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800188 /* PCM information */
189 struct hda_pcm pcm_rec[3];
190
191 /* dynamic controls, init_verbs and input_mux */
192 struct auto_pin_cfg autocfg;
193 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800194 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
195
196 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800197 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800198 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200199 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800200 enum VIA_HDA_CODEC codec_type;
201
Takashi Iwaie9d010c2012-02-01 10:33:23 +0100202 /* analog low-power control */
203 bool alc_mode;
204
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200205 /* smart51 setup */
206 unsigned int smart51_nums;
207 hda_nid_t smart51_pins[2];
208 int smart51_idxs[2];
209 const char *smart51_labels[2];
210 unsigned int smart51_enabled;
211
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800212 /* work to check hp jack state */
213 struct hda_codec *codec;
214 struct delayed_work vt1708_hp_work;
Takashi Iwai187d3332011-11-24 16:33:09 +0100215 int hp_work_active;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200216 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800217 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800218
219 void (*set_widgets_power_state)(struct hda_codec *codec);
220
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800221 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200222 int num_loopbacks;
223 struct hda_amp_list loopback_list[8];
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200224
225 /* bind capture-volume */
226 struct hda_bind_ctls *bind_cap_vol;
227 struct hda_bind_ctls *bind_cap_sw;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200228
229 struct mutex config_mutex;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800230};
231
Lydia Wang0341ccd2011-03-22 16:25:03 +0800232static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100233static struct via_spec * via_new_spec(struct hda_codec *codec)
234{
235 struct via_spec *spec;
236
237 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
238 if (spec == NULL)
239 return NULL;
240
Takashi Iwai3b607e32011-07-18 16:54:40 +0200241 mutex_init(&spec->config_mutex);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100242 codec->spec = spec;
243 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800244 spec->codec_type = get_codec_type(codec);
245 /* VT1708BCE & VT1708S are almost same */
246 if (spec->codec_type == VT1708BCE)
247 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100248 return spec;
249}
250
Lydia Wang744ff5f2009-10-10 19:07:26 +0800251static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800252{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800253 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800254 u16 ven_id = vendor_id >> 16;
255 u16 dev_id = vendor_id & 0xffff;
256 enum VIA_HDA_CODEC codec_type;
257
258 /* get codec type */
259 if (ven_id != 0x1106)
260 codec_type = UNKNOWN;
261 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
262 codec_type = VT1708;
263 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
264 codec_type = VT1709_10CH;
265 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
266 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800267 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800268 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800269 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
270 codec_type = VT1708BCE;
271 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800272 codec_type = VT1708B_4CH;
273 else if ((dev_id & 0xfff) == 0x397
274 && (dev_id >> 12) < 8)
275 codec_type = VT1708S;
276 else if ((dev_id & 0xfff) == 0x398
277 && (dev_id >> 12) < 8)
278 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800279 else if ((dev_id & 0xfff) == 0x428
280 && (dev_id >> 12) < 8)
281 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800282 else if (dev_id == 0x0433 || dev_id == 0xa721)
283 codec_type = VT1716S;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +0800284 else if (dev_id == 0x0441 || dev_id == 0x4441)
285 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800286 else if (dev_id == 0x0438 || dev_id == 0x4438)
287 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800288 else if (dev_id == 0x0448)
289 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800290 else if (dev_id == 0x0440)
291 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800292 else if ((dev_id & 0xfff) == 0x446)
293 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800294 else
295 codec_type = UNKNOWN;
296 return codec_type;
297};
298
Lydia Wangec7e7e42011-03-24 12:43:44 +0800299#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800300#define VIA_HP_EVENT 0x01
301#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200302#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800303
Joseph Chanc577b8a2006-11-29 15:29:40 +0100304enum {
305 VIA_CTL_WIDGET_VOL,
306 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800307 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100308};
309
Takashi Iwaiada509e2011-06-20 15:40:19 +0200310static void analog_low_current_mode(struct hda_codec *codec);
311static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800312
Takashi Iwai187d3332011-11-24 16:33:09 +0100313#define hp_detect_with_aa(codec) \
314 (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
315 !is_aa_path_mute(codec))
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800316
317static void vt1708_stop_hp_work(struct via_spec *spec)
318{
319 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
320 return;
Takashi Iwai187d3332011-11-24 16:33:09 +0100321 if (spec->hp_work_active) {
322 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 1);
323 cancel_delayed_work_sync(&spec->vt1708_hp_work);
324 spec->hp_work_active = 0;
325 }
326}
327
328static void vt1708_update_hp_work(struct via_spec *spec)
329{
330 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800331 return;
Takashi Iwai187d3332011-11-24 16:33:09 +0100332 if (spec->vt1708_jack_detect &&
333 (spec->active_streams || hp_detect_with_aa(spec->codec))) {
334 if (!spec->hp_work_active) {
335 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 0);
336 schedule_delayed_work(&spec->vt1708_hp_work,
337 msecs_to_jiffies(100));
338 spec->hp_work_active = 1;
339 }
340 } else if (!hp_detect_with_aa(spec->codec))
341 vt1708_stop_hp_work(spec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800342}
Lydia Wangf5271102009-10-10 19:07:35 +0800343
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800344static void set_widgets_power_state(struct hda_codec *codec)
345{
346 struct via_spec *spec = codec->spec;
347 if (spec->set_widgets_power_state)
348 spec->set_widgets_power_state(codec);
349}
Lydia Wang25eaba22009-10-10 19:08:43 +0800350
Lydia Wangf5271102009-10-10 19:07:35 +0800351static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
352 struct snd_ctl_elem_value *ucontrol)
353{
354 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
355 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
356
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800357 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200358 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Takashi Iwai187d3332011-11-24 16:33:09 +0100359 vt1708_update_hp_work(codec->spec);
Lydia Wangf5271102009-10-10 19:07:35 +0800360 return change;
361}
362
363/* modify .put = snd_hda_mixer_amp_switch_put */
364#define ANALOG_INPUT_MUTE \
365 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
366 .name = NULL, \
367 .index = 0, \
368 .info = snd_hda_mixer_amp_switch_info, \
369 .get = snd_hda_mixer_amp_switch_get, \
370 .put = analog_input_switch_put, \
371 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
372
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200373static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100374 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
375 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800376 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100377};
378
Lydia Wangab6734e2009-10-10 19:08:46 +0800379
Joseph Chanc577b8a2006-11-29 15:29:40 +0100380/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200381static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
382 const struct snd_kcontrol_new *tmpl,
383 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100384{
385 struct snd_kcontrol_new *knew;
386
Takashi Iwai603c4012008-07-30 15:01:44 +0200387 snd_array_init(&spec->kctls, sizeof(*knew), 32);
388 knew = snd_array_new(&spec->kctls);
389 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200390 return NULL;
391 *knew = *tmpl;
392 if (!name)
393 name = tmpl->name;
394 if (name) {
395 knew->name = kstrdup(name, GFP_KERNEL);
396 if (!knew->name)
397 return NULL;
398 }
399 return knew;
400}
401
402static int __via_add_control(struct via_spec *spec, int type, const char *name,
403 int idx, unsigned long val)
404{
405 struct snd_kcontrol_new *knew;
406
407 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
408 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100409 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200410 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100411 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100412 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100413 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100414 return 0;
415}
416
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200417#define via_add_control(spec, type, name, val) \
418 __via_add_control(spec, type, name, 0, val)
419
Takashi Iwai291c9e32011-06-17 16:15:26 +0200420#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100421
Takashi Iwai603c4012008-07-30 15:01:44 +0200422static void via_free_kctls(struct hda_codec *codec)
423{
424 struct via_spec *spec = codec->spec;
425
426 if (spec->kctls.list) {
427 struct snd_kcontrol_new *kctl = spec->kctls.list;
428 int i;
429 for (i = 0; i < spec->kctls.used; i++)
430 kfree(kctl[i].name);
431 }
432 snd_array_free(&spec->kctls);
433}
434
Joseph Chanc577b8a2006-11-29 15:29:40 +0100435/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800436static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200437 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100438{
439 char name[32];
440 int err;
441
442 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200443 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100444 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
445 if (err < 0)
446 return err;
447 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200448 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100449 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
450 if (err < 0)
451 return err;
452 return 0;
453}
454
Takashi Iwai5d417622011-06-20 11:32:27 +0200455#define get_connection_index(codec, mux, nid) \
Takashi Iwai8d087c72011-06-28 12:45:47 +0200456 snd_hda_get_conn_index(codec, mux, nid, 0)
Takashi Iwai5d417622011-06-20 11:32:27 +0200457
Takashi Iwai8df2a312011-06-21 11:48:29 +0200458static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
459 unsigned int mask)
460{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200461 unsigned int caps;
462 if (!nid)
463 return false;
464 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200465 if (dir == HDA_INPUT)
466 caps &= AC_WCAP_IN_AMP;
467 else
468 caps &= AC_WCAP_OUT_AMP;
469 if (!caps)
470 return false;
471 if (query_amp_caps(codec, nid, dir) & mask)
472 return true;
473 return false;
474}
475
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200476#define have_mute(codec, nid, dir) \
477 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200478
Lydia 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 Iwai09a9ad692011-06-21 15:57:44 +0200498/* enable/disable the output-route */
499static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
500 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200501{
Lydia Wangd69607b2011-07-08 14:02:52 +0800502 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200503 int i;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200504 for (i = 0; i < path->depth; i++) {
505 hda_nid_t src, dst;
506 int idx = path->idx[i];
507 src = path->path[i];
508 if (i < path->depth - 1)
509 dst = path->path[i + 1];
510 else
511 dst = 0;
512 if (enable && path->multi[i])
513 snd_hda_codec_write(codec, dst, 0,
514 AC_VERB_SET_CONNECT_SEL, idx);
Takashi Iwai3214b962011-07-18 12:49:25 +0200515 if (!force && (dst == spec->aa_mix_nid))
Lydia Wange5e14682011-07-01 10:55:07 +0800516 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +0200517 if (have_mute(codec, dst, HDA_INPUT))
518 activate_output_mix(codec, path, dst, idx, enable);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200519 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
520 continue;
521 if (have_mute(codec, src, HDA_OUTPUT)) {
522 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
523 snd_hda_codec_write(codec, src, 0,
524 AC_VERB_SET_AMP_GAIN_MUTE, val);
525 }
526 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200527}
528
529/* set the given pin as output */
530static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
531 int pin_type)
532{
533 if (!pin)
534 return;
535 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
536 pin_type);
537 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
538 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200539 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100540}
541
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200542static void via_auto_init_output(struct hda_codec *codec,
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200543 struct nid_path *path, int pin_type)
Takashi Iwai5d417622011-06-20 11:32:27 +0200544{
Takashi Iwai5d417622011-06-20 11:32:27 +0200545 unsigned int caps;
Lydia Wangd69607b2011-07-08 14:02:52 +0800546 hda_nid_t pin;
Takashi Iwai5d417622011-06-20 11:32:27 +0200547
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200548 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200549 return;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200550 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200551
552 init_output_pin(codec, pin, pin_type);
553 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
554 if (caps & AC_AMPCAP_MUTE) {
555 unsigned int val;
556 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
557 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
558 AMP_OUT_MUTE | val);
559 }
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200560 activate_output_path(codec, path, true, true); /* force on */
Takashi Iwai5d417622011-06-20 11:32:27 +0200561}
562
Joseph Chanc577b8a2006-11-29 15:29:40 +0100563static void via_auto_init_multi_out(struct hda_codec *codec)
564{
565 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200566 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100567 int i;
568
Takashi Iwai3214b962011-07-18 12:49:25 +0200569 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) {
570 path = &spec->out_path[i];
571 if (!i && spec->aamix_mode && spec->out_mix_path.depth)
572 path = &spec->out_mix_path;
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200573 via_auto_init_output(codec, path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200574 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100575}
576
Takashi Iwai020066d2011-07-21 13:45:56 +0200577/* deactivate the inactive headphone-paths */
578static void deactivate_hp_paths(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100579{
580 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200581 int shared = spec->hp_indep_shared;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100582
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200583 if (spec->hp_independent_mode) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200584 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200585 activate_output_path(codec, &spec->hp_mix_path, false, false);
586 if (shared)
587 activate_output_path(codec, &spec->out_path[shared],
588 false, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200589 } else if (spec->aamix_mode || !spec->hp_path.depth) {
590 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200591 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200592 } else {
Takashi Iwai020066d2011-07-21 13:45:56 +0200593 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200594 activate_output_path(codec, &spec->hp_mix_path, false, false);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200595 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100596}
597
Takashi Iwai020066d2011-07-21 13:45:56 +0200598static void via_auto_init_hp_out(struct hda_codec *codec)
599{
600 struct via_spec *spec = codec->spec;
601
602 if (!spec->hp_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200603 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200604 return;
605 }
606 deactivate_hp_paths(codec);
607 if (spec->hp_independent_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200608 via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200609 else if (spec->aamix_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200610 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200611 else
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200612 via_auto_init_output(codec, &spec->hp_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200613}
614
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200615static void via_auto_init_speaker_out(struct hda_codec *codec)
616{
617 struct via_spec *spec = codec->spec;
618
Takashi Iwai3214b962011-07-18 12:49:25 +0200619 if (!spec->autocfg.speaker_outs)
620 return;
621 if (!spec->speaker_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200622 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200623 return;
624 }
625 if (!spec->aamix_mode) {
626 activate_output_path(codec, &spec->speaker_mix_path,
627 false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200628 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200629 } else {
630 activate_output_path(codec, &spec->speaker_path, false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200631 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200632 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200633}
634
Takashi Iwaif4a78282011-06-17 18:46:48 +0200635static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200636static void via_hp_automute(struct hda_codec *codec);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200637
Joseph Chanc577b8a2006-11-29 15:29:40 +0100638static void via_auto_init_analog_input(struct hda_codec *codec)
639{
640 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200641 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200642 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200643 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200644 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100645
Takashi Iwai096a8852011-06-20 12:09:02 +0200646 /* init ADCs */
647 for (i = 0; i < spec->num_adc_nids; i++) {
648 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
649 AC_VERB_SET_AMP_GAIN_MUTE,
650 AMP_IN_UNMUTE(0));
651 }
652
653 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200654 for (i = 0; i < cfg->num_inputs; i++) {
655 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200656 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200657 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100658 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200659 ctl = PIN_VREF50;
660 else
661 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100662 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200663 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100664 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200665
666 /* init input-src */
667 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200668 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
669 if (spec->mux_nids[adc_idx]) {
670 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
671 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
672 AC_VERB_SET_CONNECT_SEL,
673 mux_idx);
674 }
675 if (spec->dyn_adc_switch)
676 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200677 }
678
679 /* init aa-mixer */
680 if (!spec->aa_mix_nid)
681 return;
682 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
683 ARRAY_SIZE(conn));
684 for (i = 0; i < num_conns; i++) {
685 unsigned int caps = get_wcaps(codec, conn[i]);
686 if (get_wcaps_type(caps) == AC_WID_PIN)
687 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
688 AC_VERB_SET_AMP_GAIN_MUTE,
689 AMP_IN_MUTE(i));
690 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100691}
Lydia Wangf5271102009-10-10 19:07:35 +0800692
Takashi Iwai054d8672012-01-24 12:25:50 +0100693static void update_power_state(struct hda_codec *codec, hda_nid_t nid,
694 unsigned int parm)
695{
696 if (snd_hda_codec_read(codec, nid, 0,
697 AC_VERB_GET_POWER_STATE, 0) == parm)
698 return;
699 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
700}
701
Lydia Wangf5271102009-10-10 19:07:35 +0800702static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
703 unsigned int *affected_parm)
704{
705 unsigned parm;
706 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
707 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
708 >> AC_DEFCFG_MISC_SHIFT
709 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800710 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200711 unsigned present = 0;
712
713 no_presence |= spec->no_pin_power_ctl;
714 if (!no_presence)
715 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200716 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800717 || ((no_presence || present)
718 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800719 *affected_parm = AC_PWRST_D0; /* if it's connected */
720 parm = AC_PWRST_D0;
721 } else
722 parm = AC_PWRST_D3;
723
Takashi Iwai054d8672012-01-24 12:25:50 +0100724 update_power_state(codec, nid, parm);
Lydia Wangf5271102009-10-10 19:07:35 +0800725}
726
Takashi Iwai24088a52011-06-17 16:59:21 +0200727static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
728 struct snd_ctl_elem_info *uinfo)
729{
730 static const char * const texts[] = {
731 "Disabled", "Enabled"
732 };
733
734 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
735 uinfo->count = 1;
736 uinfo->value.enumerated.items = 2;
737 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
738 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
739 strcpy(uinfo->value.enumerated.name,
740 texts[uinfo->value.enumerated.item]);
741 return 0;
742}
743
744static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
745 struct snd_ctl_elem_value *ucontrol)
746{
747 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
748 struct via_spec *spec = codec->spec;
749 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
750 return 0;
751}
752
753static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
754 struct snd_ctl_elem_value *ucontrol)
755{
756 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
757 struct via_spec *spec = codec->spec;
758 unsigned int val = !ucontrol->value.enumerated.item[0];
759
760 if (val == spec->no_pin_power_ctl)
761 return 0;
762 spec->no_pin_power_ctl = val;
763 set_widgets_power_state(codec);
Takashi Iwaie9d010c2012-02-01 10:33:23 +0100764 analog_low_current_mode(codec);
Takashi Iwai24088a52011-06-17 16:59:21 +0200765 return 1;
766}
767
768static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
769 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
770 .name = "Dynamic Power-Control",
771 .info = via_pin_power_ctl_info,
772 .get = via_pin_power_ctl_get,
773 .put = via_pin_power_ctl_put,
774};
775
776
Harald Welte0aa62ae2008-09-09 15:58:27 +0800777static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
778 struct snd_ctl_elem_info *uinfo)
779{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200780 static const char * const texts[] = { "OFF", "ON" };
781
782 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
783 uinfo->count = 1;
784 uinfo->value.enumerated.items = 2;
785 if (uinfo->value.enumerated.item >= 2)
786 uinfo->value.enumerated.item = 1;
787 strcpy(uinfo->value.enumerated.name,
788 texts[uinfo->value.enumerated.item]);
789 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800790}
791
792static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
793 struct snd_ctl_elem_value *ucontrol)
794{
795 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800796 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800797
Takashi Iwaiece8d042011-06-19 16:24:21 +0200798 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800799 return 0;
800}
801
Takashi Iwai3b607e32011-07-18 16:54:40 +0200802/* adjust spec->multiout setup according to the current flags */
803static void setup_playback_multi_pcm(struct via_spec *spec)
804{
805 const struct auto_pin_cfg *cfg = &spec->autocfg;
806 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
807 spec->multiout.hp_nid = 0;
808 if (!spec->hp_independent_mode) {
809 if (!spec->hp_indep_shared)
810 spec->multiout.hp_nid = spec->hp_dac_nid;
811 } else {
812 if (spec->hp_indep_shared)
813 spec->multiout.num_dacs = cfg->line_outs - 1;
814 }
815}
816
817/* update DAC setups according to indep-HP switch;
818 * this function is called only when indep-HP is modified
819 */
820static void switch_indep_hp_dacs(struct hda_codec *codec)
821{
822 struct via_spec *spec = codec->spec;
823 int shared = spec->hp_indep_shared;
824 hda_nid_t shared_dac, hp_dac;
825
826 if (!spec->opened_streams)
827 return;
828
829 shared_dac = shared ? spec->multiout.dac_nids[shared] : 0;
830 hp_dac = spec->hp_dac_nid;
831 if (spec->hp_independent_mode) {
832 /* switch to indep-HP mode */
833 if (spec->active_streams & STREAM_MULTI_OUT) {
834 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
835 __snd_hda_codec_cleanup_stream(codec, shared_dac, 1);
836 }
837 if (spec->active_streams & STREAM_INDEP_HP)
838 snd_hda_codec_setup_stream(codec, hp_dac,
839 spec->cur_hp_stream_tag, 0,
840 spec->cur_hp_format);
841 } else {
842 /* back to HP or shared-DAC */
843 if (spec->active_streams & STREAM_INDEP_HP)
844 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
845 if (spec->active_streams & STREAM_MULTI_OUT) {
846 hda_nid_t dac;
847 int ch;
848 if (shared_dac) { /* reset mutli-ch DAC */
849 dac = shared_dac;
850 ch = shared * 2;
851 } else { /* reset HP DAC */
852 dac = hp_dac;
853 ch = 0;
854 }
855 snd_hda_codec_setup_stream(codec, dac,
856 spec->cur_dac_stream_tag, ch,
857 spec->cur_dac_format);
858 }
859 }
860 setup_playback_multi_pcm(spec);
861}
862
Harald Welte0aa62ae2008-09-09 15:58:27 +0800863static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
864 struct snd_ctl_elem_value *ucontrol)
865{
866 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
867 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200868 int cur, shared;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200869
Takashi Iwai3b607e32011-07-18 16:54:40 +0200870 mutex_lock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200871 cur = !!ucontrol->value.enumerated.item[0];
Takashi Iwai3b607e32011-07-18 16:54:40 +0200872 if (spec->hp_independent_mode == cur) {
873 mutex_unlock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200874 return 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200875 }
Takashi Iwai25250502011-06-30 17:24:47 +0200876 spec->hp_independent_mode = cur;
Takashi Iwai3214b962011-07-18 12:49:25 +0200877 shared = spec->hp_indep_shared;
Takashi Iwai020066d2011-07-21 13:45:56 +0200878 deactivate_hp_paths(codec);
879 if (cur)
880 activate_output_path(codec, &spec->hp_indep_path, true, false);
881 else {
Takashi Iwai3214b962011-07-18 12:49:25 +0200882 if (shared)
883 activate_output_path(codec, &spec->out_path[shared],
Takashi Iwai25250502011-06-30 17:24:47 +0200884 true, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200885 if (spec->aamix_mode || !spec->hp_path.depth)
886 activate_output_path(codec, &spec->hp_mix_path,
887 true, false);
888 else
889 activate_output_path(codec, &spec->hp_path,
890 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200891 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800892
Takashi Iwai3b607e32011-07-18 16:54:40 +0200893 switch_indep_hp_dacs(codec);
894 mutex_unlock(&spec->config_mutex);
895
Lydia Wangce0e5a92011-03-22 16:22:37 +0800896 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800897 set_widgets_power_state(codec);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200898 via_hp_automute(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200899 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800900}
901
Takashi Iwaiece8d042011-06-19 16:24:21 +0200902static const struct snd_kcontrol_new via_hp_mixer = {
903 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
904 .name = "Independent HP",
905 .info = via_independent_hp_info,
906 .get = via_independent_hp_get,
907 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800908};
909
Takashi Iwai3d83e572010-04-14 14:36:23 +0200910static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100911{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200912 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100913 struct snd_kcontrol_new *knew;
914 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100915
Takashi Iwaiece8d042011-06-19 16:24:21 +0200916 nid = spec->autocfg.hp_pins[0];
917 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200918 if (knew == NULL)
919 return -ENOMEM;
920
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100921 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100922
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100923 return 0;
924}
925
Lydia Wang1564b282009-10-10 19:07:52 +0800926static void notify_aa_path_ctls(struct hda_codec *codec)
927{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200928 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800929 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800930
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200931 for (i = 0; i < spec->smart51_nums; i++) {
932 struct snd_kcontrol *ctl;
933 struct snd_ctl_elem_id id;
934 memset(&id, 0, sizeof(id));
935 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
936 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800937 ctl = snd_hda_find_mixer_ctl(codec, id.name);
938 if (ctl)
939 snd_ctl_notify(codec->bus->card,
940 SNDRV_CTL_EVENT_MASK_VALUE,
941 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800942 }
943}
944
945static void mute_aa_path(struct hda_codec *codec, int mute)
946{
947 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200948 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800949 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200950
Lydia Wang1564b282009-10-10 19:07:52 +0800951 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200952 for (i = 0; i < spec->smart51_nums; i++) {
953 if (spec->smart51_idxs[i] < 0)
954 continue;
955 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
956 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800957 HDA_AMP_MUTE, val);
958 }
959}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200960
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200961static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
962{
963 struct via_spec *spec = codec->spec;
964 int i;
965
966 for (i = 0; i < spec->smart51_nums; i++)
967 if (spec->smart51_pins[i] == pin)
968 return true;
969 return false;
970}
971
Lydia Wang1564b282009-10-10 19:07:52 +0800972static int via_smart51_get(struct snd_kcontrol *kcontrol,
973 struct snd_ctl_elem_value *ucontrol)
974{
975 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
976 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800977
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200978 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800979 return 0;
980}
981
982static int via_smart51_put(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;
987 int out_in = *ucontrol->value.integer.value
988 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800989 int i;
990
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200991 for (i = 0; i < spec->smart51_nums; i++) {
992 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200993 unsigned int parm;
994
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200995 parm = snd_hda_codec_read(codec, nid, 0,
996 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
997 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
998 parm |= out_in;
999 snd_hda_codec_write(codec, nid, 0,
1000 AC_VERB_SET_PIN_WIDGET_CONTROL,
1001 parm);
1002 if (out_in == AC_PINCTL_OUT_EN) {
1003 mute_aa_path(codec, 1);
1004 notify_aa_path_ctls(codec);
1005 }
Lydia Wang1564b282009-10-10 19:07:52 +08001006 }
1007 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001008 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +08001009 return 1;
1010}
1011
Takashi Iwai5f4b36d2011-06-17 14:55:02 +02001012static const struct snd_kcontrol_new via_smart51_mixer = {
1013 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1014 .name = "Smart 5.1",
1015 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001016 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +02001017 .get = via_smart51_get,
1018 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +08001019};
1020
Takashi Iwaif4a78282011-06-17 18:46:48 +02001021static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001022{
Takashi Iwaif4a78282011-06-17 18:46:48 +02001023 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001024
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001025 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +08001026 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001027 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001028 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001029 return 0;
1030}
1031
Takashi Iwaiada509e2011-06-20 15:40:19 +02001032/* check AA path's mute status */
1033static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001034{
Lydia Wangf5271102009-10-10 19:07:35 +08001035 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001036 const struct hda_amp_list *p;
1037 int i, ch, v;
1038
1039 for (i = 0; i < spec->num_loopbacks; i++) {
1040 p = &spec->loopback_list[i];
1041 for (ch = 0; ch < 2; ch++) {
1042 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
1043 p->idx);
1044 if (!(v & HDA_AMP_MUTE) && v > 0)
1045 return false;
Lydia Wangf5271102009-10-10 19:07:35 +08001046 }
1047 }
Takashi Iwaiada509e2011-06-20 15:40:19 +02001048 return true;
Lydia Wangf5271102009-10-10 19:07:35 +08001049}
1050
1051/* enter/exit analog low-current mode */
Takashi Iwaie9d010c2012-02-01 10:33:23 +01001052static void __analog_low_current_mode(struct hda_codec *codec, bool force)
Lydia Wangf5271102009-10-10 19:07:35 +08001053{
1054 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001055 bool enable;
1056 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +08001057
Takashi Iwaie9d010c2012-02-01 10:33:23 +01001058 if (spec->no_pin_power_ctl)
1059 enable = false;
1060 else
1061 enable = is_aa_path_mute(codec) && !spec->opened_streams;
1062 if (enable == spec->alc_mode && !force)
1063 return;
1064 spec->alc_mode = enable;
Lydia Wangf5271102009-10-10 19:07:35 +08001065
1066 /* decide low current mode's verb & parameter */
1067 switch (spec->codec_type) {
1068 case VT1708B_8CH:
1069 case VT1708B_4CH:
1070 verb = 0xf70;
1071 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1072 break;
1073 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001074 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001075 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001076 verb = 0xf73;
1077 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1078 break;
1079 case VT1702:
1080 verb = 0xf73;
1081 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1082 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001083 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001084 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +08001085 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +08001086 verb = 0xf93;
1087 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1088 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001089 default:
1090 return; /* other codecs are not supported */
1091 }
1092 /* send verb */
1093 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1094}
1095
Takashi Iwaie9d010c2012-02-01 10:33:23 +01001096static void analog_low_current_mode(struct hda_codec *codec)
1097{
1098 return __analog_low_current_mode(codec, false);
1099}
1100
Joseph Chanc577b8a2006-11-29 15:29:40 +01001101/*
1102 * generic initialization of ADC, input mixers and output mixers
1103 */
Takashi Iwai096a8852011-06-20 12:09:02 +02001104static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001105 /* power down jack detect function */
1106 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001107 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001108};
1109
Takashi Iwai3b607e32011-07-18 16:54:40 +02001110static void set_stream_open(struct hda_codec *codec, int bit, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001111{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001112 struct via_spec *spec = codec->spec;
1113
1114 if (active)
Takashi Iwai3b607e32011-07-18 16:54:40 +02001115 spec->opened_streams |= bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001116 else
Takashi Iwai3b607e32011-07-18 16:54:40 +02001117 spec->opened_streams &= ~bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001118 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001119}
1120
Takashi Iwaiece8d042011-06-19 16:24:21 +02001121static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001122 struct hda_codec *codec,
1123 struct snd_pcm_substream *substream)
1124{
1125 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001126 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001127 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001128
Takashi Iwai25250502011-06-30 17:24:47 +02001129 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
Takashi Iwai25250502011-06-30 17:24:47 +02001130 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001131 set_stream_open(codec, STREAM_MULTI_OUT, true);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001132 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1133 hinfo);
1134 if (err < 0) {
Takashi Iwai3b607e32011-07-18 16:54:40 +02001135 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001136 return err;
1137 }
1138 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001139}
1140
Takashi Iwaiece8d042011-06-19 16:24:21 +02001141static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001142 struct hda_codec *codec,
1143 struct snd_pcm_substream *substream)
1144{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001145 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001146 return 0;
1147}
1148
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001149static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1150 struct hda_codec *codec,
1151 struct snd_pcm_substream *substream)
1152{
1153 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001154
Takashi Iwaiece8d042011-06-19 16:24:21 +02001155 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001156 return -EINVAL;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001157 set_stream_open(codec, STREAM_INDEP_HP, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001158 return 0;
1159}
1160
1161static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1162 struct hda_codec *codec,
1163 struct snd_pcm_substream *substream)
1164{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001165 set_stream_open(codec, STREAM_INDEP_HP, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001166 return 0;
1167}
1168
1169static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1170 struct hda_codec *codec,
1171 unsigned int stream_tag,
1172 unsigned int format,
1173 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001174{
1175 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001176
Takashi Iwai3b607e32011-07-18 16:54:40 +02001177 mutex_lock(&spec->config_mutex);
1178 setup_playback_multi_pcm(spec);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001179 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1180 format, substream);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001181 /* remember for dynamic DAC switch with indep-HP */
1182 spec->active_streams |= STREAM_MULTI_OUT;
1183 spec->cur_dac_stream_tag = stream_tag;
1184 spec->cur_dac_format = format;
1185 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001186 vt1708_update_hp_work(spec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001187 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001188}
1189
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001190static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1191 struct hda_codec *codec,
1192 unsigned int stream_tag,
1193 unsigned int format,
1194 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001195{
1196 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001197
Takashi Iwai3b607e32011-07-18 16:54:40 +02001198 mutex_lock(&spec->config_mutex);
1199 if (spec->hp_independent_mode)
1200 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1201 stream_tag, 0, format);
1202 spec->active_streams |= STREAM_INDEP_HP;
1203 spec->cur_hp_stream_tag = stream_tag;
1204 spec->cur_hp_format = format;
1205 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001206 vt1708_update_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001207 return 0;
1208}
1209
1210static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1211 struct hda_codec *codec,
1212 struct snd_pcm_substream *substream)
1213{
1214 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001215
Takashi Iwai3b607e32011-07-18 16:54:40 +02001216 mutex_lock(&spec->config_mutex);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001217 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001218 spec->active_streams &= ~STREAM_MULTI_OUT;
1219 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001220 vt1708_update_hp_work(spec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001221 return 0;
1222}
1223
1224static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1225 struct hda_codec *codec,
1226 struct snd_pcm_substream *substream)
1227{
1228 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001229
Takashi Iwai3b607e32011-07-18 16:54:40 +02001230 mutex_lock(&spec->config_mutex);
1231 if (spec->hp_independent_mode)
1232 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
1233 spec->active_streams &= ~STREAM_INDEP_HP;
1234 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001235 vt1708_update_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001236 return 0;
1237}
1238
Joseph Chanc577b8a2006-11-29 15:29:40 +01001239/*
1240 * Digital out
1241 */
1242static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1243 struct hda_codec *codec,
1244 struct snd_pcm_substream *substream)
1245{
1246 struct via_spec *spec = codec->spec;
1247 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1248}
1249
1250static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1251 struct hda_codec *codec,
1252 struct snd_pcm_substream *substream)
1253{
1254 struct via_spec *spec = codec->spec;
1255 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1256}
1257
Harald Welte5691ec72008-09-15 22:42:26 +08001258static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001259 struct hda_codec *codec,
1260 unsigned int stream_tag,
1261 unsigned int format,
1262 struct snd_pcm_substream *substream)
1263{
1264 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001265 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1266 stream_tag, format, substream);
1267}
Harald Welte5691ec72008-09-15 22:42:26 +08001268
Takashi Iwai9da29272009-05-07 16:31:14 +02001269static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1270 struct hda_codec *codec,
1271 struct snd_pcm_substream *substream)
1272{
1273 struct via_spec *spec = codec->spec;
1274 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001275 return 0;
1276}
1277
Joseph Chanc577b8a2006-11-29 15:29:40 +01001278/*
1279 * Analog capture
1280 */
1281static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1282 struct hda_codec *codec,
1283 unsigned int stream_tag,
1284 unsigned int format,
1285 struct snd_pcm_substream *substream)
1286{
1287 struct via_spec *spec = codec->spec;
1288
1289 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1290 stream_tag, 0, format);
1291 return 0;
1292}
1293
1294static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1295 struct hda_codec *codec,
1296 struct snd_pcm_substream *substream)
1297{
1298 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001299 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001300 return 0;
1301}
1302
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001303/* analog capture with dynamic ADC switching */
1304static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1305 struct hda_codec *codec,
1306 unsigned int stream_tag,
1307 unsigned int format,
1308 struct snd_pcm_substream *substream)
1309{
1310 struct via_spec *spec = codec->spec;
1311 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1312
Takashi Iwai3b607e32011-07-18 16:54:40 +02001313 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001314 spec->cur_adc = spec->adc_nids[adc_idx];
1315 spec->cur_adc_stream_tag = stream_tag;
1316 spec->cur_adc_format = format;
1317 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001318 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001319 return 0;
1320}
1321
1322static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1323 struct hda_codec *codec,
1324 struct snd_pcm_substream *substream)
1325{
1326 struct via_spec *spec = codec->spec;
1327
Takashi Iwai3b607e32011-07-18 16:54:40 +02001328 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001329 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1330 spec->cur_adc = 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001331 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001332 return 0;
1333}
1334
1335/* re-setup the stream if running; called from input-src put */
1336static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1337{
1338 struct via_spec *spec = codec->spec;
1339 int adc_idx = spec->inputs[cur].adc_idx;
1340 hda_nid_t adc = spec->adc_nids[adc_idx];
Takashi Iwai3b607e32011-07-18 16:54:40 +02001341 bool ret = false;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001342
Takashi Iwai3b607e32011-07-18 16:54:40 +02001343 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001344 if (spec->cur_adc && spec->cur_adc != adc) {
1345 /* stream is running, let's swap the current ADC */
1346 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1347 spec->cur_adc = adc;
1348 snd_hda_codec_setup_stream(codec, adc,
1349 spec->cur_adc_stream_tag, 0,
1350 spec->cur_adc_format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001351 ret = true;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001352 }
Takashi Iwai3b607e32011-07-18 16:54:40 +02001353 mutex_unlock(&spec->config_mutex);
1354 return ret;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001355}
1356
Takashi Iwai9af74212011-06-18 16:17:45 +02001357static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001358 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001359 .channels_min = 2,
1360 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001361 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001362 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001363 .open = via_playback_multi_pcm_open,
1364 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001365 .prepare = via_playback_multi_pcm_prepare,
1366 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001367 },
1368};
1369
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001370static const struct hda_pcm_stream via_pcm_hp_playback = {
1371 .substreams = 1,
1372 .channels_min = 2,
1373 .channels_max = 2,
1374 /* NID is set in via_build_pcms */
1375 .ops = {
1376 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001377 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001378 .prepare = via_playback_hp_pcm_prepare,
1379 .cleanup = via_playback_hp_pcm_cleanup
1380 },
1381};
1382
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001383static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001384 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001385 .channels_min = 2,
1386 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001387 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001388 /* We got noisy outputs on the right channel on VT1708 when
1389 * 24bit samples are used. Until any workaround is found,
1390 * disable the 24bit format, so far.
1391 */
1392 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1393 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001394 .open = via_playback_multi_pcm_open,
1395 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001396 .prepare = via_playback_multi_pcm_prepare,
1397 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001398 },
1399};
1400
Takashi Iwai9af74212011-06-18 16:17:45 +02001401static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001402 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001403 .channels_min = 2,
1404 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001405 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001406 .ops = {
1407 .prepare = via_capture_pcm_prepare,
1408 .cleanup = via_capture_pcm_cleanup
1409 },
1410};
1411
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001412static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1413 .substreams = 1,
1414 .channels_min = 2,
1415 .channels_max = 2,
1416 /* NID is set in via_build_pcms */
1417 .ops = {
1418 .prepare = via_dyn_adc_capture_pcm_prepare,
1419 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1420 },
1421};
1422
Takashi Iwai9af74212011-06-18 16:17:45 +02001423static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001424 .substreams = 1,
1425 .channels_min = 2,
1426 .channels_max = 2,
1427 /* NID is set in via_build_pcms */
1428 .ops = {
1429 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001430 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001431 .prepare = via_dig_playback_pcm_prepare,
1432 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001433 },
1434};
1435
Takashi Iwai9af74212011-06-18 16:17:45 +02001436static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001437 .substreams = 1,
1438 .channels_min = 2,
1439 .channels_max = 2,
1440};
1441
Takashi Iwai370bafb2011-06-20 12:47:45 +02001442/*
1443 * slave controls for virtual master
1444 */
1445static const char * const via_slave_vols[] = {
1446 "Front Playback Volume",
1447 "Surround Playback Volume",
1448 "Center Playback Volume",
1449 "LFE Playback Volume",
1450 "Side Playback Volume",
1451 "Headphone Playback Volume",
1452 "Speaker Playback Volume",
1453 NULL,
1454};
1455
1456static const char * const via_slave_sws[] = {
1457 "Front Playback Switch",
1458 "Surround Playback Switch",
1459 "Center Playback Switch",
1460 "LFE Playback Switch",
1461 "Side Playback Switch",
1462 "Headphone Playback Switch",
1463 "Speaker Playback Switch",
1464 NULL,
1465};
1466
Joseph Chanc577b8a2006-11-29 15:29:40 +01001467static int via_build_controls(struct hda_codec *codec)
1468{
1469 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001470 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001471 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001472
Takashi Iwai24088a52011-06-17 16:59:21 +02001473 if (spec->set_widgets_power_state)
1474 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1475 return -ENOMEM;
1476
Joseph Chanc577b8a2006-11-29 15:29:40 +01001477 for (i = 0; i < spec->num_mixers; i++) {
1478 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1479 if (err < 0)
1480 return err;
1481 }
1482
1483 if (spec->multiout.dig_out_nid) {
1484 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001485 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001486 spec->multiout.dig_out_nid);
1487 if (err < 0)
1488 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001489 err = snd_hda_create_spdif_share_sw(codec,
1490 &spec->multiout);
1491 if (err < 0)
1492 return err;
1493 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001494 }
1495 if (spec->dig_in_nid) {
1496 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1497 if (err < 0)
1498 return err;
1499 }
Lydia Wang17314372009-10-10 19:07:37 +08001500
Takashi Iwai370bafb2011-06-20 12:47:45 +02001501 /* if we have no master control, let's create it */
1502 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1503 unsigned int vmaster_tlv[4];
1504 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1505 HDA_OUTPUT, vmaster_tlv);
1506 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1507 vmaster_tlv, via_slave_vols);
1508 if (err < 0)
1509 return err;
1510 }
1511 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1512 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1513 NULL, via_slave_sws);
1514 if (err < 0)
1515 return err;
1516 }
1517
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001518 /* assign Capture Source enums to NID */
1519 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1520 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001521 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001522 if (err < 0)
1523 return err;
1524 }
1525
Takashi Iwai603c4012008-07-30 15:01:44 +02001526 via_free_kctls(codec); /* no longer needed */
Takashi Iwai01a61e12011-10-28 00:03:22 +02001527
1528 err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
1529 if (err < 0)
1530 return err;
1531
Joseph Chanc577b8a2006-11-29 15:29:40 +01001532 return 0;
1533}
1534
1535static int via_build_pcms(struct hda_codec *codec)
1536{
1537 struct via_spec *spec = codec->spec;
1538 struct hda_pcm *info = spec->pcm_rec;
1539
Takashi Iwaia5973102011-09-28 16:43:36 +02001540 codec->num_pcms = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001541 codec->pcm_info = info;
1542
Takashi Iwaia5973102011-09-28 16:43:36 +02001543 if (spec->multiout.num_dacs || spec->num_adc_nids) {
1544 snprintf(spec->stream_name_analog,
1545 sizeof(spec->stream_name_analog),
1546 "%s Analog", codec->chip_name);
1547 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001548
Takashi Iwaia5973102011-09-28 16:43:36 +02001549 if (spec->multiout.num_dacs) {
1550 if (!spec->stream_analog_playback)
1551 spec->stream_analog_playback =
1552 &via_pcm_analog_playback;
1553 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1554 *spec->stream_analog_playback;
1555 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1556 spec->multiout.dac_nids[0];
1557 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1558 spec->multiout.max_channels;
1559 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001560
Takashi Iwaia5973102011-09-28 16:43:36 +02001561 if (!spec->stream_analog_capture) {
1562 if (spec->dyn_adc_switch)
1563 spec->stream_analog_capture =
1564 &via_pcm_dyn_adc_analog_capture;
1565 else
1566 spec->stream_analog_capture =
1567 &via_pcm_analog_capture;
1568 }
1569 if (spec->num_adc_nids) {
1570 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1571 *spec->stream_analog_capture;
1572 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1573 spec->adc_nids[0];
1574 if (!spec->dyn_adc_switch)
1575 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1576 spec->num_adc_nids;
1577 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001578 codec->num_pcms++;
1579 info++;
Takashi Iwaia5973102011-09-28 16:43:36 +02001580 }
1581
1582 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
Takashi Iwai82673bc2011-06-17 16:24:21 +02001583 snprintf(spec->stream_name_digital,
1584 sizeof(spec->stream_name_digital),
1585 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001586 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001587 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001588 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001589 if (!spec->stream_digital_playback)
1590 spec->stream_digital_playback =
1591 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001592 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001593 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001594 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1595 spec->multiout.dig_out_nid;
1596 }
1597 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001598 if (!spec->stream_digital_capture)
1599 spec->stream_digital_capture =
1600 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001601 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001602 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001603 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1604 spec->dig_in_nid;
1605 }
Takashi Iwaia5973102011-09-28 16:43:36 +02001606 codec->num_pcms++;
1607 info++;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001608 }
1609
Takashi Iwaiece8d042011-06-19 16:24:21 +02001610 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001611 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1612 "%s HP", codec->chip_name);
1613 info->name = spec->stream_name_hp;
1614 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1615 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001616 spec->hp_dac_nid;
Takashi Iwaia5973102011-09-28 16:43:36 +02001617 codec->num_pcms++;
1618 info++;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001619 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001620 return 0;
1621}
1622
1623static void via_free(struct hda_codec *codec)
1624{
1625 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001626
1627 if (!spec)
1628 return;
1629
Takashi Iwai603c4012008-07-30 15:01:44 +02001630 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001631 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001632 kfree(spec->bind_cap_vol);
1633 kfree(spec->bind_cap_sw);
1634 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001635}
1636
Takashi Iwai64be2852011-06-17 16:51:39 +02001637/* mute/unmute outputs */
1638static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1639 hda_nid_t *pins, bool mute)
1640{
1641 int i;
Takashi Iwai94994732011-07-11 11:36:44 +02001642 for (i = 0; i < num_pins; i++) {
1643 unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
1644 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1645 if (parm & AC_PINCTL_IN_EN)
1646 continue;
1647 if (mute)
1648 parm &= ~AC_PINCTL_OUT_EN;
1649 else
1650 parm |= AC_PINCTL_OUT_EN;
Takashi Iwai64be2852011-06-17 16:51:39 +02001651 snd_hda_codec_write(codec, pins[i], 0,
Takashi Iwai94994732011-07-11 11:36:44 +02001652 AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
1653 }
Takashi Iwai64be2852011-06-17 16:51:39 +02001654}
1655
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001656/* mute internal speaker if line-out is plugged */
1657static void via_line_automute(struct hda_codec *codec, int present)
1658{
1659 struct via_spec *spec = codec->spec;
1660
1661 if (!spec->autocfg.speaker_outs)
1662 return;
1663 if (!present)
1664 present = snd_hda_jack_detect(codec,
1665 spec->autocfg.line_out_pins[0]);
1666 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1667 spec->autocfg.speaker_pins,
1668 present);
1669}
1670
Harald Welte69e52a82008-09-09 15:57:32 +08001671/* mute internal speaker if HP is plugged */
1672static void via_hp_automute(struct hda_codec *codec)
1673{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001674 int present = 0;
Takashi Iwai6e969d92011-07-11 11:28:13 +02001675 int nums;
Harald Welte69e52a82008-09-09 15:57:32 +08001676 struct via_spec *spec = codec->spec;
1677
Takashi Iwai187d3332011-11-24 16:33:09 +01001678 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0] &&
1679 (spec->codec_type != VT1708 || spec->vt1708_jack_detect))
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001680 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai6e969d92011-07-11 11:28:13 +02001681
1682 if (spec->smart51_enabled)
1683 nums = spec->autocfg.line_outs + spec->smart51_nums;
1684 else
1685 nums = spec->autocfg.line_outs;
1686 toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
1687
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001688 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001689}
1690
Harald Welte69e52a82008-09-09 15:57:32 +08001691static void via_gpio_control(struct hda_codec *codec)
1692{
1693 unsigned int gpio_data;
1694 unsigned int vol_counter;
1695 unsigned int vol;
1696 unsigned int master_vol;
1697
1698 struct via_spec *spec = codec->spec;
1699
1700 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1701 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1702
1703 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1704 0xF84, 0) & 0x3F0000) >> 16;
1705
1706 vol = vol_counter & 0x1F;
1707 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1708 AC_VERB_GET_AMP_GAIN_MUTE,
1709 AC_AMP_GET_INPUT);
1710
1711 if (gpio_data == 0x02) {
1712 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001713 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1714 AC_VERB_SET_PIN_WIDGET_CONTROL,
1715 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001716 if (vol_counter & 0x20) {
1717 /* decrease volume */
1718 if (vol > master_vol)
1719 vol = master_vol;
1720 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1721 0, HDA_AMP_VOLMASK,
1722 master_vol-vol);
1723 } else {
1724 /* increase volume */
1725 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1726 HDA_AMP_VOLMASK,
1727 ((master_vol+vol) > 0x2A) ? 0x2A :
1728 (master_vol+vol));
1729 }
1730 } else if (!(gpio_data & 0x02)) {
1731 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001732 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1733 AC_VERB_SET_PIN_WIDGET_CONTROL,
1734 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001735 }
1736}
1737
1738/* unsolicited event for jack sensing */
1739static void via_unsol_event(struct hda_codec *codec,
1740 unsigned int res)
1741{
1742 res >>= 26;
Takashi Iwai3a938972011-10-28 01:16:55 +02001743 res = snd_hda_jack_get_action(codec, res);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001744
Lydia Wanga34df192009-10-10 19:08:01 +08001745 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001746 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001747
1748 res &= ~VIA_JACK_EVENT;
1749
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001750 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001751 via_hp_automute(codec);
1752 else if (res == VIA_GPIO_EVENT)
1753 via_gpio_control(codec);
Takashi Iwai01a61e12011-10-28 00:03:22 +02001754 snd_hda_jack_report_sync(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001755}
1756
Takashi Iwai2a439522011-07-26 09:52:50 +02001757#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001758static int via_suspend(struct hda_codec *codec, pm_message_t state)
1759{
1760 struct via_spec *spec = codec->spec;
1761 vt1708_stop_hp_work(spec);
1762 return 0;
1763}
1764#endif
1765
Takashi Iwaicb53c622007-08-10 17:21:45 +02001766#ifdef CONFIG_SND_HDA_POWER_SAVE
1767static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1768{
1769 struct via_spec *spec = codec->spec;
1770 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1771}
1772#endif
1773
Joseph Chanc577b8a2006-11-29 15:29:40 +01001774/*
1775 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001776
1777static int via_init(struct hda_codec *codec);
1778
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001779static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001780 .build_controls = via_build_controls,
1781 .build_pcms = via_build_pcms,
1782 .init = via_init,
1783 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001784 .unsol_event = via_unsol_event,
Takashi Iwai2a439522011-07-26 09:52:50 +02001785#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001786 .suspend = via_suspend,
1787#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001788#ifdef CONFIG_SND_HDA_POWER_SAVE
1789 .check_power_status = via_check_power_status,
1790#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001791};
1792
Takashi Iwai4a796162011-06-17 17:53:38 +02001793static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001794{
Takashi Iwai4a796162011-06-17 17:53:38 +02001795 struct via_spec *spec = codec->spec;
1796 int i;
1797
1798 for (i = 0; i < spec->multiout.num_dacs; i++) {
1799 if (spec->multiout.dac_nids[i] == dac)
1800 return false;
1801 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001802 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001803 return false;
1804 return true;
1805}
1806
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001807static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001808 hda_nid_t target_dac, int with_aa_mix,
1809 struct nid_path *path, int depth)
Takashi Iwai4a796162011-06-17 17:53:38 +02001810{
Takashi Iwai3214b962011-07-18 12:49:25 +02001811 struct via_spec *spec = codec->spec;
Takashi Iwai4a796162011-06-17 17:53:38 +02001812 hda_nid_t conn[8];
1813 int i, nums;
1814
Takashi Iwai3214b962011-07-18 12:49:25 +02001815 if (nid == spec->aa_mix_nid) {
1816 if (!with_aa_mix)
1817 return false;
1818 with_aa_mix = 2; /* mark aa-mix is included */
1819 }
1820
Takashi Iwai4a796162011-06-17 17:53:38 +02001821 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1822 for (i = 0; i < nums; i++) {
1823 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1824 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001825 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1826 /* aa-mix is requested but not included? */
1827 if (!(spec->aa_mix_nid && with_aa_mix == 1))
1828 goto found;
1829 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001830 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001831 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001832 return false;
1833 for (i = 0; i < nums; i++) {
1834 unsigned int type;
1835 type = get_wcaps_type(get_wcaps(codec, conn[i]));
Takashi Iwai3214b962011-07-18 12:49:25 +02001836 if (type == AC_WID_AUD_OUT)
Takashi Iwai4a796162011-06-17 17:53:38 +02001837 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001838 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai3214b962011-07-18 12:49:25 +02001839 with_aa_mix, path, depth + 1))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001840 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001841 }
1842 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001843
1844 found:
1845 path->path[path->depth] = conn[i];
1846 path->idx[path->depth] = i;
1847 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1848 path->multi[path->depth] = 1;
1849 path->depth++;
1850 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001851}
1852
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001853static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001854 hda_nid_t target_dac, int with_aa_mix,
1855 struct nid_path *path)
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001856{
Takashi Iwai3214b962011-07-18 12:49:25 +02001857 if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001858 path->path[path->depth] = nid;
1859 path->depth++;
Takashi Iwai3214b962011-07-18 12:49:25 +02001860 snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
1861 path->depth, path->path[0], path->path[1],
1862 path->path[2], path->path[3], path->path[4]);
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001863 return true;
1864 }
1865 return false;
1866}
1867
Takashi Iwai4a796162011-06-17 17:53:38 +02001868static int via_auto_fill_dac_nids(struct hda_codec *codec)
1869{
1870 struct via_spec *spec = codec->spec;
1871 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001872 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001873 hda_nid_t nid;
1874
Joseph Chanc577b8a2006-11-29 15:29:40 +01001875 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001876 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001877 for (i = 0; i < cfg->line_outs; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001878 hda_nid_t dac = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001879 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001880 if (!nid)
1881 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001882 if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
1883 dac = spec->out_path[i].path[0];
1884 if (!i && parse_output_path(codec, nid, dac, 1,
1885 &spec->out_mix_path))
1886 dac = spec->out_mix_path.path[0];
1887 if (dac) {
1888 spec->private_dac_nids[i] = dac;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001889 dac_num++;
1890 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001891 }
Takashi Iwai3214b962011-07-18 12:49:25 +02001892 if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
1893 spec->out_path[0] = spec->out_mix_path;
1894 spec->out_mix_path.depth = 0;
1895 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001896 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001897 return 0;
1898}
1899
Takashi Iwai4a796162011-06-17 17:53:38 +02001900static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001901 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001902{
Takashi Iwai4a796162011-06-17 17:53:38 +02001903 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001904 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001905 hda_nid_t dac, pin, sel, nid;
1906 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001907
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001908 dac = check_dac ? path->path[0] : 0;
1909 pin = path->path[path->depth - 1];
1910 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001911
Takashi Iwai8df2a312011-06-21 11:48:29 +02001912 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001913 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001914 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001915 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001916 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1917 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001918 else
1919 nid = 0;
1920 if (nid) {
1921 sprintf(name, "%s Playback Volume", pfx);
1922 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001923 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001924 if (err < 0)
1925 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001926 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001927 }
1928
Takashi Iwai8df2a312011-06-21 11:48:29 +02001929 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001930 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001931 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001932 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001933 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1934 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001935 else
1936 nid = 0;
1937 if (nid) {
1938 sprintf(name, "%s Playback Switch", pfx);
1939 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1940 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1941 if (err < 0)
1942 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001943 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001944 }
1945 return 0;
1946}
1947
Takashi Iwaif4a78282011-06-17 18:46:48 +02001948static void mangle_smart51(struct hda_codec *codec)
1949{
1950 struct via_spec *spec = codec->spec;
1951 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001952 struct auto_pin_cfg_item *ins = cfg->inputs;
1953 int i, j, nums, attr;
1954 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001955
Takashi Iwai0f98c242011-06-21 12:51:33 +02001956 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1957 nums = 0;
1958 for (i = 0; i < cfg->num_inputs; i++) {
1959 unsigned int def;
1960 if (ins[i].type > AUTO_PIN_LINE_IN)
1961 continue;
1962 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1963 if (snd_hda_get_input_pin_attr(def) != attr)
1964 continue;
1965 for (j = 0; j < nums; j++)
1966 if (ins[pins[j]].type < ins[i].type) {
1967 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001968 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001969 break;
1970 }
1971 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001972 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001973 }
1974 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001975 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001976 for (i = 0; i < nums; i++) {
1977 hda_nid_t pin = ins[pins[i]].pin;
1978 spec->smart51_pins[spec->smart51_nums++] = pin;
1979 cfg->line_out_pins[cfg->line_outs++] = pin;
1980 if (cfg->line_outs == 3)
1981 break;
1982 }
1983 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001984 }
1985}
1986
Takashi Iwai020066d2011-07-21 13:45:56 +02001987static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src)
1988{
1989 dst->vol_ctl = src->vol_ctl;
1990 dst->mute_ctl = src->mute_ctl;
1991}
1992
Takashi Iwai4a796162011-06-17 17:53:38 +02001993/* add playback controls from the parsed DAC table */
1994static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1995{
1996 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001997 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai3214b962011-07-18 12:49:25 +02001998 struct nid_path *path;
Takashi Iwaiea734962011-01-17 11:29:34 +01001999 static const char * const chname[4] = {
2000 "Front", "Surround", "C/LFE", "Side"
2001 };
Takashi Iwai4a796162011-06-17 17:53:38 +02002002 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02002003 int old_line_outs;
2004
2005 /* check smart51 */
2006 old_line_outs = cfg->line_outs;
2007 if (cfg->line_outs == 1)
2008 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002009
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002010 err = via_auto_fill_dac_nids(codec);
2011 if (err < 0)
2012 return err;
2013
Lydia Wang5c9a5612011-07-08 14:03:43 +08002014 if (spec->multiout.num_dacs < 3) {
2015 spec->smart51_nums = 0;
2016 cfg->line_outs = old_line_outs;
2017 }
Takashi Iwai4a796162011-06-17 17:53:38 +02002018 for (i = 0; i < cfg->line_outs; i++) {
2019 hda_nid_t pin, dac;
2020 pin = cfg->line_out_pins[i];
2021 dac = spec->multiout.dac_nids[i];
2022 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002023 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02002024 path = spec->out_path + i;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02002025 if (i == HDA_CLFE) {
Takashi Iwai3214b962011-07-18 12:49:25 +02002026 err = create_ch_ctls(codec, "Center", 1, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002027 if (err < 0)
2028 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002029 err = create_ch_ctls(codec, "LFE", 2, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002030 if (err < 0)
2031 return err;
2032 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02002033 const char *pfx = chname[i];
2034 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
2035 cfg->line_outs == 1)
2036 pfx = "Speaker";
Takashi Iwai3214b962011-07-18 12:49:25 +02002037 err = create_ch_ctls(codec, pfx, 3, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002038 if (err < 0)
2039 return err;
2040 }
Takashi Iwai020066d2011-07-21 13:45:56 +02002041 if (path != spec->out_path + i)
2042 copy_path_mixer_ctls(&spec->out_path[i], path);
2043 if (path == spec->out_path && spec->out_mix_path.depth)
2044 copy_path_mixer_ctls(&spec->out_mix_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002045 }
2046
Takashi Iwai4a796162011-06-17 17:53:38 +02002047 idx = get_connection_index(codec, spec->aa_mix_nid,
2048 spec->multiout.dac_nids[0]);
2049 if (idx >= 0) {
2050 /* add control to mixer */
Takashi Iwai3214b962011-07-18 12:49:25 +02002051 const char *name;
2052 name = spec->out_mix_path.depth ?
2053 "PCM Loopback Playback Volume" : "PCM Playback Volume";
2054 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002055 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2056 idx, HDA_INPUT));
2057 if (err < 0)
2058 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002059 name = spec->out_mix_path.depth ?
2060 "PCM Loopback Playback Switch" : "PCM Playback Switch";
2061 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002062 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2063 idx, HDA_INPUT));
2064 if (err < 0)
2065 return err;
2066 }
2067
Takashi Iwaif4a78282011-06-17 18:46:48 +02002068 cfg->line_outs = old_line_outs;
2069
Joseph Chanc577b8a2006-11-29 15:29:40 +01002070 return 0;
2071}
2072
Takashi Iwai4a796162011-06-17 17:53:38 +02002073static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002074{
Takashi Iwai4a796162011-06-17 17:53:38 +02002075 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002076 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002077 bool check_dac;
Takashi Iwai3214b962011-07-18 12:49:25 +02002078 int i, err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002079
2080 if (!pin)
2081 return 0;
2082
Takashi Iwai3214b962011-07-18 12:49:25 +02002083 if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
2084 for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
2085 if (i < spec->multiout.num_dacs &&
2086 parse_output_path(codec, pin,
2087 spec->multiout.dac_nids[i], 0,
2088 &spec->hp_indep_path)) {
2089 spec->hp_indep_shared = i;
2090 break;
2091 }
2092 }
Takashi Iwai25250502011-06-30 17:24:47 +02002093 }
Takashi Iwai3214b962011-07-18 12:49:25 +02002094 if (spec->hp_indep_path.depth) {
2095 spec->hp_dac_nid = spec->hp_indep_path.path[0];
2096 if (!spec->hp_indep_shared)
2097 spec->hp_path = spec->hp_indep_path;
2098 }
2099 /* optionally check front-path w/o AA-mix */
2100 if (!spec->hp_path.depth)
2101 parse_output_path(codec, pin,
2102 spec->multiout.dac_nids[HDA_FRONT], 0,
2103 &spec->hp_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002104
Takashi Iwaiece8d042011-06-19 16:24:21 +02002105 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai3214b962011-07-18 12:49:25 +02002106 1, &spec->hp_mix_path) && !spec->hp_path.depth)
Takashi Iwaiece8d042011-06-19 16:24:21 +02002107 return 0;
2108
Takashi Iwai3214b962011-07-18 12:49:25 +02002109 if (spec->hp_path.depth) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002110 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002111 check_dac = true;
2112 } else {
Takashi Iwai3214b962011-07-18 12:49:25 +02002113 path = &spec->hp_mix_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002114 check_dac = false;
2115 }
2116 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002117 if (err < 0)
2118 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002119 if (check_dac)
2120 copy_path_mixer_ctls(&spec->hp_mix_path, path);
2121 else
2122 copy_path_mixer_ctls(&spec->hp_path, path);
2123 if (spec->hp_indep_path.depth)
2124 copy_path_mixer_ctls(&spec->hp_indep_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002125 return 0;
2126}
2127
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002128static int via_auto_create_speaker_ctls(struct hda_codec *codec)
2129{
2130 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +02002131 struct nid_path *path;
2132 bool check_dac;
Wang Shaoyan81c0a782011-08-05 18:51:29 +08002133 hda_nid_t pin, dac = 0;
Takashi Iwai3214b962011-07-18 12:49:25 +02002134 int err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002135
2136 pin = spec->autocfg.speaker_pins[0];
2137 if (!spec->autocfg.speaker_outs || !pin)
2138 return 0;
2139
Takashi Iwai3214b962011-07-18 12:49:25 +02002140 if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02002141 dac = spec->speaker_path.path[0];
Takashi Iwai3214b962011-07-18 12:49:25 +02002142 if (!dac)
2143 parse_output_path(codec, pin,
2144 spec->multiout.dac_nids[HDA_FRONT], 0,
2145 &spec->speaker_path);
2146 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
2147 1, &spec->speaker_mix_path) && !dac)
2148 return 0;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002149
Takashi Iwai3214b962011-07-18 12:49:25 +02002150 /* no AA-path for front? */
2151 if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
2152 dac = 0;
2153
2154 spec->speaker_dac_nid = dac;
2155 spec->multiout.extra_out_nid[0] = dac;
2156 if (dac) {
2157 path = &spec->speaker_path;
2158 check_dac = true;
2159 } else {
2160 path = &spec->speaker_mix_path;
2161 check_dac = false;
2162 }
2163 err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
2164 if (err < 0)
2165 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002166 if (check_dac)
2167 copy_path_mixer_ctls(&spec->speaker_mix_path, path);
2168 else
2169 copy_path_mixer_ctls(&spec->speaker_path, path);
Takashi Iwai3214b962011-07-18 12:49:25 +02002170 return 0;
2171}
2172
2173#define via_aamix_ctl_info via_pin_power_ctl_info
2174
2175static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol,
2176 struct snd_ctl_elem_value *ucontrol)
2177{
2178 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2179 struct via_spec *spec = codec->spec;
2180 ucontrol->value.enumerated.item[0] = spec->aamix_mode;
2181 return 0;
2182}
2183
2184static void update_aamix_paths(struct hda_codec *codec, int do_mix,
2185 struct nid_path *nomix, struct nid_path *mix)
2186{
2187 if (do_mix) {
2188 activate_output_path(codec, nomix, false, false);
2189 activate_output_path(codec, mix, true, false);
2190 } else {
2191 activate_output_path(codec, mix, false, false);
2192 activate_output_path(codec, nomix, true, false);
2193 }
2194}
2195
2196static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol,
2197 struct snd_ctl_elem_value *ucontrol)
2198{
2199 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2200 struct via_spec *spec = codec->spec;
2201 unsigned int val = ucontrol->value.enumerated.item[0];
2202
2203 if (val == spec->aamix_mode)
2204 return 0;
2205 spec->aamix_mode = val;
2206 /* update front path */
2207 update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
2208 /* update HP path */
2209 if (!spec->hp_independent_mode) {
2210 update_aamix_paths(codec, val, &spec->hp_path,
2211 &spec->hp_mix_path);
2212 }
2213 /* update speaker path */
2214 update_aamix_paths(codec, val, &spec->speaker_path,
2215 &spec->speaker_mix_path);
2216 return 1;
2217}
2218
2219static const struct snd_kcontrol_new via_aamix_ctl_enum = {
2220 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2221 .name = "Loopback Mixing",
2222 .info = via_aamix_ctl_info,
2223 .get = via_aamix_ctl_get,
2224 .put = via_aamix_ctl_put,
2225};
2226
2227static int via_auto_create_loopback_switch(struct hda_codec *codec)
2228{
2229 struct via_spec *spec = codec->spec;
2230
Takashi Iwai4808d122012-01-10 15:16:02 +01002231 if (!spec->aa_mix_nid)
2232 return 0; /* no loopback switching available */
2233 if (!(spec->out_mix_path.depth || spec->hp_mix_path.depth ||
2234 spec->speaker_path.depth))
Takashi Iwai3214b962011-07-18 12:49:25 +02002235 return 0; /* no loopback switching available */
2236 if (!via_clone_control(spec, &via_aamix_ctl_enum))
2237 return -ENOMEM;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002238 return 0;
2239}
2240
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002241/* look for ADCs */
2242static int via_fill_adcs(struct hda_codec *codec)
2243{
2244 struct via_spec *spec = codec->spec;
2245 hda_nid_t nid = codec->start_nid;
2246 int i;
2247
2248 for (i = 0; i < codec->num_nodes; i++, nid++) {
2249 unsigned int wcaps = get_wcaps(codec, nid);
2250 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2251 continue;
2252 if (wcaps & AC_WCAP_DIGITAL)
2253 continue;
2254 if (!(wcaps & AC_WCAP_CONN_LIST))
2255 continue;
2256 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2257 return -ENOMEM;
2258 spec->adc_nids[spec->num_adc_nids++] = nid;
2259 }
2260 return 0;
2261}
2262
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002263/* input-src control */
2264static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2265 struct snd_ctl_elem_info *uinfo)
2266{
2267 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2268 struct via_spec *spec = codec->spec;
2269
2270 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2271 uinfo->count = 1;
2272 uinfo->value.enumerated.items = spec->num_inputs;
2273 if (uinfo->value.enumerated.item >= spec->num_inputs)
2274 uinfo->value.enumerated.item = spec->num_inputs - 1;
2275 strcpy(uinfo->value.enumerated.name,
2276 spec->inputs[uinfo->value.enumerated.item].label);
2277 return 0;
2278}
2279
2280static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2281 struct snd_ctl_elem_value *ucontrol)
2282{
2283 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2284 struct via_spec *spec = codec->spec;
2285 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2286
2287 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2288 return 0;
2289}
2290
2291static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2292 struct snd_ctl_elem_value *ucontrol)
2293{
2294 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2295 struct via_spec *spec = codec->spec;
2296 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2297 hda_nid_t mux;
2298 int cur;
2299
2300 cur = ucontrol->value.enumerated.item[0];
2301 if (cur < 0 || cur >= spec->num_inputs)
2302 return -EINVAL;
2303 if (spec->cur_mux[idx] == cur)
2304 return 0;
2305 spec->cur_mux[idx] = cur;
2306 if (spec->dyn_adc_switch) {
2307 int adc_idx = spec->inputs[cur].adc_idx;
2308 mux = spec->mux_nids[adc_idx];
2309 via_dyn_adc_pcm_resetup(codec, cur);
2310 } else {
2311 mux = spec->mux_nids[idx];
2312 if (snd_BUG_ON(!mux))
2313 return -EINVAL;
2314 }
2315
2316 if (mux) {
2317 /* switch to D0 beofre change index */
Takashi Iwai054d8672012-01-24 12:25:50 +01002318 update_power_state(codec, mux, AC_PWRST_D0);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002319 snd_hda_codec_write(codec, mux, 0,
2320 AC_VERB_SET_CONNECT_SEL,
2321 spec->inputs[cur].mux_idx);
2322 }
2323
2324 /* update jack power state */
2325 set_widgets_power_state(codec);
2326 return 0;
2327}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002328
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002329static const struct snd_kcontrol_new via_input_src_ctl = {
2330 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2331 /* The multiple "Capture Source" controls confuse alsamixer
2332 * So call somewhat different..
2333 */
2334 /* .name = "Capture Source", */
2335 .name = "Input Source",
2336 .info = via_mux_enum_info,
2337 .get = via_mux_enum_get,
2338 .put = via_mux_enum_put,
2339};
2340
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002341static int create_input_src_ctls(struct hda_codec *codec, int count)
2342{
2343 struct via_spec *spec = codec->spec;
2344 struct snd_kcontrol_new *knew;
2345
2346 if (spec->num_inputs <= 1 || !count)
2347 return 0; /* no need for single src */
2348
2349 knew = via_clone_control(spec, &via_input_src_ctl);
2350 if (!knew)
2351 return -ENOMEM;
2352 knew->count = count;
2353 return 0;
2354}
2355
2356/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002357static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2358{
2359 struct hda_amp_list *list;
2360
2361 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2362 return;
2363 list = spec->loopback_list + spec->num_loopbacks;
2364 list->nid = mix;
2365 list->dir = HDA_INPUT;
2366 list->idx = idx;
2367 spec->num_loopbacks++;
2368 spec->loopback.amplist = spec->loopback_list;
2369}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002370
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002371static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002372 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002373{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002374 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002375}
2376
2377/* add the input-route to the given pin */
2378static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002379{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002380 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002381 int c, idx;
2382
2383 spec->inputs[spec->num_inputs].adc_idx = -1;
2384 spec->inputs[spec->num_inputs].pin = pin;
2385 for (c = 0; c < spec->num_adc_nids; c++) {
2386 if (spec->mux_nids[c]) {
2387 idx = get_connection_index(codec, spec->mux_nids[c],
2388 pin);
2389 if (idx < 0)
2390 continue;
2391 spec->inputs[spec->num_inputs].mux_idx = idx;
2392 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002393 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002394 continue;
2395 }
2396 spec->inputs[spec->num_inputs].adc_idx = c;
2397 /* Can primary ADC satisfy all inputs? */
2398 if (!spec->dyn_adc_switch &&
2399 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2400 snd_printd(KERN_INFO
2401 "via: dynamic ADC switching enabled\n");
2402 spec->dyn_adc_switch = 1;
2403 }
2404 return true;
2405 }
2406 return false;
2407}
2408
2409static int get_mux_nids(struct hda_codec *codec);
2410
2411/* parse input-routes; fill ADCs, MUXs and input-src entries */
2412static int parse_analog_inputs(struct hda_codec *codec)
2413{
2414 struct via_spec *spec = codec->spec;
2415 const struct auto_pin_cfg *cfg = &spec->autocfg;
2416 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002417
2418 err = via_fill_adcs(codec);
2419 if (err < 0)
2420 return err;
2421 err = get_mux_nids(codec);
2422 if (err < 0)
2423 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002424
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002425 /* fill all input-routes */
2426 for (i = 0; i < cfg->num_inputs; i++) {
2427 if (add_input_route(codec, cfg->inputs[i].pin))
2428 spec->inputs[spec->num_inputs++].label =
2429 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002430 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002431
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002432 /* check for internal loopback recording */
2433 if (spec->aa_mix_nid &&
2434 add_input_route(codec, spec->aa_mix_nid))
2435 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2436
2437 return 0;
2438}
2439
2440/* create analog-loopback volume/switch controls */
2441static int create_loopback_ctls(struct hda_codec *codec)
2442{
2443 struct via_spec *spec = codec->spec;
2444 const struct auto_pin_cfg *cfg = &spec->autocfg;
2445 const char *prev_label = NULL;
2446 int type_idx = 0;
2447 int i, j, err, idx;
2448
2449 if (!spec->aa_mix_nid)
2450 return 0;
2451
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002452 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002453 hda_nid_t pin = cfg->inputs[i].pin;
2454 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2455
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002456 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002457 type_idx++;
2458 else
2459 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002460 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002461 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2462 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002463 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002464 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002465 if (err < 0)
2466 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002467 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002468 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002469
2470 /* remember the label for smart51 control */
2471 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002472 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002473 spec->smart51_idxs[j] = idx;
2474 spec->smart51_labels[j] = label;
2475 break;
2476 }
2477 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002478 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002479 return 0;
2480}
2481
2482/* create mic-boost controls (if present) */
2483static int create_mic_boost_ctls(struct hda_codec *codec)
2484{
2485 struct via_spec *spec = codec->spec;
2486 const struct auto_pin_cfg *cfg = &spec->autocfg;
2487 int i, err;
2488
2489 for (i = 0; i < cfg->num_inputs; i++) {
2490 hda_nid_t pin = cfg->inputs[i].pin;
2491 unsigned int caps;
2492 const char *label;
2493 char name[32];
2494
2495 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2496 continue;
2497 caps = query_amp_caps(codec, pin, HDA_INPUT);
2498 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2499 continue;
2500 label = hda_get_autocfg_input_label(codec, cfg, i);
2501 snprintf(name, sizeof(name), "%s Boost Volume", label);
2502 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2503 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2504 if (err < 0)
2505 return err;
2506 }
2507 return 0;
2508}
2509
2510/* create capture and input-src controls for multiple streams */
2511static int create_multi_adc_ctls(struct hda_codec *codec)
2512{
2513 struct via_spec *spec = codec->spec;
2514 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002515
2516 /* create capture mixer elements */
2517 for (i = 0; i < spec->num_adc_nids; i++) {
2518 hda_nid_t adc = spec->adc_nids[i];
2519 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2520 "Capture Volume", i,
2521 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2522 HDA_INPUT));
2523 if (err < 0)
2524 return err;
2525 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2526 "Capture Switch", i,
2527 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2528 HDA_INPUT));
2529 if (err < 0)
2530 return err;
2531 }
2532
2533 /* input-source control */
2534 for (i = 0; i < spec->num_adc_nids; i++)
2535 if (!spec->mux_nids[i])
2536 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002537 err = create_input_src_ctls(codec, i);
2538 if (err < 0)
2539 return err;
2540 return 0;
2541}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002542
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002543/* bind capture volume/switch */
2544static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2545 HDA_BIND_VOL("Capture Volume", 0);
2546static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2547 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002548
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002549static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2550 struct hda_ctl_ops *ops)
2551{
2552 struct hda_bind_ctls *ctl;
2553 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002554
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002555 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2556 if (!ctl)
2557 return -ENOMEM;
2558 ctl->ops = ops;
2559 for (i = 0; i < spec->num_adc_nids; i++)
2560 ctl->values[i] =
2561 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2562 *ctl_ret = ctl;
2563 return 0;
2564}
2565
2566/* create capture and input-src controls for dynamic ADC-switch case */
2567static int create_dyn_adc_ctls(struct hda_codec *codec)
2568{
2569 struct via_spec *spec = codec->spec;
2570 struct snd_kcontrol_new *knew;
2571 int err;
2572
2573 /* set up the bind capture ctls */
2574 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2575 if (err < 0)
2576 return err;
2577 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2578 if (err < 0)
2579 return err;
2580
2581 /* create capture mixer elements */
2582 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2583 if (!knew)
2584 return -ENOMEM;
2585 knew->private_value = (long)spec->bind_cap_vol;
2586
2587 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2588 if (!knew)
2589 return -ENOMEM;
2590 knew->private_value = (long)spec->bind_cap_sw;
2591
2592 /* input-source control */
2593 err = create_input_src_ctls(codec, 1);
2594 if (err < 0)
2595 return err;
2596 return 0;
2597}
2598
2599/* parse and create capture-related stuff */
2600static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2601{
2602 struct via_spec *spec = codec->spec;
2603 int err;
2604
2605 err = parse_analog_inputs(codec);
2606 if (err < 0)
2607 return err;
2608 if (spec->dyn_adc_switch)
2609 err = create_dyn_adc_ctls(codec);
2610 else
2611 err = create_multi_adc_ctls(codec);
2612 if (err < 0)
2613 return err;
2614 err = create_loopback_ctls(codec);
2615 if (err < 0)
2616 return err;
2617 err = create_mic_boost_ctls(codec);
2618 if (err < 0)
2619 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002620 return 0;
2621}
2622
Harald Welte76d9b0d2008-09-09 15:50:37 +08002623static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2624{
2625 unsigned int def_conf;
2626 unsigned char seqassoc;
2627
Takashi Iwai2f334f92009-02-20 14:37:42 +01002628 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002629 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2630 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002631 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2632 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2633 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2634 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002635 }
2636
2637 return;
2638}
2639
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002640static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002641 struct snd_ctl_elem_value *ucontrol)
2642{
2643 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2644 struct via_spec *spec = codec->spec;
2645
2646 if (spec->codec_type != VT1708)
2647 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002648 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002649 return 0;
2650}
2651
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002652static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002653 struct snd_ctl_elem_value *ucontrol)
2654{
2655 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2656 struct via_spec *spec = codec->spec;
Takashi Iwai187d3332011-11-24 16:33:09 +01002657 int val;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002658
2659 if (spec->codec_type != VT1708)
2660 return 0;
Takashi Iwai187d3332011-11-24 16:33:09 +01002661 val = !!ucontrol->value.integer.value[0];
2662 if (spec->vt1708_jack_detect == val)
2663 return 0;
2664 spec->vt1708_jack_detect = val;
2665 if (spec->vt1708_jack_detect &&
2666 snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") != 1) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002667 mute_aa_path(codec, 1);
2668 notify_aa_path_ctls(codec);
2669 }
Takashi Iwai187d3332011-11-24 16:33:09 +01002670 via_hp_automute(codec);
2671 vt1708_update_hp_work(spec);
2672 return 1;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002673}
2674
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002675static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2676 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2677 .name = "Jack Detect",
2678 .count = 1,
2679 .info = snd_ctl_boolean_mono_info,
2680 .get = vt1708_jack_detect_get,
2681 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002682};
2683
Takashi Iwai12daef62011-06-18 17:45:49 +02002684static void fill_dig_outs(struct hda_codec *codec);
2685static void fill_dig_in(struct hda_codec *codec);
2686
2687static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002688{
2689 struct via_spec *spec = codec->spec;
2690 int err;
2691
2692 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2693 if (err < 0)
2694 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002695 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002696 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002697
Takashi Iwai4a796162011-06-17 17:53:38 +02002698 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002699 if (err < 0)
2700 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002701 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002702 if (err < 0)
2703 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002704 err = via_auto_create_speaker_ctls(codec);
2705 if (err < 0)
2706 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002707 err = via_auto_create_loopback_switch(codec);
2708 if (err < 0)
2709 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002710 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002711 if (err < 0)
2712 return err;
2713
2714 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2715
Takashi Iwai12daef62011-06-18 17:45:49 +02002716 fill_dig_outs(codec);
2717 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002718
Takashi Iwai603c4012008-07-30 15:01:44 +02002719 if (spec->kctls.list)
2720 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002721
Joseph Chanc577b8a2006-11-29 15:29:40 +01002722
Takashi Iwai3214b962011-07-18 12:49:25 +02002723 if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002724 err = via_hp_build(codec);
2725 if (err < 0)
2726 return err;
2727 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002728
Takashi Iwaif4a78282011-06-17 18:46:48 +02002729 err = via_smart51_build(codec);
2730 if (err < 0)
2731 return err;
2732
Takashi Iwai5d417622011-06-20 11:32:27 +02002733 /* assign slave outs */
2734 if (spec->slave_dig_outs[0])
2735 codec->slave_dig_outs = spec->slave_dig_outs;
2736
Joseph Chanc577b8a2006-11-29 15:29:40 +01002737 return 1;
2738}
2739
Takashi Iwai5d417622011-06-20 11:32:27 +02002740static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002741{
Lydia Wang25eaba22009-10-10 19:08:43 +08002742 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002743 if (spec->multiout.dig_out_nid)
2744 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2745 if (spec->slave_dig_outs[0])
2746 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2747}
Lydia Wang25eaba22009-10-10 19:08:43 +08002748
Takashi Iwai5d417622011-06-20 11:32:27 +02002749static void via_auto_init_dig_in(struct hda_codec *codec)
2750{
2751 struct via_spec *spec = codec->spec;
2752 if (!spec->dig_in_nid)
2753 return;
2754 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2755 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2756}
2757
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002758/* initialize the unsolicited events */
2759static void via_auto_init_unsol_event(struct hda_codec *codec)
2760{
2761 struct via_spec *spec = codec->spec;
2762 struct auto_pin_cfg *cfg = &spec->autocfg;
2763 unsigned int ev;
2764 int i;
2765
2766 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002767 snd_hda_jack_detect_enable(codec, cfg->hp_pins[0],
2768 VIA_HP_EVENT | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002769
2770 if (cfg->speaker_pins[0])
2771 ev = VIA_LINE_EVENT;
2772 else
2773 ev = 0;
2774 for (i = 0; i < cfg->line_outs; i++) {
2775 if (cfg->line_out_pins[i] &&
2776 is_jack_detectable(codec, cfg->line_out_pins[i]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002777 snd_hda_jack_detect_enable(codec, cfg->line_out_pins[i],
2778 ev | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002779 }
2780
2781 for (i = 0; i < cfg->num_inputs; i++) {
2782 if (is_jack_detectable(codec, cfg->inputs[i].pin))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002783 snd_hda_jack_detect_enable(codec, cfg->inputs[i].pin,
2784 VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002785 }
2786}
2787
Takashi Iwai5d417622011-06-20 11:32:27 +02002788static int via_init(struct hda_codec *codec)
2789{
2790 struct via_spec *spec = codec->spec;
2791 int i;
2792
2793 for (i = 0; i < spec->num_iverbs; i++)
2794 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2795
Takashi Iwaie9d010c2012-02-01 10:33:23 +01002796 /* init power states */
2797 set_widgets_power_state(codec);
2798 __analog_low_current_mode(codec, true);
2799
Joseph Chanc577b8a2006-11-29 15:29:40 +01002800 via_auto_init_multi_out(codec);
2801 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002802 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002803 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002804 via_auto_init_dig_outs(codec);
2805 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002806
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002807 via_auto_init_unsol_event(codec);
2808
2809 via_hp_automute(codec);
Takashi Iwai187d3332011-11-24 16:33:09 +01002810 vt1708_update_hp_work(spec);
Takashi Iwai01a61e12011-10-28 00:03:22 +02002811 snd_hda_jack_report_sync(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002812
Joseph Chanc577b8a2006-11-29 15:29:40 +01002813 return 0;
2814}
2815
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002816static void vt1708_update_hp_jack_state(struct work_struct *work)
2817{
2818 struct via_spec *spec = container_of(work, struct via_spec,
2819 vt1708_hp_work.work);
2820 if (spec->codec_type != VT1708)
2821 return;
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002822 snd_hda_jack_set_dirty_all(spec->codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002823 /* if jack state toggled */
2824 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002825 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002826 spec->vt1708_hp_present ^= 1;
2827 via_hp_automute(spec->codec);
2828 }
Takashi Iwai187d3332011-11-24 16:33:09 +01002829 if (spec->vt1708_jack_detect)
2830 schedule_delayed_work(&spec->vt1708_hp_work,
2831 msecs_to_jiffies(100));
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002832}
2833
Takashi Iwai337b9d02009-07-07 18:18:59 +02002834static int get_mux_nids(struct hda_codec *codec)
2835{
2836 struct via_spec *spec = codec->spec;
2837 hda_nid_t nid, conn[8];
2838 unsigned int type;
2839 int i, n;
2840
2841 for (i = 0; i < spec->num_adc_nids; i++) {
2842 nid = spec->adc_nids[i];
2843 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002844 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002845 if (type == AC_WID_PIN)
2846 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002847 n = snd_hda_get_connections(codec, nid, conn,
2848 ARRAY_SIZE(conn));
2849 if (n <= 0)
2850 break;
2851 if (n > 1) {
2852 spec->mux_nids[i] = nid;
2853 break;
2854 }
2855 nid = conn[0];
2856 }
2857 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002858 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002859}
2860
Joseph Chanc577b8a2006-11-29 15:29:40 +01002861static int patch_vt1708(struct hda_codec *codec)
2862{
2863 struct via_spec *spec;
2864 int err;
2865
2866 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002867 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002868 if (spec == NULL)
2869 return -ENOMEM;
2870
Takashi Iwai620e2b22011-06-17 17:19:19 +02002871 spec->aa_mix_nid = 0x17;
2872
Takashi Iwai12daef62011-06-18 17:45:49 +02002873 /* Add HP and CD pin config connect bit re-config action */
2874 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2875 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2876
Joseph Chanc577b8a2006-11-29 15:29:40 +01002877 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002878 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002879 if (err < 0) {
2880 via_free(codec);
2881 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002882 }
2883
Takashi Iwai12daef62011-06-18 17:45:49 +02002884 /* add jack detect on/off control */
2885 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2886 return -ENOMEM;
2887
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002888 /* disable 32bit format on VT1708 */
2889 if (codec->vendor_id == 0x11061708)
2890 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002891
Lydia Wange322a362011-06-29 13:52:02 +08002892 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2893
Joseph Chanc577b8a2006-11-29 15:29:40 +01002894 codec->patch_ops = via_patch_ops;
2895
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002896 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002897 return 0;
2898}
2899
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002900static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002901{
2902 struct via_spec *spec;
2903 int err;
2904
2905 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002906 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002907 if (spec == NULL)
2908 return -ENOMEM;
2909
Takashi Iwai620e2b22011-06-17 17:19:19 +02002910 spec->aa_mix_nid = 0x18;
2911
Takashi Iwai12daef62011-06-18 17:45:49 +02002912 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002913 if (err < 0) {
2914 via_free(codec);
2915 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002916 }
2917
Joseph Chanc577b8a2006-11-29 15:29:40 +01002918 codec->patch_ops = via_patch_ops;
2919
Josepch Chanf7278fd2007-12-13 16:40:40 +01002920 return 0;
2921}
2922
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002923static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2924{
2925 struct via_spec *spec = codec->spec;
2926 int imux_is_smixer;
2927 unsigned int parm;
2928 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002929 if ((spec->codec_type != VT1708B_4CH) &&
2930 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002931 is_8ch = 1;
2932
2933 /* SW0 (17h) = stereo mixer */
2934 imux_is_smixer =
2935 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2936 == ((spec->codec_type == VT1708S) ? 5 : 0));
2937 /* inputs */
2938 /* PW 1/2/5 (1ah/1bh/1eh) */
2939 parm = AC_PWRST_D3;
2940 set_pin_power_state(codec, 0x1a, &parm);
2941 set_pin_power_state(codec, 0x1b, &parm);
2942 set_pin_power_state(codec, 0x1e, &parm);
2943 if (imux_is_smixer)
2944 parm = AC_PWRST_D0;
2945 /* SW0 (17h), AIW 0/1 (13h/14h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01002946 update_power_state(codec, 0x17, parm);
2947 update_power_state(codec, 0x13, parm);
2948 update_power_state(codec, 0x14, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002949
2950 /* outputs */
2951 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2952 parm = AC_PWRST_D3;
2953 set_pin_power_state(codec, 0x19, &parm);
2954 if (spec->smart51_enabled)
2955 set_pin_power_state(codec, 0x1b, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01002956 update_power_state(codec, 0x18, parm);
2957 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002958
2959 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2960 if (is_8ch) {
2961 parm = AC_PWRST_D3;
2962 set_pin_power_state(codec, 0x22, &parm);
2963 if (spec->smart51_enabled)
2964 set_pin_power_state(codec, 0x1a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01002965 update_power_state(codec, 0x26, parm);
2966 update_power_state(codec, 0x24, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002967 } else if (codec->vendor_id == 0x11064397) {
2968 /* PW7(23h), SW2(27h), AOW2(25h) */
2969 parm = AC_PWRST_D3;
2970 set_pin_power_state(codec, 0x23, &parm);
2971 if (spec->smart51_enabled)
2972 set_pin_power_state(codec, 0x1a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01002973 update_power_state(codec, 0x27, parm);
2974 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002975 }
2976
2977 /* PW 3/4/7 (1ch/1dh/23h) */
2978 parm = AC_PWRST_D3;
2979 /* force to D0 for internal Speaker */
2980 set_pin_power_state(codec, 0x1c, &parm);
2981 set_pin_power_state(codec, 0x1d, &parm);
2982 if (is_8ch)
2983 set_pin_power_state(codec, 0x23, &parm);
2984
2985 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01002986 update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
2987 update_power_state(codec, 0x10, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002988 if (is_8ch) {
Takashi Iwai054d8672012-01-24 12:25:50 +01002989 update_power_state(codec, 0x25, parm);
2990 update_power_state(codec, 0x27, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002991 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01002992 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002993}
2994
Lydia Wang518bf3b2009-10-10 19:07:29 +08002995static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002996static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002997{
2998 struct via_spec *spec;
2999 int err;
3000
Lydia Wang518bf3b2009-10-10 19:07:29 +08003001 if (get_codec_type(codec) == VT1708BCE)
3002 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003003
Josepch Chanf7278fd2007-12-13 16:40:40 +01003004 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003005 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003006 if (spec == NULL)
3007 return -ENOMEM;
3008
Takashi Iwai620e2b22011-06-17 17:19:19 +02003009 spec->aa_mix_nid = 0x16;
3010
Josepch Chanf7278fd2007-12-13 16:40:40 +01003011 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003012 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003013 if (err < 0) {
3014 via_free(codec);
3015 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003016 }
3017
Josepch Chanf7278fd2007-12-13 16:40:40 +01003018 codec->patch_ops = via_patch_ops;
3019
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003020 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
3021
Josepch Chanf7278fd2007-12-13 16:40:40 +01003022 return 0;
3023}
3024
Harald Welted949cac2008-09-09 15:56:01 +08003025/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02003026static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08003027 /* Enable Mic Boost Volume backdoor */
3028 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003029 /* don't bybass mixer */
3030 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08003031 { }
3032};
3033
Takashi Iwai9da29272009-05-07 16:31:14 +02003034/* fill out digital output widgets; one for master and one for slave outputs */
3035static void fill_dig_outs(struct hda_codec *codec)
3036{
3037 struct via_spec *spec = codec->spec;
3038 int i;
3039
3040 for (i = 0; i < spec->autocfg.dig_outs; i++) {
3041 hda_nid_t nid;
3042 int conn;
3043
3044 nid = spec->autocfg.dig_out_pins[i];
3045 if (!nid)
3046 continue;
3047 conn = snd_hda_get_connections(codec, nid, &nid, 1);
3048 if (conn < 1)
3049 continue;
3050 if (!spec->multiout.dig_out_nid)
3051 spec->multiout.dig_out_nid = nid;
3052 else {
3053 spec->slave_dig_outs[0] = nid;
3054 break; /* at most two dig outs */
3055 }
3056 }
3057}
3058
Takashi Iwai12daef62011-06-18 17:45:49 +02003059static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08003060{
3061 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02003062 hda_nid_t dig_nid;
3063 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08003064
Takashi Iwai12daef62011-06-18 17:45:49 +02003065 if (!spec->autocfg.dig_in_pin)
3066 return;
Harald Welted949cac2008-09-09 15:56:01 +08003067
Takashi Iwai12daef62011-06-18 17:45:49 +02003068 dig_nid = codec->start_nid;
3069 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
3070 unsigned int wcaps = get_wcaps(codec, dig_nid);
3071 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
3072 continue;
3073 if (!(wcaps & AC_WCAP_DIGITAL))
3074 continue;
3075 if (!(wcaps & AC_WCAP_CONN_LIST))
3076 continue;
3077 err = get_connection_index(codec, dig_nid,
3078 spec->autocfg.dig_in_pin);
3079 if (err >= 0) {
3080 spec->dig_in_nid = dig_nid;
3081 break;
3082 }
3083 }
Harald Welted949cac2008-09-09 15:56:01 +08003084}
3085
Lydia Wang6369bcf2009-10-10 19:08:31 +08003086static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
3087 int offset, int num_steps, int step_size)
3088{
3089 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
3090 (offset << AC_AMPCAP_OFFSET_SHIFT) |
3091 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
3092 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
3093 (0 << AC_AMPCAP_MUTE_SHIFT));
3094}
3095
Harald Welted949cac2008-09-09 15:56:01 +08003096static int patch_vt1708S(struct hda_codec *codec)
3097{
3098 struct via_spec *spec;
3099 int err;
3100
3101 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003102 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003103 if (spec == NULL)
3104 return -ENOMEM;
3105
Takashi Iwai620e2b22011-06-17 17:19:19 +02003106 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003107 override_mic_boost(codec, 0x1a, 0, 3, 40);
3108 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003109
Harald Welted949cac2008-09-09 15:56:01 +08003110 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003111 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003112 if (err < 0) {
3113 via_free(codec);
3114 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003115 }
3116
Takashi Iwai096a8852011-06-20 12:09:02 +02003117 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003118
Harald Welted949cac2008-09-09 15:56:01 +08003119 codec->patch_ops = via_patch_ops;
3120
Lydia Wang518bf3b2009-10-10 19:07:29 +08003121 /* correct names for VT1708BCE */
3122 if (get_codec_type(codec) == VT1708BCE) {
3123 kfree(codec->chip_name);
3124 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3125 snprintf(codec->bus->card->mixername,
3126 sizeof(codec->bus->card->mixername),
3127 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08003128 }
Lydia Wangbc92df72011-03-23 17:56:05 +08003129 /* correct names for VT1705 */
3130 if (codec->vendor_id == 0x11064397) {
3131 kfree(codec->chip_name);
3132 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3133 snprintf(codec->bus->card->mixername,
3134 sizeof(codec->bus->card->mixername),
3135 "%s %s", codec->vendor_name, codec->chip_name);
3136 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003137 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08003138 return 0;
3139}
3140
3141/* Patch for VT1702 */
3142
Takashi Iwai096a8852011-06-20 12:09:02 +02003143static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003144 /* mixer enable */
3145 {0x1, 0xF88, 0x3},
3146 /* GPIO 0~2 */
3147 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003148 { }
3149};
3150
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003151static void set_widgets_power_state_vt1702(struct hda_codec *codec)
3152{
3153 int imux_is_smixer =
3154 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3155 unsigned int parm;
3156 /* inputs */
3157 /* PW 1/2/5 (14h/15h/18h) */
3158 parm = AC_PWRST_D3;
3159 set_pin_power_state(codec, 0x14, &parm);
3160 set_pin_power_state(codec, 0x15, &parm);
3161 set_pin_power_state(codec, 0x18, &parm);
3162 if (imux_is_smixer)
3163 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
3164 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003165 update_power_state(codec, 0x13, parm);
3166 update_power_state(codec, 0x12, parm);
3167 update_power_state(codec, 0x1f, parm);
3168 update_power_state(codec, 0x20, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003169
3170 /* outputs */
3171 /* PW 3/4 (16h/17h) */
3172 parm = AC_PWRST_D3;
3173 set_pin_power_state(codec, 0x17, &parm);
3174 set_pin_power_state(codec, 0x16, &parm);
3175 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003176 update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm);
3177 update_power_state(codec, 0x10, parm);
3178 update_power_state(codec, 0x1d, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003179}
3180
Harald Welted949cac2008-09-09 15:56:01 +08003181static int patch_vt1702(struct hda_codec *codec)
3182{
3183 struct via_spec *spec;
3184 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003185
3186 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003187 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003188 if (spec == NULL)
3189 return -ENOMEM;
3190
Takashi Iwai620e2b22011-06-17 17:19:19 +02003191 spec->aa_mix_nid = 0x1a;
3192
Takashi Iwai12daef62011-06-18 17:45:49 +02003193 /* limit AA path volume to 0 dB */
3194 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3195 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3196 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3197 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3198 (1 << AC_AMPCAP_MUTE_SHIFT));
3199
Harald Welted949cac2008-09-09 15:56:01 +08003200 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003201 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003202 if (err < 0) {
3203 via_free(codec);
3204 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003205 }
3206
Takashi Iwai096a8852011-06-20 12:09:02 +02003207 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003208
Harald Welted949cac2008-09-09 15:56:01 +08003209 codec->patch_ops = via_patch_ops;
3210
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003211 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08003212 return 0;
3213}
3214
Lydia Wangeb7188c2009-10-10 19:08:34 +08003215/* Patch for VT1718S */
3216
Takashi Iwai096a8852011-06-20 12:09:02 +02003217static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08003218 /* Enable MW0 adjust Gain 5 */
3219 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003220 /* Enable Boost Volume backdoor */
3221 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02003222
Lydia Wangeb7188c2009-10-10 19:08:34 +08003223 { }
3224};
3225
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003226static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
3227{
3228 struct via_spec *spec = codec->spec;
3229 int imux_is_smixer;
3230 unsigned int parm;
3231 /* MUX6 (1eh) = stereo mixer */
3232 imux_is_smixer =
3233 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3234 /* inputs */
3235 /* PW 5/6/7 (29h/2ah/2bh) */
3236 parm = AC_PWRST_D3;
3237 set_pin_power_state(codec, 0x29, &parm);
3238 set_pin_power_state(codec, 0x2a, &parm);
3239 set_pin_power_state(codec, 0x2b, &parm);
3240 if (imux_is_smixer)
3241 parm = AC_PWRST_D0;
3242 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003243 update_power_state(codec, 0x1e, parm);
3244 update_power_state(codec, 0x1f, parm);
3245 update_power_state(codec, 0x10, parm);
3246 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003247
3248 /* outputs */
3249 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
3250 parm = AC_PWRST_D3;
3251 set_pin_power_state(codec, 0x27, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003252 update_power_state(codec, 0x1a, parm);
3253 update_power_state(codec, 0xb, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003254
3255 /* PW2 (26h), AOW2 (ah) */
3256 parm = AC_PWRST_D3;
3257 set_pin_power_state(codec, 0x26, &parm);
3258 if (spec->smart51_enabled)
3259 set_pin_power_state(codec, 0x2b, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003260 update_power_state(codec, 0xa, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003261
3262 /* PW0 (24h), AOW0 (8h) */
3263 parm = AC_PWRST_D3;
3264 set_pin_power_state(codec, 0x24, &parm);
3265 if (!spec->hp_independent_mode) /* check for redirected HP */
3266 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003267 update_power_state(codec, 0x8, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003268 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003269 update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003270
3271 /* PW1 (25h), AOW1 (9h) */
3272 parm = AC_PWRST_D3;
3273 set_pin_power_state(codec, 0x25, &parm);
3274 if (spec->smart51_enabled)
3275 set_pin_power_state(codec, 0x2a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003276 update_power_state(codec, 0x9, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003277
3278 if (spec->hp_independent_mode) {
3279 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3280 parm = AC_PWRST_D3;
3281 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003282 update_power_state(codec, 0x1b, parm);
3283 update_power_state(codec, 0x34, parm);
3284 update_power_state(codec, 0xc, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003285 }
3286}
3287
Takashi Iwai30b45032011-07-11 17:05:04 +02003288/* Add a connection to the primary DAC from AA-mixer for some codecs
3289 * This isn't listed from the raw info, but the chip has a secret connection.
3290 */
3291static int add_secret_dac_path(struct hda_codec *codec)
3292{
3293 struct via_spec *spec = codec->spec;
3294 int i, nums;
3295 hda_nid_t conn[8];
3296 hda_nid_t nid;
3297
3298 if (!spec->aa_mix_nid)
3299 return 0;
3300 nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
3301 ARRAY_SIZE(conn) - 1);
3302 for (i = 0; i < nums; i++) {
3303 if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
3304 return 0;
3305 }
3306
3307 /* find the primary DAC and add to the connection list */
3308 nid = codec->start_nid;
3309 for (i = 0; i < codec->num_nodes; i++, nid++) {
3310 unsigned int caps = get_wcaps(codec, nid);
3311 if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
3312 !(caps & AC_WCAP_DIGITAL)) {
3313 conn[nums++] = nid;
3314 return snd_hda_override_conn_list(codec,
3315 spec->aa_mix_nid,
3316 nums, conn);
3317 }
3318 }
3319 return 0;
3320}
3321
3322
Lydia Wangeb7188c2009-10-10 19:08:34 +08003323static int patch_vt1718S(struct hda_codec *codec)
3324{
3325 struct via_spec *spec;
3326 int err;
3327
3328 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003329 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003330 if (spec == NULL)
3331 return -ENOMEM;
3332
Takashi Iwai620e2b22011-06-17 17:19:19 +02003333 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003334 override_mic_boost(codec, 0x2b, 0, 3, 40);
3335 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003336 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003337
Lydia Wangeb7188c2009-10-10 19:08:34 +08003338 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003339 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003340 if (err < 0) {
3341 via_free(codec);
3342 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003343 }
3344
Takashi Iwai096a8852011-06-20 12:09:02 +02003345 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003346
Lydia Wangeb7188c2009-10-10 19:08:34 +08003347 codec->patch_ops = via_patch_ops;
3348
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003349 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3350
Lydia Wangeb7188c2009-10-10 19:08:34 +08003351 return 0;
3352}
Lydia Wangf3db4232009-10-10 19:08:41 +08003353
3354/* Patch for VT1716S */
3355
3356static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3357 struct snd_ctl_elem_info *uinfo)
3358{
3359 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3360 uinfo->count = 1;
3361 uinfo->value.integer.min = 0;
3362 uinfo->value.integer.max = 1;
3363 return 0;
3364}
3365
3366static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3367 struct snd_ctl_elem_value *ucontrol)
3368{
3369 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3370 int index = 0;
3371
3372 index = snd_hda_codec_read(codec, 0x26, 0,
3373 AC_VERB_GET_CONNECT_SEL, 0);
3374 if (index != -1)
3375 *ucontrol->value.integer.value = index;
3376
3377 return 0;
3378}
3379
3380static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3381 struct snd_ctl_elem_value *ucontrol)
3382{
3383 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3384 struct via_spec *spec = codec->spec;
3385 int index = *ucontrol->value.integer.value;
3386
3387 snd_hda_codec_write(codec, 0x26, 0,
3388 AC_VERB_SET_CONNECT_SEL, index);
3389 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003390 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003391 return 1;
3392}
3393
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003394static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003395 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3396 {
3397 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3398 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003399 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003400 .count = 1,
3401 .info = vt1716s_dmic_info,
3402 .get = vt1716s_dmic_get,
3403 .put = vt1716s_dmic_put,
3404 },
3405 {} /* end */
3406};
3407
3408
3409/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003410static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003411 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3412 { } /* end */
3413};
3414
Takashi Iwai096a8852011-06-20 12:09:02 +02003415static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003416 /* Enable Boost Volume backdoor */
3417 {0x1, 0xf8a, 0x80},
3418 /* don't bybass mixer */
3419 {0x1, 0xf88, 0xc0},
3420 /* Enable mono output */
3421 {0x1, 0xf90, 0x08},
3422 { }
3423};
3424
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003425static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3426{
3427 struct via_spec *spec = codec->spec;
3428 int imux_is_smixer;
3429 unsigned int parm;
3430 unsigned int mono_out, present;
3431 /* SW0 (17h) = stereo mixer */
3432 imux_is_smixer =
3433 (snd_hda_codec_read(codec, 0x17, 0,
3434 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3435 /* inputs */
3436 /* PW 1/2/5 (1ah/1bh/1eh) */
3437 parm = AC_PWRST_D3;
3438 set_pin_power_state(codec, 0x1a, &parm);
3439 set_pin_power_state(codec, 0x1b, &parm);
3440 set_pin_power_state(codec, 0x1e, &parm);
3441 if (imux_is_smixer)
3442 parm = AC_PWRST_D0;
3443 /* SW0 (17h), AIW0(13h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003444 update_power_state(codec, 0x17, parm);
3445 update_power_state(codec, 0x13, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003446
3447 parm = AC_PWRST_D3;
3448 set_pin_power_state(codec, 0x1e, &parm);
3449 /* PW11 (22h) */
3450 if (spec->dmic_enabled)
3451 set_pin_power_state(codec, 0x22, &parm);
3452 else
Takashi Iwai054d8672012-01-24 12:25:50 +01003453 update_power_state(codec, 0x22, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003454
3455 /* SW2(26h), AIW1(14h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003456 update_power_state(codec, 0x26, parm);
3457 update_power_state(codec, 0x14, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003458
3459 /* outputs */
3460 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3461 parm = AC_PWRST_D3;
3462 set_pin_power_state(codec, 0x19, &parm);
3463 /* Smart 5.1 PW2(1bh) */
3464 if (spec->smart51_enabled)
3465 set_pin_power_state(codec, 0x1b, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003466 update_power_state(codec, 0x18, parm);
3467 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003468
3469 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3470 parm = AC_PWRST_D3;
3471 set_pin_power_state(codec, 0x23, &parm);
3472 /* Smart 5.1 PW1(1ah) */
3473 if (spec->smart51_enabled)
3474 set_pin_power_state(codec, 0x1a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003475 update_power_state(codec, 0x27, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003476
3477 /* Smart 5.1 PW5(1eh) */
3478 if (spec->smart51_enabled)
3479 set_pin_power_state(codec, 0x1e, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003480 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003481
3482 /* Mono out */
3483 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3484 present = snd_hda_jack_detect(codec, 0x1c);
3485
3486 if (present)
3487 mono_out = 0;
3488 else {
3489 present = snd_hda_jack_detect(codec, 0x1d);
3490 if (!spec->hp_independent_mode && present)
3491 mono_out = 0;
3492 else
3493 mono_out = 1;
3494 }
3495 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
Takashi Iwai054d8672012-01-24 12:25:50 +01003496 update_power_state(codec, 0x28, parm);
3497 update_power_state(codec, 0x29, parm);
3498 update_power_state(codec, 0x2a, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003499
3500 /* PW 3/4 (1ch/1dh) */
3501 parm = AC_PWRST_D3;
3502 set_pin_power_state(codec, 0x1c, &parm);
3503 set_pin_power_state(codec, 0x1d, &parm);
3504 /* HP Independent Mode, power on AOW3 */
3505 if (spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01003506 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003507
3508 /* force to D0 for internal Speaker */
3509 /* MW0 (16h), AOW0 (10h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003510 update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
3511 update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003512}
3513
Lydia Wangf3db4232009-10-10 19:08:41 +08003514static int patch_vt1716S(struct hda_codec *codec)
3515{
3516 struct via_spec *spec;
3517 int err;
3518
3519 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003520 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003521 if (spec == NULL)
3522 return -ENOMEM;
3523
Takashi Iwai620e2b22011-06-17 17:19:19 +02003524 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003525 override_mic_boost(codec, 0x1a, 0, 3, 40);
3526 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003527
Lydia Wangf3db4232009-10-10 19:08:41 +08003528 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003529 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003530 if (err < 0) {
3531 via_free(codec);
3532 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003533 }
3534
Takashi Iwai096a8852011-06-20 12:09:02 +02003535 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003536
Lydia Wangf3db4232009-10-10 19:08:41 +08003537 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3538 spec->num_mixers++;
3539
3540 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3541
3542 codec->patch_ops = via_patch_ops;
3543
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003544 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003545 return 0;
3546}
Lydia Wang25eaba22009-10-10 19:08:43 +08003547
3548/* for vt2002P */
3549
Takashi Iwai096a8852011-06-20 12:09:02 +02003550static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003551 /* Class-D speaker related verbs */
3552 {0x1, 0xfe0, 0x4},
3553 {0x1, 0xfe9, 0x80},
3554 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003555 /* Enable Boost Volume backdoor */
3556 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003557 /* Enable AOW0 to MW9 */
3558 {0x1, 0xfb8, 0x88},
3559 { }
3560};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003561
Takashi Iwai096a8852011-06-20 12:09:02 +02003562static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003563 /* Enable Boost Volume backdoor */
3564 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003565 /* Enable AOW0 to MW9 */
3566 {0x1, 0xfb8, 0x88},
3567 { }
3568};
Lydia Wang25eaba22009-10-10 19:08:43 +08003569
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003570static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3571{
3572 struct via_spec *spec = codec->spec;
3573 int imux_is_smixer;
3574 unsigned int parm;
3575 unsigned int present;
3576 /* MUX9 (1eh) = stereo mixer */
3577 imux_is_smixer =
3578 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3579 /* inputs */
3580 /* PW 5/6/7 (29h/2ah/2bh) */
3581 parm = AC_PWRST_D3;
3582 set_pin_power_state(codec, 0x29, &parm);
3583 set_pin_power_state(codec, 0x2a, &parm);
3584 set_pin_power_state(codec, 0x2b, &parm);
3585 parm = AC_PWRST_D0;
3586 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003587 update_power_state(codec, 0x1e, parm);
3588 update_power_state(codec, 0x1f, parm);
3589 update_power_state(codec, 0x10, parm);
3590 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003591
3592 /* outputs */
3593 /* AOW0 (8h)*/
Takashi Iwai054d8672012-01-24 12:25:50 +01003594 update_power_state(codec, 0x8, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003595
Lydia Wang118909562011-03-23 17:57:34 +08003596 if (spec->codec_type == VT1802) {
3597 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3598 parm = AC_PWRST_D3;
3599 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003600 update_power_state(codec, 0x18, parm);
3601 update_power_state(codec, 0x38, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003602 } else {
3603 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3604 parm = AC_PWRST_D3;
3605 set_pin_power_state(codec, 0x26, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003606 update_power_state(codec, 0x1c, parm);
3607 update_power_state(codec, 0x37, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003608 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003609
Lydia Wang118909562011-03-23 17:57:34 +08003610 if (spec->codec_type == VT1802) {
3611 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3612 parm = AC_PWRST_D3;
3613 set_pin_power_state(codec, 0x25, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003614 update_power_state(codec, 0x15, parm);
3615 update_power_state(codec, 0x35, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003616 } else {
3617 /* PW1 (25h), MW1 (19h), 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, 0x19, parm);
3621 update_power_state(codec, 0x35, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003622 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003623
3624 if (spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01003625 update_power_state(codec, 0x9, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003626
3627 /* Class-D */
3628 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3629 present = snd_hda_jack_detect(codec, 0x25);
3630
3631 parm = AC_PWRST_D3;
3632 set_pin_power_state(codec, 0x24, &parm);
3633 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003634 if (spec->codec_type == VT1802)
Takashi Iwai054d8672012-01-24 12:25:50 +01003635 update_power_state(codec, 0x14, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003636 else
Takashi Iwai054d8672012-01-24 12:25:50 +01003637 update_power_state(codec, 0x18, parm);
3638 update_power_state(codec, 0x34, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003639
3640 /* Mono Out */
3641 present = snd_hda_jack_detect(codec, 0x26);
3642
3643 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003644 if (spec->codec_type == VT1802) {
3645 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003646 update_power_state(codec, 0x33, parm);
3647 update_power_state(codec, 0x1c, parm);
3648 update_power_state(codec, 0x3c, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003649 } else {
3650 /* PW15 (31h), MW8(17h), MUX8(3bh) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003651 update_power_state(codec, 0x31, parm);
3652 update_power_state(codec, 0x17, parm);
3653 update_power_state(codec, 0x3b, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003654 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003655 /* MW9 (21h) */
3656 if (imux_is_smixer || !is_aa_path_mute(codec))
Takashi Iwai054d8672012-01-24 12:25:50 +01003657 update_power_state(codec, 0x21, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003658 else
Takashi Iwai054d8672012-01-24 12:25:50 +01003659 update_power_state(codec, 0x21, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003660}
Lydia Wang25eaba22009-10-10 19:08:43 +08003661
3662/* patch for vt2002P */
3663static int patch_vt2002P(struct hda_codec *codec)
3664{
3665 struct via_spec *spec;
3666 int err;
3667
3668 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003669 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003670 if (spec == NULL)
3671 return -ENOMEM;
3672
Takashi Iwai620e2b22011-06-17 17:19:19 +02003673 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003674 override_mic_boost(codec, 0x2b, 0, 3, 40);
3675 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003676 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003677
Lydia Wang25eaba22009-10-10 19:08:43 +08003678 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003679 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003680 if (err < 0) {
3681 via_free(codec);
3682 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003683 }
3684
Lydia Wang118909562011-03-23 17:57:34 +08003685 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003686 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003687 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003688 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003689
Lydia Wang25eaba22009-10-10 19:08:43 +08003690 codec->patch_ops = via_patch_ops;
3691
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003692 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003693 return 0;
3694}
Lydia Wangab6734e2009-10-10 19:08:46 +08003695
3696/* for vt1812 */
3697
Takashi Iwai096a8852011-06-20 12:09:02 +02003698static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003699 /* Enable Boost Volume backdoor */
3700 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003701 /* Enable AOW0 to MW9 */
3702 {0x1, 0xfb8, 0xa8},
3703 { }
3704};
3705
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003706static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3707{
3708 struct via_spec *spec = codec->spec;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003709 unsigned int parm;
3710 unsigned int present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003711 /* inputs */
3712 /* PW 5/6/7 (29h/2ah/2bh) */
3713 parm = AC_PWRST_D3;
3714 set_pin_power_state(codec, 0x29, &parm);
3715 set_pin_power_state(codec, 0x2a, &parm);
3716 set_pin_power_state(codec, 0x2b, &parm);
3717 parm = AC_PWRST_D0;
3718 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003719 update_power_state(codec, 0x1e, parm);
3720 update_power_state(codec, 0x1f, parm);
3721 update_power_state(codec, 0x10, parm);
3722 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003723
3724 /* outputs */
3725 /* AOW0 (8h)*/
Takashi Iwai054d8672012-01-24 12:25:50 +01003726 update_power_state(codec, 0x8, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003727
3728 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3729 parm = AC_PWRST_D3;
3730 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003731 update_power_state(codec, 0x18, parm);
3732 update_power_state(codec, 0x38, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003733
3734 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3735 parm = AC_PWRST_D3;
3736 set_pin_power_state(codec, 0x25, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003737 update_power_state(codec, 0x15, parm);
3738 update_power_state(codec, 0x35, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003739 if (spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01003740 update_power_state(codec, 0x9, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003741
3742 /* Internal Speaker */
3743 /* PW0 (24h), MW0(14h), MUX0(34h) */
3744 present = snd_hda_jack_detect(codec, 0x25);
3745
3746 parm = AC_PWRST_D3;
3747 set_pin_power_state(codec, 0x24, &parm);
3748 if (present) {
Takashi Iwai054d8672012-01-24 12:25:50 +01003749 update_power_state(codec, 0x14, AC_PWRST_D3);
3750 update_power_state(codec, 0x34, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003751 } else {
Takashi Iwai054d8672012-01-24 12:25:50 +01003752 update_power_state(codec, 0x14, AC_PWRST_D0);
3753 update_power_state(codec, 0x34, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003754 }
3755
3756
3757 /* Mono Out */
3758 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3759 present = snd_hda_jack_detect(codec, 0x28);
3760
3761 parm = AC_PWRST_D3;
3762 set_pin_power_state(codec, 0x31, &parm);
3763 if (present) {
Takashi Iwai054d8672012-01-24 12:25:50 +01003764 update_power_state(codec, 0x1c, AC_PWRST_D3);
3765 update_power_state(codec, 0x3c, AC_PWRST_D3);
3766 update_power_state(codec, 0x3e, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003767 } else {
Takashi Iwai054d8672012-01-24 12:25:50 +01003768 update_power_state(codec, 0x1c, AC_PWRST_D0);
3769 update_power_state(codec, 0x3c, AC_PWRST_D0);
3770 update_power_state(codec, 0x3e, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003771 }
3772
3773 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3774 parm = AC_PWRST_D3;
3775 set_pin_power_state(codec, 0x33, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003776 update_power_state(codec, 0x1d, parm);
3777 update_power_state(codec, 0x3d, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003778
3779}
Lydia Wangab6734e2009-10-10 19:08:46 +08003780
3781/* patch for vt1812 */
3782static int patch_vt1812(struct hda_codec *codec)
3783{
3784 struct via_spec *spec;
3785 int err;
3786
3787 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003788 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003789 if (spec == NULL)
3790 return -ENOMEM;
3791
Takashi Iwai620e2b22011-06-17 17:19:19 +02003792 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003793 override_mic_boost(codec, 0x2b, 0, 3, 40);
3794 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003795 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003796
Lydia Wangab6734e2009-10-10 19:08:46 +08003797 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003798 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003799 if (err < 0) {
3800 via_free(codec);
3801 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003802 }
3803
Takashi Iwai096a8852011-06-20 12:09:02 +02003804 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003805
Lydia Wangab6734e2009-10-10 19:08:46 +08003806 codec->patch_ops = via_patch_ops;
3807
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003808 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003809 return 0;
3810}
3811
Joseph Chanc577b8a2006-11-29 15:29:40 +01003812/*
3813 * patch entries
3814 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003815static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003816 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3817 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3818 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3819 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3820 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003821 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003822 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003823 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003824 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003825 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003826 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003827 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003828 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003829 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003830 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003831 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003832 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003833 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003834 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003835 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003836 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003837 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003838 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003839 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003840 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003841 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003842 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003843 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003844 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003845 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003846 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003847 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003848 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003849 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003850 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003851 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003852 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003853 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003854 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003855 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003856 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003857 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003858 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003859 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003860 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003861 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003862 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003863 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003864 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003865 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003866 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003867 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003868 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003869 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003870 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003871 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003872 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003873 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003874 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003875 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003876 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003877 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003878 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003879 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003880 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003881 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003882 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003883 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003884 { .id = 0x11060428, .name = "VT1718S",
3885 .patch = patch_vt1718S},
3886 { .id = 0x11064428, .name = "VT1718S",
3887 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003888 { .id = 0x11060441, .name = "VT2020",
3889 .patch = patch_vt1718S},
3890 { .id = 0x11064441, .name = "VT1828S",
3891 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003892 { .id = 0x11060433, .name = "VT1716S",
3893 .patch = patch_vt1716S},
3894 { .id = 0x1106a721, .name = "VT1716S",
3895 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003896 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3897 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003898 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003899 { .id = 0x11060440, .name = "VT1818S",
3900 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003901 { .id = 0x11060446, .name = "VT1802",
3902 .patch = patch_vt2002P},
3903 { .id = 0x11068446, .name = "VT1802",
3904 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003905 {} /* terminator */
3906};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003907
3908MODULE_ALIAS("snd-hda-codec-id:1106*");
3909
3910static struct hda_codec_preset_list via_list = {
3911 .preset = snd_hda_preset_via,
3912 .owner = THIS_MODULE,
3913};
3914
3915MODULE_LICENSE("GPL");
3916MODULE_DESCRIPTION("VIA HD-audio codec");
3917
3918static int __init patch_via_init(void)
3919{
3920 return snd_hda_add_codec_preset(&via_list);
3921}
3922
3923static void __exit patch_via_exit(void)
3924{
3925 snd_hda_delete_codec_preset(&via_list);
3926}
3927
3928module_init(patch_via_init)
3929module_exit(patch_via_exit)