blob: f73c98638abd35883413b74f13e490c6d887bf0e [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Paul Gortmakerda155d52011-07-15 12:38:28 -040052#include <linux/module.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010053#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080054#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010055#include "hda_codec.h"
56#include "hda_local.h"
Takashi Iwai1835a0f2011-10-27 22:12:46 +020057#include "hda_jack.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010058
Joseph Chanc577b8a2006-11-29 15:29:40 +010059/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080060#define VT1708_HP_PIN_NID 0x20
61#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010062
Harald Welted7426322008-09-15 22:43:23 +080063enum VIA_HDA_CODEC {
64 UNKNOWN = -1,
65 VT1708,
66 VT1709_10CH,
67 VT1709_6CH,
68 VT1708B_8CH,
69 VT1708B_4CH,
70 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080071 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080072 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080073 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080074 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080075 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080076 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080077 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080078 CODEC_TYPES,
79};
80
Lydia Wang118909562011-03-23 17:57:34 +080081#define VT2002P_COMPATIBLE(spec) \
82 ((spec)->codec_type == VT2002P ||\
83 (spec)->codec_type == VT1812 ||\
84 (spec)->codec_type == VT1802)
85
Takashi Iwai8e3679d2011-06-21 09:01:36 +020086#define MAX_NID_PATH_DEPTH 5
87
Takashi Iwai09a9ad62011-06-21 15:57:44 +020088/* output-path: DAC -> ... -> pin
89 * idx[] contains the source index number of the next widget;
90 * e.g. idx[0] is the index of the DAC selected by path[1] widget
91 * multi[] indicates whether it's a selector widget with multi-connectors
92 * (i.e. the connection selection is mandatory)
93 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
94 */
Takashi Iwai4a796162011-06-17 17:53:38 +020095struct nid_path {
96 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020097 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad62011-06-21 15:57:44 +020098 unsigned char idx[MAX_NID_PATH_DEPTH];
99 unsigned char multi[MAX_NID_PATH_DEPTH];
100 unsigned int vol_ctl;
101 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200102};
103
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200104/* input-path */
105struct via_input {
106 hda_nid_t pin; /* input-pin or aa-mix */
107 int adc_idx; /* ADC index to be used */
108 int mux_idx; /* MUX index (if any) */
109 const char *label; /* input-source label */
110};
111
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200112#define VIA_MAX_ADCS 3
113
Takashi Iwai3b607e32011-07-18 16:54:40 +0200114enum {
115 STREAM_MULTI_OUT = (1 << 0),
116 STREAM_INDEP_HP = (1 << 1),
117};
118
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800119struct via_spec {
120 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200121 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800122 unsigned int num_mixers;
123
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200124 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800125 unsigned int num_iverbs;
126
Takashi Iwai82673bc2011-06-17 16:24:21 +0200127 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200128 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200129 const struct hda_pcm_stream *stream_analog_playback;
130 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800131
Takashi Iwai82673bc2011-06-17 16:24:21 +0200132 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200133 const struct hda_pcm_stream *stream_digital_playback;
134 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800135
136 /* playback */
137 struct hda_multi_out multiout;
138 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200139 hda_nid_t hp_dac_nid;
Takashi Iwai3214b962011-07-18 12:49:25 +0200140 hda_nid_t speaker_dac_nid;
141 int hp_indep_shared; /* indep HP-DAC is shared with side ch */
Takashi Iwai3b607e32011-07-18 16:54:40 +0200142 int opened_streams; /* STREAM_* bits */
143 int active_streams; /* STREAM_* bits */
Takashi Iwai3214b962011-07-18 12:49:25 +0200144 int aamix_mode; /* loopback is enabled for output-path? */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800145
Takashi Iwai3214b962011-07-18 12:49:25 +0200146 /* Output-paths:
147 * There are different output-paths depending on the setup.
148 * out_path, hp_path and speaker_path are primary paths. If both
149 * direct DAC and aa-loopback routes are available, these contain
150 * the former paths. Meanwhile *_mix_path contain the paths with
151 * loopback mixer. (Since the loopback is only for front channel,
152 * no out_mix_path for surround channels.)
153 * The HP output has another path, hp_indep_path, which is used in
154 * the independent-HP mode.
155 */
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200156 struct nid_path out_path[HDA_SIDE + 1];
Takashi Iwai3214b962011-07-18 12:49:25 +0200157 struct nid_path out_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200158 struct nid_path hp_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200159 struct nid_path hp_mix_path;
160 struct nid_path hp_indep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200161 struct nid_path speaker_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200162 struct nid_path speaker_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200163
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800164 /* capture */
165 unsigned int num_adc_nids;
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200166 hda_nid_t adc_nids[VIA_MAX_ADCS];
167 hda_nid_t mux_nids[VIA_MAX_ADCS];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200168 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800169 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800170
171 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200172 bool dyn_adc_switch;
173 int num_inputs;
174 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200175 unsigned int cur_mux[VIA_MAX_ADCS];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800176
Takashi Iwai3b607e32011-07-18 16:54:40 +0200177 /* dynamic DAC switching */
178 unsigned int cur_dac_stream_tag;
179 unsigned int cur_dac_format;
180 unsigned int cur_hp_stream_tag;
181 unsigned int cur_hp_format;
182
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200183 /* dynamic ADC switching */
184 hda_nid_t cur_adc;
185 unsigned int cur_adc_stream_tag;
186 unsigned int cur_adc_format;
187
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800188 /* PCM information */
189 struct hda_pcm pcm_rec[3];
190
191 /* dynamic controls, init_verbs and input_mux */
192 struct auto_pin_cfg autocfg;
193 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800194 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
195
196 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800197 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800198 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200199 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800200 enum VIA_HDA_CODEC codec_type;
201
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200202 /* smart51 setup */
203 unsigned int smart51_nums;
204 hda_nid_t smart51_pins[2];
205 int smart51_idxs[2];
206 const char *smart51_labels[2];
207 unsigned int smart51_enabled;
208
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800209 /* work to check hp jack state */
210 struct hda_codec *codec;
211 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200212 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800213 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800214
215 void (*set_widgets_power_state)(struct hda_codec *codec);
216
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800217 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200218 int num_loopbacks;
219 struct hda_amp_list loopback_list[8];
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200220
221 /* bind capture-volume */
222 struct hda_bind_ctls *bind_cap_vol;
223 struct hda_bind_ctls *bind_cap_sw;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200224
225 struct mutex config_mutex;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800226};
227
Lydia Wang0341ccd2011-03-22 16:25:03 +0800228static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100229static struct via_spec * via_new_spec(struct hda_codec *codec)
230{
231 struct via_spec *spec;
232
233 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
234 if (spec == NULL)
235 return NULL;
236
Takashi Iwai3b607e32011-07-18 16:54:40 +0200237 mutex_init(&spec->config_mutex);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100238 codec->spec = spec;
239 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800240 spec->codec_type = get_codec_type(codec);
241 /* VT1708BCE & VT1708S are almost same */
242 if (spec->codec_type == VT1708BCE)
243 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100244 return spec;
245}
246
Lydia Wang744ff5f2009-10-10 19:07:26 +0800247static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800248{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800249 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800250 u16 ven_id = vendor_id >> 16;
251 u16 dev_id = vendor_id & 0xffff;
252 enum VIA_HDA_CODEC codec_type;
253
254 /* get codec type */
255 if (ven_id != 0x1106)
256 codec_type = UNKNOWN;
257 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
258 codec_type = VT1708;
259 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
260 codec_type = VT1709_10CH;
261 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
262 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800263 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800264 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800265 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
266 codec_type = VT1708BCE;
267 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800268 codec_type = VT1708B_4CH;
269 else if ((dev_id & 0xfff) == 0x397
270 && (dev_id >> 12) < 8)
271 codec_type = VT1708S;
272 else if ((dev_id & 0xfff) == 0x398
273 && (dev_id >> 12) < 8)
274 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800275 else if ((dev_id & 0xfff) == 0x428
276 && (dev_id >> 12) < 8)
277 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800278 else if (dev_id == 0x0433 || dev_id == 0xa721)
279 codec_type = VT1716S;
Lydia Wangbb3c6bf2009-10-10 19:08:39 +0800280 else if (dev_id == 0x0441 || dev_id == 0x4441)
281 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800282 else if (dev_id == 0x0438 || dev_id == 0x4438)
283 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800284 else if (dev_id == 0x0448)
285 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800286 else if (dev_id == 0x0440)
287 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800288 else if ((dev_id & 0xfff) == 0x446)
289 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800290 else
291 codec_type = UNKNOWN;
292 return codec_type;
293};
294
Lydia Wangec7e7e42011-03-24 12:43:44 +0800295#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800296#define VIA_HP_EVENT 0x01
297#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200298#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800299
Joseph Chanc577b8a2006-11-29 15:29:40 +0100300enum {
301 VIA_CTL_WIDGET_VOL,
302 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800303 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100304};
305
Takashi Iwaiada509e2011-06-20 15:40:19 +0200306static void analog_low_current_mode(struct hda_codec *codec);
307static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800308
309static void vt1708_start_hp_work(struct via_spec *spec)
310{
311 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
312 return;
313 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200314 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800315 if (!delayed_work_pending(&spec->vt1708_hp_work))
316 schedule_delayed_work(&spec->vt1708_hp_work,
317 msecs_to_jiffies(100));
318}
319
320static void vt1708_stop_hp_work(struct via_spec *spec)
321{
322 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
323 return;
324 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
325 && !is_aa_path_mute(spec->codec))
326 return;
327 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200328 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100329 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800330}
Lydia Wangf5271102009-10-10 19:07:35 +0800331
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800332static void set_widgets_power_state(struct hda_codec *codec)
333{
334 struct via_spec *spec = codec->spec;
335 if (spec->set_widgets_power_state)
336 spec->set_widgets_power_state(codec);
337}
Lydia Wang25eaba22009-10-10 19:08:43 +0800338
Lydia Wangf5271102009-10-10 19:07:35 +0800339static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
340 struct snd_ctl_elem_value *ucontrol)
341{
342 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
343 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
344
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800345 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200346 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800347 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
348 if (is_aa_path_mute(codec))
349 vt1708_start_hp_work(codec->spec);
350 else
351 vt1708_stop_hp_work(codec->spec);
352 }
Lydia Wangf5271102009-10-10 19:07:35 +0800353 return change;
354}
355
356/* modify .put = snd_hda_mixer_amp_switch_put */
357#define ANALOG_INPUT_MUTE \
358 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
359 .name = NULL, \
360 .index = 0, \
361 .info = snd_hda_mixer_amp_switch_info, \
362 .get = snd_hda_mixer_amp_switch_get, \
363 .put = analog_input_switch_put, \
364 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
365
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200366static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100367 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
368 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800369 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100370};
371
Lydia Wangab6734e2009-10-10 19:08:46 +0800372
Joseph Chanc577b8a2006-11-29 15:29:40 +0100373/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200374static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
375 const struct snd_kcontrol_new *tmpl,
376 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100377{
378 struct snd_kcontrol_new *knew;
379
Takashi Iwai603c4012008-07-30 15:01:44 +0200380 snd_array_init(&spec->kctls, sizeof(*knew), 32);
381 knew = snd_array_new(&spec->kctls);
382 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200383 return NULL;
384 *knew = *tmpl;
385 if (!name)
386 name = tmpl->name;
387 if (name) {
388 knew->name = kstrdup(name, GFP_KERNEL);
389 if (!knew->name)
390 return NULL;
391 }
392 return knew;
393}
394
395static int __via_add_control(struct via_spec *spec, int type, const char *name,
396 int idx, unsigned long val)
397{
398 struct snd_kcontrol_new *knew;
399
400 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
401 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100402 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200403 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100404 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100405 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100406 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100407 return 0;
408}
409
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200410#define via_add_control(spec, type, name, val) \
411 __via_add_control(spec, type, name, 0, val)
412
Takashi Iwai291c9e32011-06-17 16:15:26 +0200413#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100414
Takashi Iwai603c4012008-07-30 15:01:44 +0200415static void via_free_kctls(struct hda_codec *codec)
416{
417 struct via_spec *spec = codec->spec;
418
419 if (spec->kctls.list) {
420 struct snd_kcontrol_new *kctl = spec->kctls.list;
421 int i;
422 for (i = 0; i < spec->kctls.used; i++)
423 kfree(kctl[i].name);
424 }
425 snd_array_free(&spec->kctls);
426}
427
Joseph Chanc577b8a2006-11-29 15:29:40 +0100428/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800429static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200430 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100431{
432 char name[32];
433 int err;
434
435 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200436 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100437 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
438 if (err < 0)
439 return err;
440 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200441 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100442 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
443 if (err < 0)
444 return err;
445 return 0;
446}
447
Takashi Iwai5d417622011-06-20 11:32:27 +0200448#define get_connection_index(codec, mux, nid) \
Takashi Iwai8d087c72011-06-28 12:45:47 +0200449 snd_hda_get_conn_index(codec, mux, nid, 0)
Takashi Iwai5d417622011-06-20 11:32:27 +0200450
Takashi Iwai8df2a312011-06-21 11:48:29 +0200451static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
452 unsigned int mask)
453{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200454 unsigned int caps;
455 if (!nid)
456 return false;
457 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200458 if (dir == HDA_INPUT)
459 caps &= AC_WCAP_IN_AMP;
460 else
461 caps &= AC_WCAP_OUT_AMP;
462 if (!caps)
463 return false;
464 if (query_amp_caps(codec, nid, dir) & mask)
465 return true;
466 return false;
467}
468
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200469#define have_mute(codec, nid, dir) \
470 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200471
Lydia Wangd69607b2011-07-08 14:02:52 +0800472/* enable/disable the output-route mixers */
473static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
Takashi Iwai3214b962011-07-18 12:49:25 +0200474 hda_nid_t mix_nid, int idx, bool enable)
Lydia Wangd69607b2011-07-08 14:02:52 +0800475{
476 int i, num, val;
Lydia Wangd69607b2011-07-08 14:02:52 +0800477
478 if (!path)
479 return;
480 num = snd_hda_get_conn_list(codec, mix_nid, NULL);
Lydia Wangd69607b2011-07-08 14:02:52 +0800481 for (i = 0; i < num; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +0200482 if (i == idx)
483 val = AMP_IN_UNMUTE(i);
484 else
485 val = AMP_IN_MUTE(i);
Lydia Wangd69607b2011-07-08 14:02:52 +0800486 snd_hda_codec_write(codec, mix_nid, 0,
487 AC_VERB_SET_AMP_GAIN_MUTE, val);
488 }
489}
490
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200491/* enable/disable the output-route */
492static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
493 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200494{
Lydia Wangd69607b2011-07-08 14:02:52 +0800495 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200496 int i;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200497 for (i = 0; i < path->depth; i++) {
498 hda_nid_t src, dst;
499 int idx = path->idx[i];
500 src = path->path[i];
501 if (i < path->depth - 1)
502 dst = path->path[i + 1];
503 else
504 dst = 0;
505 if (enable && path->multi[i])
506 snd_hda_codec_write(codec, dst, 0,
507 AC_VERB_SET_CONNECT_SEL, idx);
Takashi Iwai3214b962011-07-18 12:49:25 +0200508 if (!force && (dst == spec->aa_mix_nid))
Lydia Wange5e14682011-07-01 10:55:07 +0800509 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +0200510 if (have_mute(codec, dst, HDA_INPUT))
511 activate_output_mix(codec, path, dst, idx, enable);
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200512 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
513 continue;
514 if (have_mute(codec, src, HDA_OUTPUT)) {
515 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
516 snd_hda_codec_write(codec, src, 0,
517 AC_VERB_SET_AMP_GAIN_MUTE, val);
518 }
519 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200520}
521
522/* set the given pin as output */
523static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
524 int pin_type)
525{
526 if (!pin)
527 return;
528 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
529 pin_type);
530 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
531 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200532 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100533}
534
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200535static void via_auto_init_output(struct hda_codec *codec,
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200536 struct nid_path *path, int pin_type)
Takashi Iwai5d417622011-06-20 11:32:27 +0200537{
Takashi Iwai5d417622011-06-20 11:32:27 +0200538 unsigned int caps;
Lydia Wangd69607b2011-07-08 14:02:52 +0800539 hda_nid_t pin;
Takashi Iwai5d417622011-06-20 11:32:27 +0200540
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200541 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200542 return;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200543 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200544
545 init_output_pin(codec, pin, pin_type);
546 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
547 if (caps & AC_AMPCAP_MUTE) {
548 unsigned int val;
549 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
550 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
551 AMP_OUT_MUTE | val);
552 }
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200553 activate_output_path(codec, path, true, true); /* force on */
Takashi Iwai5d417622011-06-20 11:32:27 +0200554}
555
Joseph Chanc577b8a2006-11-29 15:29:40 +0100556static void via_auto_init_multi_out(struct hda_codec *codec)
557{
558 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200559 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100560 int i;
561
Takashi Iwai3214b962011-07-18 12:49:25 +0200562 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) {
563 path = &spec->out_path[i];
564 if (!i && spec->aamix_mode && spec->out_mix_path.depth)
565 path = &spec->out_mix_path;
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200566 via_auto_init_output(codec, path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200567 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100568}
569
Takashi Iwai020066d2011-07-21 13:45:56 +0200570/* deactivate the inactive headphone-paths */
571static void deactivate_hp_paths(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100572{
573 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200574 int shared = spec->hp_indep_shared;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100575
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200576 if (spec->hp_independent_mode) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200577 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200578 activate_output_path(codec, &spec->hp_mix_path, false, false);
579 if (shared)
580 activate_output_path(codec, &spec->out_path[shared],
581 false, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200582 } else if (spec->aamix_mode || !spec->hp_path.depth) {
583 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200584 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200585 } else {
Takashi Iwai020066d2011-07-21 13:45:56 +0200586 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200587 activate_output_path(codec, &spec->hp_mix_path, false, false);
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200588 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100589}
590
Takashi Iwai020066d2011-07-21 13:45:56 +0200591static void via_auto_init_hp_out(struct hda_codec *codec)
592{
593 struct via_spec *spec = codec->spec;
594
595 if (!spec->hp_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200596 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200597 return;
598 }
599 deactivate_hp_paths(codec);
600 if (spec->hp_independent_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200601 via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200602 else if (spec->aamix_mode)
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 else
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200605 via_auto_init_output(codec, &spec->hp_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200606}
607
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200608static void via_auto_init_speaker_out(struct hda_codec *codec)
609{
610 struct via_spec *spec = codec->spec;
611
Takashi Iwai3214b962011-07-18 12:49:25 +0200612 if (!spec->autocfg.speaker_outs)
613 return;
614 if (!spec->speaker_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200615 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200616 return;
617 }
618 if (!spec->aamix_mode) {
619 activate_output_path(codec, &spec->speaker_mix_path,
620 false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200621 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200622 } else {
623 activate_output_path(codec, &spec->speaker_path, false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200624 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200625 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200626}
627
Takashi Iwaif4a78282011-06-17 18:46:48 +0200628static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200629static void via_hp_automute(struct hda_codec *codec);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200630
Joseph Chanc577b8a2006-11-29 15:29:40 +0100631static void via_auto_init_analog_input(struct hda_codec *codec)
632{
633 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200634 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200635 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200636 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200637 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100638
Takashi Iwai096a8852011-06-20 12:09:02 +0200639 /* init ADCs */
640 for (i = 0; i < spec->num_adc_nids; i++) {
641 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
642 AC_VERB_SET_AMP_GAIN_MUTE,
643 AMP_IN_UNMUTE(0));
644 }
645
646 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200647 for (i = 0; i < cfg->num_inputs; i++) {
648 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200649 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200650 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100651 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200652 ctl = PIN_VREF50;
653 else
654 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100655 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200656 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100657 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200658
659 /* init input-src */
660 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200661 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
662 if (spec->mux_nids[adc_idx]) {
663 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
664 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
665 AC_VERB_SET_CONNECT_SEL,
666 mux_idx);
667 }
668 if (spec->dyn_adc_switch)
669 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200670 }
671
672 /* init aa-mixer */
673 if (!spec->aa_mix_nid)
674 return;
675 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
676 ARRAY_SIZE(conn));
677 for (i = 0; i < num_conns; i++) {
678 unsigned int caps = get_wcaps(codec, conn[i]);
679 if (get_wcaps_type(caps) == AC_WID_PIN)
680 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
681 AC_VERB_SET_AMP_GAIN_MUTE,
682 AMP_IN_MUTE(i));
683 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100684}
Lydia Wangf5271102009-10-10 19:07:35 +0800685
686static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
687 unsigned int *affected_parm)
688{
689 unsigned parm;
690 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
691 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
692 >> AC_DEFCFG_MISC_SHIFT
693 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800694 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200695 unsigned present = 0;
696
697 no_presence |= spec->no_pin_power_ctl;
698 if (!no_presence)
699 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200700 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800701 || ((no_presence || present)
702 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800703 *affected_parm = AC_PWRST_D0; /* if it's connected */
704 parm = AC_PWRST_D0;
705 } else
706 parm = AC_PWRST_D3;
707
708 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
709}
710
Takashi Iwai24088a52011-06-17 16:59:21 +0200711static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
712 struct snd_ctl_elem_info *uinfo)
713{
714 static const char * const texts[] = {
715 "Disabled", "Enabled"
716 };
717
718 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
719 uinfo->count = 1;
720 uinfo->value.enumerated.items = 2;
721 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
722 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
723 strcpy(uinfo->value.enumerated.name,
724 texts[uinfo->value.enumerated.item]);
725 return 0;
726}
727
728static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
729 struct snd_ctl_elem_value *ucontrol)
730{
731 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
732 struct via_spec *spec = codec->spec;
733 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
734 return 0;
735}
736
737static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
738 struct snd_ctl_elem_value *ucontrol)
739{
740 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
741 struct via_spec *spec = codec->spec;
742 unsigned int val = !ucontrol->value.enumerated.item[0];
743
744 if (val == spec->no_pin_power_ctl)
745 return 0;
746 spec->no_pin_power_ctl = val;
747 set_widgets_power_state(codec);
748 return 1;
749}
750
751static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
752 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
753 .name = "Dynamic Power-Control",
754 .info = via_pin_power_ctl_info,
755 .get = via_pin_power_ctl_get,
756 .put = via_pin_power_ctl_put,
757};
758
759
Harald Welte0aa62ae2008-09-09 15:58:27 +0800760static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
761 struct snd_ctl_elem_info *uinfo)
762{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200763 static const char * const texts[] = { "OFF", "ON" };
764
765 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
766 uinfo->count = 1;
767 uinfo->value.enumerated.items = 2;
768 if (uinfo->value.enumerated.item >= 2)
769 uinfo->value.enumerated.item = 1;
770 strcpy(uinfo->value.enumerated.name,
771 texts[uinfo->value.enumerated.item]);
772 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800773}
774
775static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
776 struct snd_ctl_elem_value *ucontrol)
777{
778 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800779 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800780
Takashi Iwaiece8d042011-06-19 16:24:21 +0200781 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800782 return 0;
783}
784
Takashi Iwai3b607e32011-07-18 16:54:40 +0200785/* adjust spec->multiout setup according to the current flags */
786static void setup_playback_multi_pcm(struct via_spec *spec)
787{
788 const struct auto_pin_cfg *cfg = &spec->autocfg;
789 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
790 spec->multiout.hp_nid = 0;
791 if (!spec->hp_independent_mode) {
792 if (!spec->hp_indep_shared)
793 spec->multiout.hp_nid = spec->hp_dac_nid;
794 } else {
795 if (spec->hp_indep_shared)
796 spec->multiout.num_dacs = cfg->line_outs - 1;
797 }
798}
799
800/* update DAC setups according to indep-HP switch;
801 * this function is called only when indep-HP is modified
802 */
803static void switch_indep_hp_dacs(struct hda_codec *codec)
804{
805 struct via_spec *spec = codec->spec;
806 int shared = spec->hp_indep_shared;
807 hda_nid_t shared_dac, hp_dac;
808
809 if (!spec->opened_streams)
810 return;
811
812 shared_dac = shared ? spec->multiout.dac_nids[shared] : 0;
813 hp_dac = spec->hp_dac_nid;
814 if (spec->hp_independent_mode) {
815 /* switch to indep-HP mode */
816 if (spec->active_streams & STREAM_MULTI_OUT) {
817 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
818 __snd_hda_codec_cleanup_stream(codec, shared_dac, 1);
819 }
820 if (spec->active_streams & STREAM_INDEP_HP)
821 snd_hda_codec_setup_stream(codec, hp_dac,
822 spec->cur_hp_stream_tag, 0,
823 spec->cur_hp_format);
824 } else {
825 /* back to HP or shared-DAC */
826 if (spec->active_streams & STREAM_INDEP_HP)
827 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
828 if (spec->active_streams & STREAM_MULTI_OUT) {
829 hda_nid_t dac;
830 int ch;
831 if (shared_dac) { /* reset mutli-ch DAC */
832 dac = shared_dac;
833 ch = shared * 2;
834 } else { /* reset HP DAC */
835 dac = hp_dac;
836 ch = 0;
837 }
838 snd_hda_codec_setup_stream(codec, dac,
839 spec->cur_dac_stream_tag, ch,
840 spec->cur_dac_format);
841 }
842 }
843 setup_playback_multi_pcm(spec);
844}
845
Harald Welte0aa62ae2008-09-09 15:58:27 +0800846static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
847 struct snd_ctl_elem_value *ucontrol)
848{
849 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
850 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200851 int cur, shared;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200852
Takashi Iwai3b607e32011-07-18 16:54:40 +0200853 mutex_lock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200854 cur = !!ucontrol->value.enumerated.item[0];
Takashi Iwai3b607e32011-07-18 16:54:40 +0200855 if (spec->hp_independent_mode == cur) {
856 mutex_unlock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200857 return 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200858 }
Takashi Iwai25250502011-06-30 17:24:47 +0200859 spec->hp_independent_mode = cur;
Takashi Iwai3214b962011-07-18 12:49:25 +0200860 shared = spec->hp_indep_shared;
Takashi Iwai020066d2011-07-21 13:45:56 +0200861 deactivate_hp_paths(codec);
862 if (cur)
863 activate_output_path(codec, &spec->hp_indep_path, true, false);
864 else {
Takashi Iwai3214b962011-07-18 12:49:25 +0200865 if (shared)
866 activate_output_path(codec, &spec->out_path[shared],
Takashi Iwai25250502011-06-30 17:24:47 +0200867 true, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200868 if (spec->aamix_mode || !spec->hp_path.depth)
869 activate_output_path(codec, &spec->hp_mix_path,
870 true, false);
871 else
872 activate_output_path(codec, &spec->hp_path,
873 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200874 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800875
Takashi Iwai3b607e32011-07-18 16:54:40 +0200876 switch_indep_hp_dacs(codec);
877 mutex_unlock(&spec->config_mutex);
878
Lydia Wangce0e5a92011-03-22 16:22:37 +0800879 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800880 set_widgets_power_state(codec);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200881 via_hp_automute(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200882 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800883}
884
Takashi Iwaiece8d042011-06-19 16:24:21 +0200885static const struct snd_kcontrol_new via_hp_mixer = {
886 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
887 .name = "Independent HP",
888 .info = via_independent_hp_info,
889 .get = via_independent_hp_get,
890 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800891};
892
Takashi Iwai3d83e572010-04-14 14:36:23 +0200893static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100894{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200895 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100896 struct snd_kcontrol_new *knew;
897 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100898
Takashi Iwaiece8d042011-06-19 16:24:21 +0200899 nid = spec->autocfg.hp_pins[0];
900 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200901 if (knew == NULL)
902 return -ENOMEM;
903
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100904 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100905
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100906 return 0;
907}
908
Lydia Wang1564b282009-10-10 19:07:52 +0800909static void notify_aa_path_ctls(struct hda_codec *codec)
910{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200911 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800912 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800913
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200914 for (i = 0; i < spec->smart51_nums; i++) {
915 struct snd_kcontrol *ctl;
916 struct snd_ctl_elem_id id;
917 memset(&id, 0, sizeof(id));
918 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
919 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800920 ctl = snd_hda_find_mixer_ctl(codec, id.name);
921 if (ctl)
922 snd_ctl_notify(codec->bus->card,
923 SNDRV_CTL_EVENT_MASK_VALUE,
924 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800925 }
926}
927
928static void mute_aa_path(struct hda_codec *codec, int mute)
929{
930 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200931 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800932 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200933
Lydia Wang1564b282009-10-10 19:07:52 +0800934 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200935 for (i = 0; i < spec->smart51_nums; i++) {
936 if (spec->smart51_idxs[i] < 0)
937 continue;
938 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
939 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800940 HDA_AMP_MUTE, val);
941 }
942}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200943
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200944static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
945{
946 struct via_spec *spec = codec->spec;
947 int i;
948
949 for (i = 0; i < spec->smart51_nums; i++)
950 if (spec->smart51_pins[i] == pin)
951 return true;
952 return false;
953}
954
Lydia Wang1564b282009-10-10 19:07:52 +0800955static int via_smart51_get(struct snd_kcontrol *kcontrol,
956 struct snd_ctl_elem_value *ucontrol)
957{
958 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
959 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800960
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200961 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800962 return 0;
963}
964
965static int via_smart51_put(struct snd_kcontrol *kcontrol,
966 struct snd_ctl_elem_value *ucontrol)
967{
968 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
969 struct via_spec *spec = codec->spec;
970 int out_in = *ucontrol->value.integer.value
971 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800972 int i;
973
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200974 for (i = 0; i < spec->smart51_nums; i++) {
975 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200976 unsigned int parm;
977
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200978 parm = snd_hda_codec_read(codec, nid, 0,
979 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
980 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
981 parm |= out_in;
982 snd_hda_codec_write(codec, nid, 0,
983 AC_VERB_SET_PIN_WIDGET_CONTROL,
984 parm);
985 if (out_in == AC_PINCTL_OUT_EN) {
986 mute_aa_path(codec, 1);
987 notify_aa_path_ctls(codec);
988 }
Lydia Wang1564b282009-10-10 19:07:52 +0800989 }
990 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800991 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800992 return 1;
993}
994
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200995static const struct snd_kcontrol_new via_smart51_mixer = {
996 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
997 .name = "Smart 5.1",
998 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200999 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +02001000 .get = via_smart51_get,
1001 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +08001002};
1003
Takashi Iwaif4a78282011-06-17 18:46:48 +02001004static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001005{
Takashi Iwaif4a78282011-06-17 18:46:48 +02001006 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001007
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001008 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +08001009 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001010 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001011 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001012 return 0;
1013}
1014
Takashi Iwaiada509e2011-06-20 15:40:19 +02001015/* check AA path's mute status */
1016static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001017{
Lydia Wangf5271102009-10-10 19:07:35 +08001018 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001019 const struct hda_amp_list *p;
1020 int i, ch, v;
1021
1022 for (i = 0; i < spec->num_loopbacks; i++) {
1023 p = &spec->loopback_list[i];
1024 for (ch = 0; ch < 2; ch++) {
1025 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
1026 p->idx);
1027 if (!(v & HDA_AMP_MUTE) && v > 0)
1028 return false;
Lydia Wangf5271102009-10-10 19:07:35 +08001029 }
1030 }
Takashi Iwaiada509e2011-06-20 15:40:19 +02001031 return true;
Lydia Wangf5271102009-10-10 19:07:35 +08001032}
1033
1034/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +02001035static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001036{
1037 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001038 bool enable;
1039 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +08001040
Takashi Iwai3b607e32011-07-18 16:54:40 +02001041 enable = is_aa_path_mute(codec) && (spec->opened_streams != 0);
Lydia Wangf5271102009-10-10 19:07:35 +08001042
1043 /* decide low current mode's verb & parameter */
1044 switch (spec->codec_type) {
1045 case VT1708B_8CH:
1046 case VT1708B_4CH:
1047 verb = 0xf70;
1048 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1049 break;
1050 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001051 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001052 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001053 verb = 0xf73;
1054 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1055 break;
1056 case VT1702:
1057 verb = 0xf73;
1058 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1059 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001060 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001061 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +08001062 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +08001063 verb = 0xf93;
1064 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1065 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001066 default:
1067 return; /* other codecs are not supported */
1068 }
1069 /* send verb */
1070 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1071}
1072
Joseph Chanc577b8a2006-11-29 15:29:40 +01001073/*
1074 * generic initialization of ADC, input mixers and output mixers
1075 */
Takashi Iwai096a8852011-06-20 12:09:02 +02001076static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001077 /* power down jack detect function */
1078 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001079 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001080};
1081
Takashi Iwai3b607e32011-07-18 16:54:40 +02001082static void set_stream_open(struct hda_codec *codec, int bit, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001083{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001084 struct via_spec *spec = codec->spec;
1085
1086 if (active)
Takashi Iwai3b607e32011-07-18 16:54:40 +02001087 spec->opened_streams |= bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001088 else
Takashi Iwai3b607e32011-07-18 16:54:40 +02001089 spec->opened_streams &= ~bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001090 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001091}
1092
Takashi Iwaiece8d042011-06-19 16:24:21 +02001093static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001094 struct hda_codec *codec,
1095 struct snd_pcm_substream *substream)
1096{
1097 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001098 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001099 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001100
Takashi Iwai25250502011-06-30 17:24:47 +02001101 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
Takashi Iwai25250502011-06-30 17:24:47 +02001102 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001103 set_stream_open(codec, STREAM_MULTI_OUT, true);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001104 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1105 hinfo);
1106 if (err < 0) {
Takashi Iwai3b607e32011-07-18 16:54:40 +02001107 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001108 return err;
1109 }
1110 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001111}
1112
Takashi Iwaiece8d042011-06-19 16:24:21 +02001113static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001114 struct hda_codec *codec,
1115 struct snd_pcm_substream *substream)
1116{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001117 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001118 return 0;
1119}
1120
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001121static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1122 struct hda_codec *codec,
1123 struct snd_pcm_substream *substream)
1124{
1125 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001126
Takashi Iwaiece8d042011-06-19 16:24:21 +02001127 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001128 return -EINVAL;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001129 set_stream_open(codec, STREAM_INDEP_HP, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001130 return 0;
1131}
1132
1133static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1134 struct hda_codec *codec,
1135 struct snd_pcm_substream *substream)
1136{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001137 set_stream_open(codec, STREAM_INDEP_HP, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001138 return 0;
1139}
1140
1141static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1142 struct hda_codec *codec,
1143 unsigned int stream_tag,
1144 unsigned int format,
1145 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001146{
1147 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001148
Takashi Iwai3b607e32011-07-18 16:54:40 +02001149 mutex_lock(&spec->config_mutex);
1150 setup_playback_multi_pcm(spec);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001151 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1152 format, substream);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001153 /* remember for dynamic DAC switch with indep-HP */
1154 spec->active_streams |= STREAM_MULTI_OUT;
1155 spec->cur_dac_stream_tag = stream_tag;
1156 spec->cur_dac_format = format;
1157 mutex_unlock(&spec->config_mutex);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001158 vt1708_start_hp_work(spec);
1159 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001160}
1161
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001162static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1163 struct hda_codec *codec,
1164 unsigned int stream_tag,
1165 unsigned int format,
1166 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001167{
1168 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001169
Takashi Iwai3b607e32011-07-18 16:54:40 +02001170 mutex_lock(&spec->config_mutex);
1171 if (spec->hp_independent_mode)
1172 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1173 stream_tag, 0, format);
1174 spec->active_streams |= STREAM_INDEP_HP;
1175 spec->cur_hp_stream_tag = stream_tag;
1176 spec->cur_hp_format = format;
1177 mutex_unlock(&spec->config_mutex);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001178 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001179 return 0;
1180}
1181
1182static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1183 struct hda_codec *codec,
1184 struct snd_pcm_substream *substream)
1185{
1186 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001187
Takashi Iwai3b607e32011-07-18 16:54:40 +02001188 mutex_lock(&spec->config_mutex);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001189 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001190 spec->active_streams &= ~STREAM_MULTI_OUT;
1191 mutex_unlock(&spec->config_mutex);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001192 vt1708_stop_hp_work(spec);
1193 return 0;
1194}
1195
1196static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1197 struct hda_codec *codec,
1198 struct snd_pcm_substream *substream)
1199{
1200 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001201
Takashi Iwai3b607e32011-07-18 16:54:40 +02001202 mutex_lock(&spec->config_mutex);
1203 if (spec->hp_independent_mode)
1204 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
1205 spec->active_streams &= ~STREAM_INDEP_HP;
1206 mutex_unlock(&spec->config_mutex);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001207 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001208 return 0;
1209}
1210
Joseph Chanc577b8a2006-11-29 15:29:40 +01001211/*
1212 * Digital out
1213 */
1214static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1215 struct hda_codec *codec,
1216 struct snd_pcm_substream *substream)
1217{
1218 struct via_spec *spec = codec->spec;
1219 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1220}
1221
1222static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1223 struct hda_codec *codec,
1224 struct snd_pcm_substream *substream)
1225{
1226 struct via_spec *spec = codec->spec;
1227 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1228}
1229
Harald Welte5691ec72008-09-15 22:42:26 +08001230static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001231 struct hda_codec *codec,
1232 unsigned int stream_tag,
1233 unsigned int format,
1234 struct snd_pcm_substream *substream)
1235{
1236 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001237 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1238 stream_tag, format, substream);
1239}
Harald Welte5691ec72008-09-15 22:42:26 +08001240
Takashi Iwai9da29272009-05-07 16:31:14 +02001241static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1242 struct hda_codec *codec,
1243 struct snd_pcm_substream *substream)
1244{
1245 struct via_spec *spec = codec->spec;
1246 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001247 return 0;
1248}
1249
Joseph Chanc577b8a2006-11-29 15:29:40 +01001250/*
1251 * Analog capture
1252 */
1253static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1254 struct hda_codec *codec,
1255 unsigned int stream_tag,
1256 unsigned int format,
1257 struct snd_pcm_substream *substream)
1258{
1259 struct via_spec *spec = codec->spec;
1260
1261 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1262 stream_tag, 0, format);
1263 return 0;
1264}
1265
1266static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1267 struct hda_codec *codec,
1268 struct snd_pcm_substream *substream)
1269{
1270 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001271 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001272 return 0;
1273}
1274
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001275/* analog capture with dynamic ADC switching */
1276static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1277 struct hda_codec *codec,
1278 unsigned int stream_tag,
1279 unsigned int format,
1280 struct snd_pcm_substream *substream)
1281{
1282 struct via_spec *spec = codec->spec;
1283 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1284
Takashi Iwai3b607e32011-07-18 16:54:40 +02001285 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001286 spec->cur_adc = spec->adc_nids[adc_idx];
1287 spec->cur_adc_stream_tag = stream_tag;
1288 spec->cur_adc_format = format;
1289 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001290 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001291 return 0;
1292}
1293
1294static int via_dyn_adc_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;
1299
Takashi Iwai3b607e32011-07-18 16:54:40 +02001300 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001301 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1302 spec->cur_adc = 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001303 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001304 return 0;
1305}
1306
1307/* re-setup the stream if running; called from input-src put */
1308static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1309{
1310 struct via_spec *spec = codec->spec;
1311 int adc_idx = spec->inputs[cur].adc_idx;
1312 hda_nid_t adc = spec->adc_nids[adc_idx];
Takashi Iwai3b607e32011-07-18 16:54:40 +02001313 bool ret = false;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001314
Takashi Iwai3b607e32011-07-18 16:54:40 +02001315 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001316 if (spec->cur_adc && spec->cur_adc != adc) {
1317 /* stream is running, let's swap the current ADC */
1318 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1319 spec->cur_adc = adc;
1320 snd_hda_codec_setup_stream(codec, adc,
1321 spec->cur_adc_stream_tag, 0,
1322 spec->cur_adc_format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001323 ret = true;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001324 }
Takashi Iwai3b607e32011-07-18 16:54:40 +02001325 mutex_unlock(&spec->config_mutex);
1326 return ret;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001327}
1328
Takashi Iwai9af74212011-06-18 16:17:45 +02001329static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001330 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001331 .channels_min = 2,
1332 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001333 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001334 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001335 .open = via_playback_multi_pcm_open,
1336 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001337 .prepare = via_playback_multi_pcm_prepare,
1338 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001339 },
1340};
1341
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001342static const struct hda_pcm_stream via_pcm_hp_playback = {
1343 .substreams = 1,
1344 .channels_min = 2,
1345 .channels_max = 2,
1346 /* NID is set in via_build_pcms */
1347 .ops = {
1348 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001349 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001350 .prepare = via_playback_hp_pcm_prepare,
1351 .cleanup = via_playback_hp_pcm_cleanup
1352 },
1353};
1354
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001355static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001356 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001357 .channels_min = 2,
1358 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001359 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001360 /* We got noisy outputs on the right channel on VT1708 when
1361 * 24bit samples are used. Until any workaround is found,
1362 * disable the 24bit format, so far.
1363 */
1364 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1365 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001366 .open = via_playback_multi_pcm_open,
1367 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001368 .prepare = via_playback_multi_pcm_prepare,
1369 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001370 },
1371};
1372
Takashi Iwai9af74212011-06-18 16:17:45 +02001373static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001374 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001375 .channels_min = 2,
1376 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001377 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001378 .ops = {
1379 .prepare = via_capture_pcm_prepare,
1380 .cleanup = via_capture_pcm_cleanup
1381 },
1382};
1383
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001384static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1385 .substreams = 1,
1386 .channels_min = 2,
1387 .channels_max = 2,
1388 /* NID is set in via_build_pcms */
1389 .ops = {
1390 .prepare = via_dyn_adc_capture_pcm_prepare,
1391 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1392 },
1393};
1394
Takashi Iwai9af74212011-06-18 16:17:45 +02001395static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001396 .substreams = 1,
1397 .channels_min = 2,
1398 .channels_max = 2,
1399 /* NID is set in via_build_pcms */
1400 .ops = {
1401 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001402 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001403 .prepare = via_dig_playback_pcm_prepare,
1404 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001405 },
1406};
1407
Takashi Iwai9af74212011-06-18 16:17:45 +02001408static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001409 .substreams = 1,
1410 .channels_min = 2,
1411 .channels_max = 2,
1412};
1413
Takashi Iwai370bafb2011-06-20 12:47:45 +02001414/*
1415 * slave controls for virtual master
1416 */
1417static const char * const via_slave_vols[] = {
1418 "Front Playback Volume",
1419 "Surround Playback Volume",
1420 "Center Playback Volume",
1421 "LFE Playback Volume",
1422 "Side Playback Volume",
1423 "Headphone Playback Volume",
1424 "Speaker Playback Volume",
1425 NULL,
1426};
1427
1428static const char * const via_slave_sws[] = {
1429 "Front Playback Switch",
1430 "Surround Playback Switch",
1431 "Center Playback Switch",
1432 "LFE Playback Switch",
1433 "Side Playback Switch",
1434 "Headphone Playback Switch",
1435 "Speaker Playback Switch",
1436 NULL,
1437};
1438
Joseph Chanc577b8a2006-11-29 15:29:40 +01001439static int via_build_controls(struct hda_codec *codec)
1440{
1441 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001442 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001443 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001444
Takashi Iwai24088a52011-06-17 16:59:21 +02001445 if (spec->set_widgets_power_state)
1446 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1447 return -ENOMEM;
1448
Joseph Chanc577b8a2006-11-29 15:29:40 +01001449 for (i = 0; i < spec->num_mixers; i++) {
1450 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1451 if (err < 0)
1452 return err;
1453 }
1454
1455 if (spec->multiout.dig_out_nid) {
1456 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001457 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001458 spec->multiout.dig_out_nid);
1459 if (err < 0)
1460 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001461 err = snd_hda_create_spdif_share_sw(codec,
1462 &spec->multiout);
1463 if (err < 0)
1464 return err;
1465 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001466 }
1467 if (spec->dig_in_nid) {
1468 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1469 if (err < 0)
1470 return err;
1471 }
Lydia Wang17314372009-10-10 19:07:37 +08001472
Takashi Iwai370bafb2011-06-20 12:47:45 +02001473 /* if we have no master control, let's create it */
1474 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1475 unsigned int vmaster_tlv[4];
1476 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1477 HDA_OUTPUT, vmaster_tlv);
1478 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1479 vmaster_tlv, via_slave_vols);
1480 if (err < 0)
1481 return err;
1482 }
1483 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1484 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1485 NULL, via_slave_sws);
1486 if (err < 0)
1487 return err;
1488 }
1489
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001490 /* assign Capture Source enums to NID */
1491 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1492 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001493 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001494 if (err < 0)
1495 return err;
1496 }
1497
Lydia Wang17314372009-10-10 19:07:37 +08001498 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001499 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001500 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001501
Takashi Iwai603c4012008-07-30 15:01:44 +02001502 via_free_kctls(codec); /* no longer needed */
Takashi Iwai01a61e12011-10-28 00:03:22 +02001503
1504 err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
1505 if (err < 0)
1506 return err;
1507
Joseph Chanc577b8a2006-11-29 15:29:40 +01001508 return 0;
1509}
1510
1511static int via_build_pcms(struct hda_codec *codec)
1512{
1513 struct via_spec *spec = codec->spec;
1514 struct hda_pcm *info = spec->pcm_rec;
1515
Takashi Iwaia5973102011-09-28 16:43:36 +02001516 codec->num_pcms = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001517 codec->pcm_info = info;
1518
Takashi Iwaia5973102011-09-28 16:43:36 +02001519 if (spec->multiout.num_dacs || spec->num_adc_nids) {
1520 snprintf(spec->stream_name_analog,
1521 sizeof(spec->stream_name_analog),
1522 "%s Analog", codec->chip_name);
1523 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001524
Takashi Iwaia5973102011-09-28 16:43:36 +02001525 if (spec->multiout.num_dacs) {
1526 if (!spec->stream_analog_playback)
1527 spec->stream_analog_playback =
1528 &via_pcm_analog_playback;
1529 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1530 *spec->stream_analog_playback;
1531 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1532 spec->multiout.dac_nids[0];
1533 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1534 spec->multiout.max_channels;
1535 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001536
Takashi Iwaia5973102011-09-28 16:43:36 +02001537 if (!spec->stream_analog_capture) {
1538 if (spec->dyn_adc_switch)
1539 spec->stream_analog_capture =
1540 &via_pcm_dyn_adc_analog_capture;
1541 else
1542 spec->stream_analog_capture =
1543 &via_pcm_analog_capture;
1544 }
1545 if (spec->num_adc_nids) {
1546 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1547 *spec->stream_analog_capture;
1548 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1549 spec->adc_nids[0];
1550 if (!spec->dyn_adc_switch)
1551 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1552 spec->num_adc_nids;
1553 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001554 codec->num_pcms++;
1555 info++;
Takashi Iwaia5973102011-09-28 16:43:36 +02001556 }
1557
1558 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
Takashi Iwai82673bc2011-06-17 16:24:21 +02001559 snprintf(spec->stream_name_digital,
1560 sizeof(spec->stream_name_digital),
1561 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001562 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001563 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001564 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001565 if (!spec->stream_digital_playback)
1566 spec->stream_digital_playback =
1567 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001568 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001569 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001570 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1571 spec->multiout.dig_out_nid;
1572 }
1573 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001574 if (!spec->stream_digital_capture)
1575 spec->stream_digital_capture =
1576 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001577 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001578 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001579 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1580 spec->dig_in_nid;
1581 }
Takashi Iwaia5973102011-09-28 16:43:36 +02001582 codec->num_pcms++;
1583 info++;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001584 }
1585
Takashi Iwaiece8d042011-06-19 16:24:21 +02001586 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001587 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1588 "%s HP", codec->chip_name);
1589 info->name = spec->stream_name_hp;
1590 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1591 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001592 spec->hp_dac_nid;
Takashi Iwaia5973102011-09-28 16:43:36 +02001593 codec->num_pcms++;
1594 info++;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001595 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001596 return 0;
1597}
1598
1599static void via_free(struct hda_codec *codec)
1600{
1601 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001602
1603 if (!spec)
1604 return;
1605
Takashi Iwai603c4012008-07-30 15:01:44 +02001606 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001607 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001608 kfree(spec->bind_cap_vol);
1609 kfree(spec->bind_cap_sw);
1610 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001611}
1612
Takashi Iwai64be2852011-06-17 16:51:39 +02001613/* mute/unmute outputs */
1614static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1615 hda_nid_t *pins, bool mute)
1616{
1617 int i;
Takashi Iwai94994732011-07-11 11:36:44 +02001618 for (i = 0; i < num_pins; i++) {
1619 unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
1620 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1621 if (parm & AC_PINCTL_IN_EN)
1622 continue;
1623 if (mute)
1624 parm &= ~AC_PINCTL_OUT_EN;
1625 else
1626 parm |= AC_PINCTL_OUT_EN;
Takashi Iwai64be2852011-06-17 16:51:39 +02001627 snd_hda_codec_write(codec, pins[i], 0,
Takashi Iwai94994732011-07-11 11:36:44 +02001628 AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
1629 }
Takashi Iwai64be2852011-06-17 16:51:39 +02001630}
1631
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001632/* mute internal speaker if line-out is plugged */
1633static void via_line_automute(struct hda_codec *codec, int present)
1634{
1635 struct via_spec *spec = codec->spec;
1636
1637 if (!spec->autocfg.speaker_outs)
1638 return;
1639 if (!present)
1640 present = snd_hda_jack_detect(codec,
1641 spec->autocfg.line_out_pins[0]);
1642 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1643 spec->autocfg.speaker_pins,
1644 present);
1645}
1646
Harald Welte69e52a82008-09-09 15:57:32 +08001647/* mute internal speaker if HP is plugged */
1648static void via_hp_automute(struct hda_codec *codec)
1649{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001650 int present = 0;
Takashi Iwai6e969d92011-07-11 11:28:13 +02001651 int nums;
Harald Welte69e52a82008-09-09 15:57:32 +08001652 struct via_spec *spec = codec->spec;
1653
Takashi Iwai6e969d92011-07-11 11:28:13 +02001654 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0])
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001655 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai6e969d92011-07-11 11:28:13 +02001656
1657 if (spec->smart51_enabled)
1658 nums = spec->autocfg.line_outs + spec->smart51_nums;
1659 else
1660 nums = spec->autocfg.line_outs;
1661 toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
1662
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001663 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001664}
1665
Harald Welte69e52a82008-09-09 15:57:32 +08001666static void via_gpio_control(struct hda_codec *codec)
1667{
1668 unsigned int gpio_data;
1669 unsigned int vol_counter;
1670 unsigned int vol;
1671 unsigned int master_vol;
1672
1673 struct via_spec *spec = codec->spec;
1674
1675 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1676 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1677
1678 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1679 0xF84, 0) & 0x3F0000) >> 16;
1680
1681 vol = vol_counter & 0x1F;
1682 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1683 AC_VERB_GET_AMP_GAIN_MUTE,
1684 AC_AMP_GET_INPUT);
1685
1686 if (gpio_data == 0x02) {
1687 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001688 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1689 AC_VERB_SET_PIN_WIDGET_CONTROL,
1690 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001691 if (vol_counter & 0x20) {
1692 /* decrease volume */
1693 if (vol > master_vol)
1694 vol = master_vol;
1695 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1696 0, HDA_AMP_VOLMASK,
1697 master_vol-vol);
1698 } else {
1699 /* increase volume */
1700 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1701 HDA_AMP_VOLMASK,
1702 ((master_vol+vol) > 0x2A) ? 0x2A :
1703 (master_vol+vol));
1704 }
1705 } else if (!(gpio_data & 0x02)) {
1706 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001707 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1708 AC_VERB_SET_PIN_WIDGET_CONTROL,
1709 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001710 }
1711}
1712
1713/* unsolicited event for jack sensing */
1714static void via_unsol_event(struct hda_codec *codec,
1715 unsigned int res)
1716{
1717 res >>= 26;
Takashi Iwai3a938972011-10-28 01:16:55 +02001718 res = snd_hda_jack_get_action(codec, res);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001719
Lydia Wanga34df192009-10-10 19:08:01 +08001720 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001721 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001722
1723 res &= ~VIA_JACK_EVENT;
1724
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001725 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001726 via_hp_automute(codec);
1727 else if (res == VIA_GPIO_EVENT)
1728 via_gpio_control(codec);
Takashi Iwai01a61e12011-10-28 00:03:22 +02001729 snd_hda_jack_report_sync(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001730}
1731
Takashi Iwai2a439522011-07-26 09:52:50 +02001732#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001733static int via_suspend(struct hda_codec *codec, pm_message_t state)
1734{
1735 struct via_spec *spec = codec->spec;
1736 vt1708_stop_hp_work(spec);
1737 return 0;
1738}
1739#endif
1740
Takashi Iwaicb53c622007-08-10 17:21:45 +02001741#ifdef CONFIG_SND_HDA_POWER_SAVE
1742static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1743{
1744 struct via_spec *spec = codec->spec;
1745 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1746}
1747#endif
1748
Joseph Chanc577b8a2006-11-29 15:29:40 +01001749/*
1750 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001751
1752static int via_init(struct hda_codec *codec);
1753
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001754static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001755 .build_controls = via_build_controls,
1756 .build_pcms = via_build_pcms,
1757 .init = via_init,
1758 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001759 .unsol_event = via_unsol_event,
Takashi Iwai2a439522011-07-26 09:52:50 +02001760#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001761 .suspend = via_suspend,
1762#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001763#ifdef CONFIG_SND_HDA_POWER_SAVE
1764 .check_power_status = via_check_power_status,
1765#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001766};
1767
Takashi Iwai4a796162011-06-17 17:53:38 +02001768static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001769{
Takashi Iwai4a796162011-06-17 17:53:38 +02001770 struct via_spec *spec = codec->spec;
1771 int i;
1772
1773 for (i = 0; i < spec->multiout.num_dacs; i++) {
1774 if (spec->multiout.dac_nids[i] == dac)
1775 return false;
1776 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001777 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001778 return false;
1779 return true;
1780}
1781
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001782static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001783 hda_nid_t target_dac, int with_aa_mix,
1784 struct nid_path *path, int depth)
Takashi Iwai4a796162011-06-17 17:53:38 +02001785{
Takashi Iwai3214b962011-07-18 12:49:25 +02001786 struct via_spec *spec = codec->spec;
Takashi Iwai4a796162011-06-17 17:53:38 +02001787 hda_nid_t conn[8];
1788 int i, nums;
1789
Takashi Iwai3214b962011-07-18 12:49:25 +02001790 if (nid == spec->aa_mix_nid) {
1791 if (!with_aa_mix)
1792 return false;
1793 with_aa_mix = 2; /* mark aa-mix is included */
1794 }
1795
Takashi Iwai4a796162011-06-17 17:53:38 +02001796 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1797 for (i = 0; i < nums; i++) {
1798 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1799 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001800 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1801 /* aa-mix is requested but not included? */
1802 if (!(spec->aa_mix_nid && with_aa_mix == 1))
1803 goto found;
1804 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001805 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001806 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001807 return false;
1808 for (i = 0; i < nums; i++) {
1809 unsigned int type;
1810 type = get_wcaps_type(get_wcaps(codec, conn[i]));
Takashi Iwai3214b962011-07-18 12:49:25 +02001811 if (type == AC_WID_AUD_OUT)
Takashi Iwai4a796162011-06-17 17:53:38 +02001812 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001813 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai3214b962011-07-18 12:49:25 +02001814 with_aa_mix, path, depth + 1))
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001815 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001816 }
1817 return false;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001818
1819 found:
1820 path->path[path->depth] = conn[i];
1821 path->idx[path->depth] = i;
1822 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1823 path->multi[path->depth] = 1;
1824 path->depth++;
1825 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001826}
1827
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001828static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001829 hda_nid_t target_dac, int with_aa_mix,
1830 struct nid_path *path)
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001831{
Takashi Iwai3214b962011-07-18 12:49:25 +02001832 if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001833 path->path[path->depth] = nid;
1834 path->depth++;
Takashi Iwai3214b962011-07-18 12:49:25 +02001835 snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
1836 path->depth, path->path[0], path->path[1],
1837 path->path[2], path->path[3], path->path[4]);
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001838 return true;
1839 }
1840 return false;
1841}
1842
Takashi Iwai4a796162011-06-17 17:53:38 +02001843static int via_auto_fill_dac_nids(struct hda_codec *codec)
1844{
1845 struct via_spec *spec = codec->spec;
1846 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001847 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001848 hda_nid_t nid;
1849
Joseph Chanc577b8a2006-11-29 15:29:40 +01001850 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001851 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001852 for (i = 0; i < cfg->line_outs; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001853 hda_nid_t dac = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001854 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001855 if (!nid)
1856 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001857 if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
1858 dac = spec->out_path[i].path[0];
1859 if (!i && parse_output_path(codec, nid, dac, 1,
1860 &spec->out_mix_path))
1861 dac = spec->out_mix_path.path[0];
1862 if (dac) {
1863 spec->private_dac_nids[i] = dac;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001864 dac_num++;
1865 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001866 }
Takashi Iwai3214b962011-07-18 12:49:25 +02001867 if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
1868 spec->out_path[0] = spec->out_mix_path;
1869 spec->out_mix_path.depth = 0;
1870 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001871 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001872 return 0;
1873}
1874
Takashi Iwai4a796162011-06-17 17:53:38 +02001875static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001876 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001877{
Takashi Iwai4a796162011-06-17 17:53:38 +02001878 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001879 char name[32];
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001880 hda_nid_t dac, pin, sel, nid;
1881 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001882
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001883 dac = check_dac ? path->path[0] : 0;
1884 pin = path->path[path->depth - 1];
1885 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001886
Takashi Iwai8df2a312011-06-21 11:48:29 +02001887 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001888 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001889 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001890 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001891 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1892 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001893 else
1894 nid = 0;
1895 if (nid) {
1896 sprintf(name, "%s Playback Volume", pfx);
1897 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001898 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001899 if (err < 0)
1900 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001901 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001902 }
1903
Takashi Iwai8df2a312011-06-21 11:48:29 +02001904 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001905 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001906 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001907 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001908 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1909 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001910 else
1911 nid = 0;
1912 if (nid) {
1913 sprintf(name, "%s Playback Switch", pfx);
1914 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1915 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1916 if (err < 0)
1917 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001918 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001919 }
1920 return 0;
1921}
1922
Takashi Iwaif4a78282011-06-17 18:46:48 +02001923static void mangle_smart51(struct hda_codec *codec)
1924{
1925 struct via_spec *spec = codec->spec;
1926 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001927 struct auto_pin_cfg_item *ins = cfg->inputs;
1928 int i, j, nums, attr;
1929 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001930
Takashi Iwai0f98c242011-06-21 12:51:33 +02001931 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1932 nums = 0;
1933 for (i = 0; i < cfg->num_inputs; i++) {
1934 unsigned int def;
1935 if (ins[i].type > AUTO_PIN_LINE_IN)
1936 continue;
1937 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1938 if (snd_hda_get_input_pin_attr(def) != attr)
1939 continue;
1940 for (j = 0; j < nums; j++)
1941 if (ins[pins[j]].type < ins[i].type) {
1942 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001943 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001944 break;
1945 }
1946 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001947 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001948 }
1949 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001950 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001951 for (i = 0; i < nums; i++) {
1952 hda_nid_t pin = ins[pins[i]].pin;
1953 spec->smart51_pins[spec->smart51_nums++] = pin;
1954 cfg->line_out_pins[cfg->line_outs++] = pin;
1955 if (cfg->line_outs == 3)
1956 break;
1957 }
1958 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001959 }
1960}
1961
Takashi Iwai020066d2011-07-21 13:45:56 +02001962static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src)
1963{
1964 dst->vol_ctl = src->vol_ctl;
1965 dst->mute_ctl = src->mute_ctl;
1966}
1967
Takashi Iwai4a796162011-06-17 17:53:38 +02001968/* add playback controls from the parsed DAC table */
1969static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1970{
1971 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001972 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai3214b962011-07-18 12:49:25 +02001973 struct nid_path *path;
Takashi Iwaiea734962011-01-17 11:29:34 +01001974 static const char * const chname[4] = {
1975 "Front", "Surround", "C/LFE", "Side"
1976 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001977 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001978 int old_line_outs;
1979
1980 /* check smart51 */
1981 old_line_outs = cfg->line_outs;
1982 if (cfg->line_outs == 1)
1983 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001984
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001985 err = via_auto_fill_dac_nids(codec);
1986 if (err < 0)
1987 return err;
1988
Lydia Wang5c9a5612011-07-08 14:03:43 +08001989 if (spec->multiout.num_dacs < 3) {
1990 spec->smart51_nums = 0;
1991 cfg->line_outs = old_line_outs;
1992 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001993 for (i = 0; i < cfg->line_outs; i++) {
1994 hda_nid_t pin, dac;
1995 pin = cfg->line_out_pins[i];
1996 dac = spec->multiout.dac_nids[i];
1997 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001998 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001999 path = spec->out_path + i;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02002000 if (i == HDA_CLFE) {
Takashi Iwai3214b962011-07-18 12:49:25 +02002001 err = create_ch_ctls(codec, "Center", 1, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002002 if (err < 0)
2003 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002004 err = create_ch_ctls(codec, "LFE", 2, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002005 if (err < 0)
2006 return err;
2007 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02002008 const char *pfx = chname[i];
2009 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
2010 cfg->line_outs == 1)
2011 pfx = "Speaker";
Takashi Iwai3214b962011-07-18 12:49:25 +02002012 err = create_ch_ctls(codec, pfx, 3, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002013 if (err < 0)
2014 return err;
2015 }
Takashi Iwai020066d2011-07-21 13:45:56 +02002016 if (path != spec->out_path + i)
2017 copy_path_mixer_ctls(&spec->out_path[i], path);
2018 if (path == spec->out_path && spec->out_mix_path.depth)
2019 copy_path_mixer_ctls(&spec->out_mix_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002020 }
2021
Takashi Iwai4a796162011-06-17 17:53:38 +02002022 idx = get_connection_index(codec, spec->aa_mix_nid,
2023 spec->multiout.dac_nids[0]);
2024 if (idx >= 0) {
2025 /* add control to mixer */
Takashi Iwai3214b962011-07-18 12:49:25 +02002026 const char *name;
2027 name = spec->out_mix_path.depth ?
2028 "PCM Loopback Playback Volume" : "PCM Playback Volume";
2029 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002030 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2031 idx, HDA_INPUT));
2032 if (err < 0)
2033 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002034 name = spec->out_mix_path.depth ?
2035 "PCM Loopback Playback Switch" : "PCM Playback Switch";
2036 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002037 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2038 idx, HDA_INPUT));
2039 if (err < 0)
2040 return err;
2041 }
2042
Takashi Iwaif4a78282011-06-17 18:46:48 +02002043 cfg->line_outs = old_line_outs;
2044
Joseph Chanc577b8a2006-11-29 15:29:40 +01002045 return 0;
2046}
2047
Takashi Iwai4a796162011-06-17 17:53:38 +02002048static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002049{
Takashi Iwai4a796162011-06-17 17:53:38 +02002050 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02002051 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002052 bool check_dac;
Takashi Iwai3214b962011-07-18 12:49:25 +02002053 int i, err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002054
2055 if (!pin)
2056 return 0;
2057
Takashi Iwai3214b962011-07-18 12:49:25 +02002058 if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
2059 for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
2060 if (i < spec->multiout.num_dacs &&
2061 parse_output_path(codec, pin,
2062 spec->multiout.dac_nids[i], 0,
2063 &spec->hp_indep_path)) {
2064 spec->hp_indep_shared = i;
2065 break;
2066 }
2067 }
Takashi Iwai25250502011-06-30 17:24:47 +02002068 }
Takashi Iwai3214b962011-07-18 12:49:25 +02002069 if (spec->hp_indep_path.depth) {
2070 spec->hp_dac_nid = spec->hp_indep_path.path[0];
2071 if (!spec->hp_indep_shared)
2072 spec->hp_path = spec->hp_indep_path;
2073 }
2074 /* optionally check front-path w/o AA-mix */
2075 if (!spec->hp_path.depth)
2076 parse_output_path(codec, pin,
2077 spec->multiout.dac_nids[HDA_FRONT], 0,
2078 &spec->hp_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002079
Takashi Iwaiece8d042011-06-19 16:24:21 +02002080 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai3214b962011-07-18 12:49:25 +02002081 1, &spec->hp_mix_path) && !spec->hp_path.depth)
Takashi Iwaiece8d042011-06-19 16:24:21 +02002082 return 0;
2083
Takashi Iwai3214b962011-07-18 12:49:25 +02002084 if (spec->hp_path.depth) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +02002085 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002086 check_dac = true;
2087 } else {
Takashi Iwai3214b962011-07-18 12:49:25 +02002088 path = &spec->hp_mix_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002089 check_dac = false;
2090 }
2091 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002092 if (err < 0)
2093 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002094 if (check_dac)
2095 copy_path_mixer_ctls(&spec->hp_mix_path, path);
2096 else
2097 copy_path_mixer_ctls(&spec->hp_path, path);
2098 if (spec->hp_indep_path.depth)
2099 copy_path_mixer_ctls(&spec->hp_indep_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002100 return 0;
2101}
2102
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002103static int via_auto_create_speaker_ctls(struct hda_codec *codec)
2104{
2105 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +02002106 struct nid_path *path;
2107 bool check_dac;
Wang Shaoyan81c0a782011-08-05 18:51:29 +08002108 hda_nid_t pin, dac = 0;
Takashi Iwai3214b962011-07-18 12:49:25 +02002109 int err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002110
2111 pin = spec->autocfg.speaker_pins[0];
2112 if (!spec->autocfg.speaker_outs || !pin)
2113 return 0;
2114
Takashi Iwai3214b962011-07-18 12:49:25 +02002115 if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02002116 dac = spec->speaker_path.path[0];
Takashi Iwai3214b962011-07-18 12:49:25 +02002117 if (!dac)
2118 parse_output_path(codec, pin,
2119 spec->multiout.dac_nids[HDA_FRONT], 0,
2120 &spec->speaker_path);
2121 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
2122 1, &spec->speaker_mix_path) && !dac)
2123 return 0;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002124
Takashi Iwai3214b962011-07-18 12:49:25 +02002125 /* no AA-path for front? */
2126 if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
2127 dac = 0;
2128
2129 spec->speaker_dac_nid = dac;
2130 spec->multiout.extra_out_nid[0] = dac;
2131 if (dac) {
2132 path = &spec->speaker_path;
2133 check_dac = true;
2134 } else {
2135 path = &spec->speaker_mix_path;
2136 check_dac = false;
2137 }
2138 err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
2139 if (err < 0)
2140 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002141 if (check_dac)
2142 copy_path_mixer_ctls(&spec->speaker_mix_path, path);
2143 else
2144 copy_path_mixer_ctls(&spec->speaker_path, path);
Takashi Iwai3214b962011-07-18 12:49:25 +02002145 return 0;
2146}
2147
2148#define via_aamix_ctl_info via_pin_power_ctl_info
2149
2150static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol,
2151 struct snd_ctl_elem_value *ucontrol)
2152{
2153 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2154 struct via_spec *spec = codec->spec;
2155 ucontrol->value.enumerated.item[0] = spec->aamix_mode;
2156 return 0;
2157}
2158
2159static void update_aamix_paths(struct hda_codec *codec, int do_mix,
2160 struct nid_path *nomix, struct nid_path *mix)
2161{
2162 if (do_mix) {
2163 activate_output_path(codec, nomix, false, false);
2164 activate_output_path(codec, mix, true, false);
2165 } else {
2166 activate_output_path(codec, mix, false, false);
2167 activate_output_path(codec, nomix, true, false);
2168 }
2169}
2170
2171static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol,
2172 struct snd_ctl_elem_value *ucontrol)
2173{
2174 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2175 struct via_spec *spec = codec->spec;
2176 unsigned int val = ucontrol->value.enumerated.item[0];
2177
2178 if (val == spec->aamix_mode)
2179 return 0;
2180 spec->aamix_mode = val;
2181 /* update front path */
2182 update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
2183 /* update HP path */
2184 if (!spec->hp_independent_mode) {
2185 update_aamix_paths(codec, val, &spec->hp_path,
2186 &spec->hp_mix_path);
2187 }
2188 /* update speaker path */
2189 update_aamix_paths(codec, val, &spec->speaker_path,
2190 &spec->speaker_mix_path);
2191 return 1;
2192}
2193
2194static const struct snd_kcontrol_new via_aamix_ctl_enum = {
2195 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2196 .name = "Loopback Mixing",
2197 .info = via_aamix_ctl_info,
2198 .get = via_aamix_ctl_get,
2199 .put = via_aamix_ctl_put,
2200};
2201
2202static int via_auto_create_loopback_switch(struct hda_codec *codec)
2203{
2204 struct via_spec *spec = codec->spec;
2205
2206 if (!spec->aa_mix_nid || !spec->out_mix_path.depth)
2207 return 0; /* no loopback switching available */
2208 if (!via_clone_control(spec, &via_aamix_ctl_enum))
2209 return -ENOMEM;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002210 return 0;
2211}
2212
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002213/* look for ADCs */
2214static int via_fill_adcs(struct hda_codec *codec)
2215{
2216 struct via_spec *spec = codec->spec;
2217 hda_nid_t nid = codec->start_nid;
2218 int i;
2219
2220 for (i = 0; i < codec->num_nodes; i++, nid++) {
2221 unsigned int wcaps = get_wcaps(codec, nid);
2222 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2223 continue;
2224 if (wcaps & AC_WCAP_DIGITAL)
2225 continue;
2226 if (!(wcaps & AC_WCAP_CONN_LIST))
2227 continue;
2228 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2229 return -ENOMEM;
2230 spec->adc_nids[spec->num_adc_nids++] = nid;
2231 }
2232 return 0;
2233}
2234
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002235/* input-src control */
2236static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2237 struct snd_ctl_elem_info *uinfo)
2238{
2239 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2240 struct via_spec *spec = codec->spec;
2241
2242 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2243 uinfo->count = 1;
2244 uinfo->value.enumerated.items = spec->num_inputs;
2245 if (uinfo->value.enumerated.item >= spec->num_inputs)
2246 uinfo->value.enumerated.item = spec->num_inputs - 1;
2247 strcpy(uinfo->value.enumerated.name,
2248 spec->inputs[uinfo->value.enumerated.item].label);
2249 return 0;
2250}
2251
2252static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2253 struct snd_ctl_elem_value *ucontrol)
2254{
2255 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2256 struct via_spec *spec = codec->spec;
2257 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2258
2259 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2260 return 0;
2261}
2262
2263static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2264 struct snd_ctl_elem_value *ucontrol)
2265{
2266 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2267 struct via_spec *spec = codec->spec;
2268 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2269 hda_nid_t mux;
2270 int cur;
2271
2272 cur = ucontrol->value.enumerated.item[0];
2273 if (cur < 0 || cur >= spec->num_inputs)
2274 return -EINVAL;
2275 if (spec->cur_mux[idx] == cur)
2276 return 0;
2277 spec->cur_mux[idx] = cur;
2278 if (spec->dyn_adc_switch) {
2279 int adc_idx = spec->inputs[cur].adc_idx;
2280 mux = spec->mux_nids[adc_idx];
2281 via_dyn_adc_pcm_resetup(codec, cur);
2282 } else {
2283 mux = spec->mux_nids[idx];
2284 if (snd_BUG_ON(!mux))
2285 return -EINVAL;
2286 }
2287
2288 if (mux) {
2289 /* switch to D0 beofre change index */
2290 if (snd_hda_codec_read(codec, mux, 0,
2291 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2292 snd_hda_codec_write(codec, mux, 0,
2293 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2294 snd_hda_codec_write(codec, mux, 0,
2295 AC_VERB_SET_CONNECT_SEL,
2296 spec->inputs[cur].mux_idx);
2297 }
2298
2299 /* update jack power state */
2300 set_widgets_power_state(codec);
2301 return 0;
2302}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002303
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002304static const struct snd_kcontrol_new via_input_src_ctl = {
2305 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2306 /* The multiple "Capture Source" controls confuse alsamixer
2307 * So call somewhat different..
2308 */
2309 /* .name = "Capture Source", */
2310 .name = "Input Source",
2311 .info = via_mux_enum_info,
2312 .get = via_mux_enum_get,
2313 .put = via_mux_enum_put,
2314};
2315
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002316static int create_input_src_ctls(struct hda_codec *codec, int count)
2317{
2318 struct via_spec *spec = codec->spec;
2319 struct snd_kcontrol_new *knew;
2320
2321 if (spec->num_inputs <= 1 || !count)
2322 return 0; /* no need for single src */
2323
2324 knew = via_clone_control(spec, &via_input_src_ctl);
2325 if (!knew)
2326 return -ENOMEM;
2327 knew->count = count;
2328 return 0;
2329}
2330
2331/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002332static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2333{
2334 struct hda_amp_list *list;
2335
2336 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2337 return;
2338 list = spec->loopback_list + spec->num_loopbacks;
2339 list->nid = mix;
2340 list->dir = HDA_INPUT;
2341 list->idx = idx;
2342 spec->num_loopbacks++;
2343 spec->loopback.amplist = spec->loopback_list;
2344}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002345
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002346static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002347 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002348{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002349 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002350}
2351
2352/* add the input-route to the given pin */
2353static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002354{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002355 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002356 int c, idx;
2357
2358 spec->inputs[spec->num_inputs].adc_idx = -1;
2359 spec->inputs[spec->num_inputs].pin = pin;
2360 for (c = 0; c < spec->num_adc_nids; c++) {
2361 if (spec->mux_nids[c]) {
2362 idx = get_connection_index(codec, spec->mux_nids[c],
2363 pin);
2364 if (idx < 0)
2365 continue;
2366 spec->inputs[spec->num_inputs].mux_idx = idx;
2367 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002368 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002369 continue;
2370 }
2371 spec->inputs[spec->num_inputs].adc_idx = c;
2372 /* Can primary ADC satisfy all inputs? */
2373 if (!spec->dyn_adc_switch &&
2374 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2375 snd_printd(KERN_INFO
2376 "via: dynamic ADC switching enabled\n");
2377 spec->dyn_adc_switch = 1;
2378 }
2379 return true;
2380 }
2381 return false;
2382}
2383
2384static int get_mux_nids(struct hda_codec *codec);
2385
2386/* parse input-routes; fill ADCs, MUXs and input-src entries */
2387static int parse_analog_inputs(struct hda_codec *codec)
2388{
2389 struct via_spec *spec = codec->spec;
2390 const struct auto_pin_cfg *cfg = &spec->autocfg;
2391 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002392
2393 err = via_fill_adcs(codec);
2394 if (err < 0)
2395 return err;
2396 err = get_mux_nids(codec);
2397 if (err < 0)
2398 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002399
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002400 /* fill all input-routes */
2401 for (i = 0; i < cfg->num_inputs; i++) {
2402 if (add_input_route(codec, cfg->inputs[i].pin))
2403 spec->inputs[spec->num_inputs++].label =
2404 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002405 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002406
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002407 /* check for internal loopback recording */
2408 if (spec->aa_mix_nid &&
2409 add_input_route(codec, spec->aa_mix_nid))
2410 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2411
2412 return 0;
2413}
2414
2415/* create analog-loopback volume/switch controls */
2416static int create_loopback_ctls(struct hda_codec *codec)
2417{
2418 struct via_spec *spec = codec->spec;
2419 const struct auto_pin_cfg *cfg = &spec->autocfg;
2420 const char *prev_label = NULL;
2421 int type_idx = 0;
2422 int i, j, err, idx;
2423
2424 if (!spec->aa_mix_nid)
2425 return 0;
2426
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002427 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002428 hda_nid_t pin = cfg->inputs[i].pin;
2429 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2430
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002431 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002432 type_idx++;
2433 else
2434 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002435 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002436 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2437 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002438 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002439 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002440 if (err < 0)
2441 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002442 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002443 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002444
2445 /* remember the label for smart51 control */
2446 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002447 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002448 spec->smart51_idxs[j] = idx;
2449 spec->smart51_labels[j] = label;
2450 break;
2451 }
2452 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002453 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002454 return 0;
2455}
2456
2457/* create mic-boost controls (if present) */
2458static int create_mic_boost_ctls(struct hda_codec *codec)
2459{
2460 struct via_spec *spec = codec->spec;
2461 const struct auto_pin_cfg *cfg = &spec->autocfg;
2462 int i, err;
2463
2464 for (i = 0; i < cfg->num_inputs; i++) {
2465 hda_nid_t pin = cfg->inputs[i].pin;
2466 unsigned int caps;
2467 const char *label;
2468 char name[32];
2469
2470 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2471 continue;
2472 caps = query_amp_caps(codec, pin, HDA_INPUT);
2473 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2474 continue;
2475 label = hda_get_autocfg_input_label(codec, cfg, i);
2476 snprintf(name, sizeof(name), "%s Boost Volume", label);
2477 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2478 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2479 if (err < 0)
2480 return err;
2481 }
2482 return 0;
2483}
2484
2485/* create capture and input-src controls for multiple streams */
2486static int create_multi_adc_ctls(struct hda_codec *codec)
2487{
2488 struct via_spec *spec = codec->spec;
2489 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002490
2491 /* create capture mixer elements */
2492 for (i = 0; i < spec->num_adc_nids; i++) {
2493 hda_nid_t adc = spec->adc_nids[i];
2494 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2495 "Capture Volume", i,
2496 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2497 HDA_INPUT));
2498 if (err < 0)
2499 return err;
2500 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2501 "Capture Switch", i,
2502 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2503 HDA_INPUT));
2504 if (err < 0)
2505 return err;
2506 }
2507
2508 /* input-source control */
2509 for (i = 0; i < spec->num_adc_nids; i++)
2510 if (!spec->mux_nids[i])
2511 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002512 err = create_input_src_ctls(codec, i);
2513 if (err < 0)
2514 return err;
2515 return 0;
2516}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002517
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002518/* bind capture volume/switch */
2519static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2520 HDA_BIND_VOL("Capture Volume", 0);
2521static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2522 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002523
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002524static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2525 struct hda_ctl_ops *ops)
2526{
2527 struct hda_bind_ctls *ctl;
2528 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002529
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002530 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2531 if (!ctl)
2532 return -ENOMEM;
2533 ctl->ops = ops;
2534 for (i = 0; i < spec->num_adc_nids; i++)
2535 ctl->values[i] =
2536 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2537 *ctl_ret = ctl;
2538 return 0;
2539}
2540
2541/* create capture and input-src controls for dynamic ADC-switch case */
2542static int create_dyn_adc_ctls(struct hda_codec *codec)
2543{
2544 struct via_spec *spec = codec->spec;
2545 struct snd_kcontrol_new *knew;
2546 int err;
2547
2548 /* set up the bind capture ctls */
2549 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2550 if (err < 0)
2551 return err;
2552 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2553 if (err < 0)
2554 return err;
2555
2556 /* create capture mixer elements */
2557 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2558 if (!knew)
2559 return -ENOMEM;
2560 knew->private_value = (long)spec->bind_cap_vol;
2561
2562 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2563 if (!knew)
2564 return -ENOMEM;
2565 knew->private_value = (long)spec->bind_cap_sw;
2566
2567 /* input-source control */
2568 err = create_input_src_ctls(codec, 1);
2569 if (err < 0)
2570 return err;
2571 return 0;
2572}
2573
2574/* parse and create capture-related stuff */
2575static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2576{
2577 struct via_spec *spec = codec->spec;
2578 int err;
2579
2580 err = parse_analog_inputs(codec);
2581 if (err < 0)
2582 return err;
2583 if (spec->dyn_adc_switch)
2584 err = create_dyn_adc_ctls(codec);
2585 else
2586 err = create_multi_adc_ctls(codec);
2587 if (err < 0)
2588 return err;
2589 err = create_loopback_ctls(codec);
2590 if (err < 0)
2591 return err;
2592 err = create_mic_boost_ctls(codec);
2593 if (err < 0)
2594 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002595 return 0;
2596}
2597
Harald Welte76d9b0d2008-09-09 15:50:37 +08002598static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2599{
2600 unsigned int def_conf;
2601 unsigned char seqassoc;
2602
Takashi Iwai2f334f92009-02-20 14:37:42 +01002603 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002604 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2605 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002606 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2607 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2608 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2609 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002610 }
2611
2612 return;
2613}
2614
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002615static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002616 struct snd_ctl_elem_value *ucontrol)
2617{
2618 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2619 struct via_spec *spec = codec->spec;
2620
2621 if (spec->codec_type != VT1708)
2622 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002623 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002624 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002625 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002626 return 0;
2627}
2628
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002629static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002630 struct snd_ctl_elem_value *ucontrol)
2631{
2632 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2633 struct via_spec *spec = codec->spec;
2634 int change;
2635
2636 if (spec->codec_type != VT1708)
2637 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002638 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002639 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002640 == !spec->vt1708_jack_detect;
2641 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002642 mute_aa_path(codec, 1);
2643 notify_aa_path_ctls(codec);
2644 }
2645 return change;
2646}
2647
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002648static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2649 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2650 .name = "Jack Detect",
2651 .count = 1,
2652 .info = snd_ctl_boolean_mono_info,
2653 .get = vt1708_jack_detect_get,
2654 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002655};
2656
Takashi Iwai12daef62011-06-18 17:45:49 +02002657static void fill_dig_outs(struct hda_codec *codec);
2658static void fill_dig_in(struct hda_codec *codec);
2659
2660static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002661{
2662 struct via_spec *spec = codec->spec;
2663 int err;
2664
2665 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2666 if (err < 0)
2667 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002668 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002669 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002670
Takashi Iwai4a796162011-06-17 17:53:38 +02002671 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002672 if (err < 0)
2673 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002674 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002675 if (err < 0)
2676 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002677 err = via_auto_create_speaker_ctls(codec);
2678 if (err < 0)
2679 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002680 err = via_auto_create_loopback_switch(codec);
2681 if (err < 0)
2682 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002683 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002684 if (err < 0)
2685 return err;
2686
2687 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2688
Takashi Iwai12daef62011-06-18 17:45:49 +02002689 fill_dig_outs(codec);
2690 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002691
Takashi Iwai603c4012008-07-30 15:01:44 +02002692 if (spec->kctls.list)
2693 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002694
Joseph Chanc577b8a2006-11-29 15:29:40 +01002695
Takashi Iwai3214b962011-07-18 12:49:25 +02002696 if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002697 err = via_hp_build(codec);
2698 if (err < 0)
2699 return err;
2700 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002701
Takashi Iwaif4a78282011-06-17 18:46:48 +02002702 err = via_smart51_build(codec);
2703 if (err < 0)
2704 return err;
2705
Takashi Iwai5d417622011-06-20 11:32:27 +02002706 /* assign slave outs */
2707 if (spec->slave_dig_outs[0])
2708 codec->slave_dig_outs = spec->slave_dig_outs;
2709
Joseph Chanc577b8a2006-11-29 15:29:40 +01002710 return 1;
2711}
2712
Takashi Iwai5d417622011-06-20 11:32:27 +02002713static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002714{
Lydia Wang25eaba22009-10-10 19:08:43 +08002715 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002716 if (spec->multiout.dig_out_nid)
2717 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2718 if (spec->slave_dig_outs[0])
2719 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2720}
Lydia Wang25eaba22009-10-10 19:08:43 +08002721
Takashi Iwai5d417622011-06-20 11:32:27 +02002722static void via_auto_init_dig_in(struct hda_codec *codec)
2723{
2724 struct via_spec *spec = codec->spec;
2725 if (!spec->dig_in_nid)
2726 return;
2727 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2728 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2729}
2730
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002731/* initialize the unsolicited events */
2732static void via_auto_init_unsol_event(struct hda_codec *codec)
2733{
2734 struct via_spec *spec = codec->spec;
2735 struct auto_pin_cfg *cfg = &spec->autocfg;
2736 unsigned int ev;
2737 int i;
2738
2739 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002740 snd_hda_jack_detect_enable(codec, cfg->hp_pins[0],
2741 VIA_HP_EVENT | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002742
2743 if (cfg->speaker_pins[0])
2744 ev = VIA_LINE_EVENT;
2745 else
2746 ev = 0;
2747 for (i = 0; i < cfg->line_outs; i++) {
2748 if (cfg->line_out_pins[i] &&
2749 is_jack_detectable(codec, cfg->line_out_pins[i]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002750 snd_hda_jack_detect_enable(codec, cfg->line_out_pins[i],
2751 ev | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002752 }
2753
2754 for (i = 0; i < cfg->num_inputs; i++) {
2755 if (is_jack_detectable(codec, cfg->inputs[i].pin))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002756 snd_hda_jack_detect_enable(codec, cfg->inputs[i].pin,
2757 VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002758 }
2759}
2760
Takashi Iwai5d417622011-06-20 11:32:27 +02002761static int via_init(struct hda_codec *codec)
2762{
2763 struct via_spec *spec = codec->spec;
2764 int i;
2765
2766 for (i = 0; i < spec->num_iverbs; i++)
2767 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2768
Joseph Chanc577b8a2006-11-29 15:29:40 +01002769 via_auto_init_multi_out(codec);
2770 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002771 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002772 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002773 via_auto_init_dig_outs(codec);
2774 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002775
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002776 via_auto_init_unsol_event(codec);
2777
2778 via_hp_automute(codec);
Takashi Iwai01a61e12011-10-28 00:03:22 +02002779 snd_hda_jack_report_sync(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002780
Joseph Chanc577b8a2006-11-29 15:29:40 +01002781 return 0;
2782}
2783
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002784static void vt1708_update_hp_jack_state(struct work_struct *work)
2785{
2786 struct via_spec *spec = container_of(work, struct via_spec,
2787 vt1708_hp_work.work);
2788 if (spec->codec_type != VT1708)
2789 return;
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002790 snd_hda_jack_set_dirty_all(spec->codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002791 /* if jack state toggled */
2792 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002793 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002794 spec->vt1708_hp_present ^= 1;
2795 via_hp_automute(spec->codec);
2796 }
2797 vt1708_start_hp_work(spec);
2798}
2799
Takashi Iwai337b9d02009-07-07 18:18:59 +02002800static int get_mux_nids(struct hda_codec *codec)
2801{
2802 struct via_spec *spec = codec->spec;
2803 hda_nid_t nid, conn[8];
2804 unsigned int type;
2805 int i, n;
2806
2807 for (i = 0; i < spec->num_adc_nids; i++) {
2808 nid = spec->adc_nids[i];
2809 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002810 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002811 if (type == AC_WID_PIN)
2812 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002813 n = snd_hda_get_connections(codec, nid, conn,
2814 ARRAY_SIZE(conn));
2815 if (n <= 0)
2816 break;
2817 if (n > 1) {
2818 spec->mux_nids[i] = nid;
2819 break;
2820 }
2821 nid = conn[0];
2822 }
2823 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002824 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002825}
2826
Joseph Chanc577b8a2006-11-29 15:29:40 +01002827static int patch_vt1708(struct hda_codec *codec)
2828{
2829 struct via_spec *spec;
2830 int err;
2831
2832 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002833 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002834 if (spec == NULL)
2835 return -ENOMEM;
2836
Takashi Iwai620e2b22011-06-17 17:19:19 +02002837 spec->aa_mix_nid = 0x17;
2838
Takashi Iwai12daef62011-06-18 17:45:49 +02002839 /* Add HP and CD pin config connect bit re-config action */
2840 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2841 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2842
Joseph Chanc577b8a2006-11-29 15:29:40 +01002843 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002844 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002845 if (err < 0) {
2846 via_free(codec);
2847 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002848 }
2849
Takashi Iwai12daef62011-06-18 17:45:49 +02002850 /* add jack detect on/off control */
2851 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2852 return -ENOMEM;
2853
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002854 /* disable 32bit format on VT1708 */
2855 if (codec->vendor_id == 0x11061708)
2856 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002857
Lydia Wange322a362011-06-29 13:52:02 +08002858 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2859
Joseph Chanc577b8a2006-11-29 15:29:40 +01002860 codec->patch_ops = via_patch_ops;
2861
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002862 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002863 return 0;
2864}
2865
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002866static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002867{
2868 struct via_spec *spec;
2869 int err;
2870
2871 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002872 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002873 if (spec == NULL)
2874 return -ENOMEM;
2875
Takashi Iwai620e2b22011-06-17 17:19:19 +02002876 spec->aa_mix_nid = 0x18;
2877
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
Joseph Chanc577b8a2006-11-29 15:29:40 +01002884 codec->patch_ops = via_patch_ops;
2885
Josepch Chanf7278fd2007-12-13 16:40:40 +01002886 return 0;
2887}
2888
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002889static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2890{
2891 struct via_spec *spec = codec->spec;
2892 int imux_is_smixer;
2893 unsigned int parm;
2894 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002895 if ((spec->codec_type != VT1708B_4CH) &&
2896 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002897 is_8ch = 1;
2898
2899 /* SW0 (17h) = stereo mixer */
2900 imux_is_smixer =
2901 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2902 == ((spec->codec_type == VT1708S) ? 5 : 0));
2903 /* inputs */
2904 /* PW 1/2/5 (1ah/1bh/1eh) */
2905 parm = AC_PWRST_D3;
2906 set_pin_power_state(codec, 0x1a, &parm);
2907 set_pin_power_state(codec, 0x1b, &parm);
2908 set_pin_power_state(codec, 0x1e, &parm);
2909 if (imux_is_smixer)
2910 parm = AC_PWRST_D0;
2911 /* SW0 (17h), AIW 0/1 (13h/14h) */
2912 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2913 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2914 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2915
2916 /* outputs */
2917 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2918 parm = AC_PWRST_D3;
2919 set_pin_power_state(codec, 0x19, &parm);
2920 if (spec->smart51_enabled)
2921 set_pin_power_state(codec, 0x1b, &parm);
2922 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2923 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2924
2925 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2926 if (is_8ch) {
2927 parm = AC_PWRST_D3;
2928 set_pin_power_state(codec, 0x22, &parm);
2929 if (spec->smart51_enabled)
2930 set_pin_power_state(codec, 0x1a, &parm);
2931 snd_hda_codec_write(codec, 0x26, 0,
2932 AC_VERB_SET_POWER_STATE, parm);
2933 snd_hda_codec_write(codec, 0x24, 0,
2934 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002935 } else if (codec->vendor_id == 0x11064397) {
2936 /* PW7(23h), SW2(27h), AOW2(25h) */
2937 parm = AC_PWRST_D3;
2938 set_pin_power_state(codec, 0x23, &parm);
2939 if (spec->smart51_enabled)
2940 set_pin_power_state(codec, 0x1a, &parm);
2941 snd_hda_codec_write(codec, 0x27, 0,
2942 AC_VERB_SET_POWER_STATE, parm);
2943 snd_hda_codec_write(codec, 0x25, 0,
2944 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002945 }
2946
2947 /* PW 3/4/7 (1ch/1dh/23h) */
2948 parm = AC_PWRST_D3;
2949 /* force to D0 for internal Speaker */
2950 set_pin_power_state(codec, 0x1c, &parm);
2951 set_pin_power_state(codec, 0x1d, &parm);
2952 if (is_8ch)
2953 set_pin_power_state(codec, 0x23, &parm);
2954
2955 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2956 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2957 imux_is_smixer ? AC_PWRST_D0 : parm);
2958 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2959 if (is_8ch) {
2960 snd_hda_codec_write(codec, 0x25, 0,
2961 AC_VERB_SET_POWER_STATE, parm);
2962 snd_hda_codec_write(codec, 0x27, 0,
2963 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002964 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2965 snd_hda_codec_write(codec, 0x25, 0,
2966 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002967}
2968
Lydia Wang518bf3b2009-10-10 19:07:29 +08002969static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002970static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002971{
2972 struct via_spec *spec;
2973 int err;
2974
Lydia Wang518bf3b2009-10-10 19:07:29 +08002975 if (get_codec_type(codec) == VT1708BCE)
2976 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002977
Josepch Chanf7278fd2007-12-13 16:40:40 +01002978 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002979 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002980 if (spec == NULL)
2981 return -ENOMEM;
2982
Takashi Iwai620e2b22011-06-17 17:19:19 +02002983 spec->aa_mix_nid = 0x16;
2984
Josepch Chanf7278fd2007-12-13 16:40:40 +01002985 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002986 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002987 if (err < 0) {
2988 via_free(codec);
2989 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002990 }
2991
Josepch Chanf7278fd2007-12-13 16:40:40 +01002992 codec->patch_ops = via_patch_ops;
2993
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002994 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2995
Josepch Chanf7278fd2007-12-13 16:40:40 +01002996 return 0;
2997}
2998
Harald Welted949cac2008-09-09 15:56:01 +08002999/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02003000static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08003001 /* Enable Mic Boost Volume backdoor */
3002 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003003 /* don't bybass mixer */
3004 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08003005 { }
3006};
3007
Takashi Iwai9da29272009-05-07 16:31:14 +02003008/* fill out digital output widgets; one for master and one for slave outputs */
3009static void fill_dig_outs(struct hda_codec *codec)
3010{
3011 struct via_spec *spec = codec->spec;
3012 int i;
3013
3014 for (i = 0; i < spec->autocfg.dig_outs; i++) {
3015 hda_nid_t nid;
3016 int conn;
3017
3018 nid = spec->autocfg.dig_out_pins[i];
3019 if (!nid)
3020 continue;
3021 conn = snd_hda_get_connections(codec, nid, &nid, 1);
3022 if (conn < 1)
3023 continue;
3024 if (!spec->multiout.dig_out_nid)
3025 spec->multiout.dig_out_nid = nid;
3026 else {
3027 spec->slave_dig_outs[0] = nid;
3028 break; /* at most two dig outs */
3029 }
3030 }
3031}
3032
Takashi Iwai12daef62011-06-18 17:45:49 +02003033static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08003034{
3035 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02003036 hda_nid_t dig_nid;
3037 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08003038
Takashi Iwai12daef62011-06-18 17:45:49 +02003039 if (!spec->autocfg.dig_in_pin)
3040 return;
Harald Welted949cac2008-09-09 15:56:01 +08003041
Takashi Iwai12daef62011-06-18 17:45:49 +02003042 dig_nid = codec->start_nid;
3043 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
3044 unsigned int wcaps = get_wcaps(codec, dig_nid);
3045 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
3046 continue;
3047 if (!(wcaps & AC_WCAP_DIGITAL))
3048 continue;
3049 if (!(wcaps & AC_WCAP_CONN_LIST))
3050 continue;
3051 err = get_connection_index(codec, dig_nid,
3052 spec->autocfg.dig_in_pin);
3053 if (err >= 0) {
3054 spec->dig_in_nid = dig_nid;
3055 break;
3056 }
3057 }
Harald Welted949cac2008-09-09 15:56:01 +08003058}
3059
Lydia Wang6369bcf2009-10-10 19:08:31 +08003060static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
3061 int offset, int num_steps, int step_size)
3062{
3063 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
3064 (offset << AC_AMPCAP_OFFSET_SHIFT) |
3065 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
3066 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
3067 (0 << AC_AMPCAP_MUTE_SHIFT));
3068}
3069
Harald Welted949cac2008-09-09 15:56:01 +08003070static int patch_vt1708S(struct hda_codec *codec)
3071{
3072 struct via_spec *spec;
3073 int err;
3074
3075 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003076 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003077 if (spec == NULL)
3078 return -ENOMEM;
3079
Takashi Iwai620e2b22011-06-17 17:19:19 +02003080 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003081 override_mic_boost(codec, 0x1a, 0, 3, 40);
3082 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003083
Harald Welted949cac2008-09-09 15:56:01 +08003084 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003085 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003086 if (err < 0) {
3087 via_free(codec);
3088 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003089 }
3090
Takashi Iwai096a8852011-06-20 12:09:02 +02003091 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003092
Harald Welted949cac2008-09-09 15:56:01 +08003093 codec->patch_ops = via_patch_ops;
3094
Lydia Wang518bf3b2009-10-10 19:07:29 +08003095 /* correct names for VT1708BCE */
3096 if (get_codec_type(codec) == VT1708BCE) {
3097 kfree(codec->chip_name);
3098 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3099 snprintf(codec->bus->card->mixername,
3100 sizeof(codec->bus->card->mixername),
3101 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08003102 }
Lydia Wangbc92df72011-03-23 17:56:05 +08003103 /* correct names for VT1705 */
3104 if (codec->vendor_id == 0x11064397) {
3105 kfree(codec->chip_name);
3106 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3107 snprintf(codec->bus->card->mixername,
3108 sizeof(codec->bus->card->mixername),
3109 "%s %s", codec->vendor_name, codec->chip_name);
3110 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003111 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08003112 return 0;
3113}
3114
3115/* Patch for VT1702 */
3116
Takashi Iwai096a8852011-06-20 12:09:02 +02003117static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003118 /* mixer enable */
3119 {0x1, 0xF88, 0x3},
3120 /* GPIO 0~2 */
3121 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003122 { }
3123};
3124
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003125static void set_widgets_power_state_vt1702(struct hda_codec *codec)
3126{
3127 int imux_is_smixer =
3128 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3129 unsigned int parm;
3130 /* inputs */
3131 /* PW 1/2/5 (14h/15h/18h) */
3132 parm = AC_PWRST_D3;
3133 set_pin_power_state(codec, 0x14, &parm);
3134 set_pin_power_state(codec, 0x15, &parm);
3135 set_pin_power_state(codec, 0x18, &parm);
3136 if (imux_is_smixer)
3137 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
3138 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
3139 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3140 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
3141 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3142 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
3143
3144 /* outputs */
3145 /* PW 3/4 (16h/17h) */
3146 parm = AC_PWRST_D3;
3147 set_pin_power_state(codec, 0x17, &parm);
3148 set_pin_power_state(codec, 0x16, &parm);
3149 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
3150 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
3151 imux_is_smixer ? AC_PWRST_D0 : parm);
3152 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3153 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3154}
3155
Harald Welted949cac2008-09-09 15:56:01 +08003156static int patch_vt1702(struct hda_codec *codec)
3157{
3158 struct via_spec *spec;
3159 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003160
3161 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003162 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003163 if (spec == NULL)
3164 return -ENOMEM;
3165
Takashi Iwai620e2b22011-06-17 17:19:19 +02003166 spec->aa_mix_nid = 0x1a;
3167
Takashi Iwai12daef62011-06-18 17:45:49 +02003168 /* limit AA path volume to 0 dB */
3169 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3170 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3171 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3172 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3173 (1 << AC_AMPCAP_MUTE_SHIFT));
3174
Harald Welted949cac2008-09-09 15:56:01 +08003175 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003176 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003177 if (err < 0) {
3178 via_free(codec);
3179 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003180 }
3181
Takashi Iwai096a8852011-06-20 12:09:02 +02003182 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003183
Harald Welted949cac2008-09-09 15:56:01 +08003184 codec->patch_ops = via_patch_ops;
3185
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003186 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08003187 return 0;
3188}
3189
Lydia Wangeb7188c2009-10-10 19:08:34 +08003190/* Patch for VT1718S */
3191
Takashi Iwai096a8852011-06-20 12:09:02 +02003192static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08003193 /* Enable MW0 adjust Gain 5 */
3194 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003195 /* Enable Boost Volume backdoor */
3196 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02003197
Lydia Wangeb7188c2009-10-10 19:08:34 +08003198 { }
3199};
3200
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003201static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
3202{
3203 struct via_spec *spec = codec->spec;
3204 int imux_is_smixer;
3205 unsigned int parm;
3206 /* MUX6 (1eh) = stereo mixer */
3207 imux_is_smixer =
3208 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3209 /* inputs */
3210 /* PW 5/6/7 (29h/2ah/2bh) */
3211 parm = AC_PWRST_D3;
3212 set_pin_power_state(codec, 0x29, &parm);
3213 set_pin_power_state(codec, 0x2a, &parm);
3214 set_pin_power_state(codec, 0x2b, &parm);
3215 if (imux_is_smixer)
3216 parm = AC_PWRST_D0;
3217 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
3218 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3219 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3220 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3221 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3222
3223 /* outputs */
3224 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
3225 parm = AC_PWRST_D3;
3226 set_pin_power_state(codec, 0x27, &parm);
3227 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
3228 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
3229
3230 /* PW2 (26h), AOW2 (ah) */
3231 parm = AC_PWRST_D3;
3232 set_pin_power_state(codec, 0x26, &parm);
3233 if (spec->smart51_enabled)
3234 set_pin_power_state(codec, 0x2b, &parm);
3235 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
3236
3237 /* PW0 (24h), AOW0 (8h) */
3238 parm = AC_PWRST_D3;
3239 set_pin_power_state(codec, 0x24, &parm);
3240 if (!spec->hp_independent_mode) /* check for redirected HP */
3241 set_pin_power_state(codec, 0x28, &parm);
3242 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3243 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
3244 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
3245 imux_is_smixer ? AC_PWRST_D0 : parm);
3246
3247 /* PW1 (25h), AOW1 (9h) */
3248 parm = AC_PWRST_D3;
3249 set_pin_power_state(codec, 0x25, &parm);
3250 if (spec->smart51_enabled)
3251 set_pin_power_state(codec, 0x2a, &parm);
3252 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
3253
3254 if (spec->hp_independent_mode) {
3255 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3256 parm = AC_PWRST_D3;
3257 set_pin_power_state(codec, 0x28, &parm);
3258 snd_hda_codec_write(codec, 0x1b, 0,
3259 AC_VERB_SET_POWER_STATE, parm);
3260 snd_hda_codec_write(codec, 0x34, 0,
3261 AC_VERB_SET_POWER_STATE, parm);
3262 snd_hda_codec_write(codec, 0xc, 0,
3263 AC_VERB_SET_POWER_STATE, parm);
3264 }
3265}
3266
Takashi Iwai30b45032011-07-11 17:05:04 +02003267/* Add a connection to the primary DAC from AA-mixer for some codecs
3268 * This isn't listed from the raw info, but the chip has a secret connection.
3269 */
3270static int add_secret_dac_path(struct hda_codec *codec)
3271{
3272 struct via_spec *spec = codec->spec;
3273 int i, nums;
3274 hda_nid_t conn[8];
3275 hda_nid_t nid;
3276
3277 if (!spec->aa_mix_nid)
3278 return 0;
3279 nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
3280 ARRAY_SIZE(conn) - 1);
3281 for (i = 0; i < nums; i++) {
3282 if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
3283 return 0;
3284 }
3285
3286 /* find the primary DAC and add to the connection list */
3287 nid = codec->start_nid;
3288 for (i = 0; i < codec->num_nodes; i++, nid++) {
3289 unsigned int caps = get_wcaps(codec, nid);
3290 if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
3291 !(caps & AC_WCAP_DIGITAL)) {
3292 conn[nums++] = nid;
3293 return snd_hda_override_conn_list(codec,
3294 spec->aa_mix_nid,
3295 nums, conn);
3296 }
3297 }
3298 return 0;
3299}
3300
3301
Lydia Wangeb7188c2009-10-10 19:08:34 +08003302static int patch_vt1718S(struct hda_codec *codec)
3303{
3304 struct via_spec *spec;
3305 int err;
3306
3307 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003308 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003309 if (spec == NULL)
3310 return -ENOMEM;
3311
Takashi Iwai620e2b22011-06-17 17:19:19 +02003312 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003313 override_mic_boost(codec, 0x2b, 0, 3, 40);
3314 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003315 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003316
Lydia Wangeb7188c2009-10-10 19:08:34 +08003317 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003318 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003319 if (err < 0) {
3320 via_free(codec);
3321 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003322 }
3323
Takashi Iwai096a8852011-06-20 12:09:02 +02003324 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003325
Lydia Wangeb7188c2009-10-10 19:08:34 +08003326 codec->patch_ops = via_patch_ops;
3327
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003328 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3329
Lydia Wangeb7188c2009-10-10 19:08:34 +08003330 return 0;
3331}
Lydia Wangf3db4232009-10-10 19:08:41 +08003332
3333/* Patch for VT1716S */
3334
3335static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3336 struct snd_ctl_elem_info *uinfo)
3337{
3338 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3339 uinfo->count = 1;
3340 uinfo->value.integer.min = 0;
3341 uinfo->value.integer.max = 1;
3342 return 0;
3343}
3344
3345static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3346 struct snd_ctl_elem_value *ucontrol)
3347{
3348 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3349 int index = 0;
3350
3351 index = snd_hda_codec_read(codec, 0x26, 0,
3352 AC_VERB_GET_CONNECT_SEL, 0);
3353 if (index != -1)
3354 *ucontrol->value.integer.value = index;
3355
3356 return 0;
3357}
3358
3359static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3360 struct snd_ctl_elem_value *ucontrol)
3361{
3362 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3363 struct via_spec *spec = codec->spec;
3364 int index = *ucontrol->value.integer.value;
3365
3366 snd_hda_codec_write(codec, 0x26, 0,
3367 AC_VERB_SET_CONNECT_SEL, index);
3368 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003369 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003370 return 1;
3371}
3372
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003373static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003374 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3375 {
3376 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3377 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003378 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003379 .count = 1,
3380 .info = vt1716s_dmic_info,
3381 .get = vt1716s_dmic_get,
3382 .put = vt1716s_dmic_put,
3383 },
3384 {} /* end */
3385};
3386
3387
3388/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003389static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003390 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3391 { } /* end */
3392};
3393
Takashi Iwai096a8852011-06-20 12:09:02 +02003394static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003395 /* Enable Boost Volume backdoor */
3396 {0x1, 0xf8a, 0x80},
3397 /* don't bybass mixer */
3398 {0x1, 0xf88, 0xc0},
3399 /* Enable mono output */
3400 {0x1, 0xf90, 0x08},
3401 { }
3402};
3403
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003404static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3405{
3406 struct via_spec *spec = codec->spec;
3407 int imux_is_smixer;
3408 unsigned int parm;
3409 unsigned int mono_out, present;
3410 /* SW0 (17h) = stereo mixer */
3411 imux_is_smixer =
3412 (snd_hda_codec_read(codec, 0x17, 0,
3413 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3414 /* inputs */
3415 /* PW 1/2/5 (1ah/1bh/1eh) */
3416 parm = AC_PWRST_D3;
3417 set_pin_power_state(codec, 0x1a, &parm);
3418 set_pin_power_state(codec, 0x1b, &parm);
3419 set_pin_power_state(codec, 0x1e, &parm);
3420 if (imux_is_smixer)
3421 parm = AC_PWRST_D0;
3422 /* SW0 (17h), AIW0(13h) */
3423 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3424 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3425
3426 parm = AC_PWRST_D3;
3427 set_pin_power_state(codec, 0x1e, &parm);
3428 /* PW11 (22h) */
3429 if (spec->dmic_enabled)
3430 set_pin_power_state(codec, 0x22, &parm);
3431 else
3432 snd_hda_codec_write(codec, 0x22, 0,
3433 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3434
3435 /* SW2(26h), AIW1(14h) */
3436 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3437 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3438
3439 /* outputs */
3440 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3441 parm = AC_PWRST_D3;
3442 set_pin_power_state(codec, 0x19, &parm);
3443 /* Smart 5.1 PW2(1bh) */
3444 if (spec->smart51_enabled)
3445 set_pin_power_state(codec, 0x1b, &parm);
3446 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3447 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3448
3449 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3450 parm = AC_PWRST_D3;
3451 set_pin_power_state(codec, 0x23, &parm);
3452 /* Smart 5.1 PW1(1ah) */
3453 if (spec->smart51_enabled)
3454 set_pin_power_state(codec, 0x1a, &parm);
3455 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3456
3457 /* Smart 5.1 PW5(1eh) */
3458 if (spec->smart51_enabled)
3459 set_pin_power_state(codec, 0x1e, &parm);
3460 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3461
3462 /* Mono out */
3463 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3464 present = snd_hda_jack_detect(codec, 0x1c);
3465
3466 if (present)
3467 mono_out = 0;
3468 else {
3469 present = snd_hda_jack_detect(codec, 0x1d);
3470 if (!spec->hp_independent_mode && present)
3471 mono_out = 0;
3472 else
3473 mono_out = 1;
3474 }
3475 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3476 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3477 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3478 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3479
3480 /* PW 3/4 (1ch/1dh) */
3481 parm = AC_PWRST_D3;
3482 set_pin_power_state(codec, 0x1c, &parm);
3483 set_pin_power_state(codec, 0x1d, &parm);
3484 /* HP Independent Mode, power on AOW3 */
3485 if (spec->hp_independent_mode)
3486 snd_hda_codec_write(codec, 0x25, 0,
3487 AC_VERB_SET_POWER_STATE, parm);
3488
3489 /* force to D0 for internal Speaker */
3490 /* MW0 (16h), AOW0 (10h) */
3491 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3492 imux_is_smixer ? AC_PWRST_D0 : parm);
3493 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3494 mono_out ? AC_PWRST_D0 : parm);
3495}
3496
Lydia Wangf3db4232009-10-10 19:08:41 +08003497static int patch_vt1716S(struct hda_codec *codec)
3498{
3499 struct via_spec *spec;
3500 int err;
3501
3502 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003503 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003504 if (spec == NULL)
3505 return -ENOMEM;
3506
Takashi Iwai620e2b22011-06-17 17:19:19 +02003507 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003508 override_mic_boost(codec, 0x1a, 0, 3, 40);
3509 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003510
Lydia Wangf3db4232009-10-10 19:08:41 +08003511 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003512 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003513 if (err < 0) {
3514 via_free(codec);
3515 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003516 }
3517
Takashi Iwai096a8852011-06-20 12:09:02 +02003518 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003519
Lydia Wangf3db4232009-10-10 19:08:41 +08003520 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3521 spec->num_mixers++;
3522
3523 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3524
3525 codec->patch_ops = via_patch_ops;
3526
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003527 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003528 return 0;
3529}
Lydia Wang25eaba22009-10-10 19:08:43 +08003530
3531/* for vt2002P */
3532
Takashi Iwai096a8852011-06-20 12:09:02 +02003533static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003534 /* Class-D speaker related verbs */
3535 {0x1, 0xfe0, 0x4},
3536 {0x1, 0xfe9, 0x80},
3537 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003538 /* Enable Boost Volume backdoor */
3539 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003540 /* Enable AOW0 to MW9 */
3541 {0x1, 0xfb8, 0x88},
3542 { }
3543};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003544
Takashi Iwai096a8852011-06-20 12:09:02 +02003545static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003546 /* Enable Boost Volume backdoor */
3547 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003548 /* Enable AOW0 to MW9 */
3549 {0x1, 0xfb8, 0x88},
3550 { }
3551};
Lydia Wang25eaba22009-10-10 19:08:43 +08003552
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003553static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3554{
3555 struct via_spec *spec = codec->spec;
3556 int imux_is_smixer;
3557 unsigned int parm;
3558 unsigned int present;
3559 /* MUX9 (1eh) = stereo mixer */
3560 imux_is_smixer =
3561 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3562 /* inputs */
3563 /* PW 5/6/7 (29h/2ah/2bh) */
3564 parm = AC_PWRST_D3;
3565 set_pin_power_state(codec, 0x29, &parm);
3566 set_pin_power_state(codec, 0x2a, &parm);
3567 set_pin_power_state(codec, 0x2b, &parm);
3568 parm = AC_PWRST_D0;
3569 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3570 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3571 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3572 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3573 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3574
3575 /* outputs */
3576 /* AOW0 (8h)*/
3577 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3578
Lydia Wang118909562011-03-23 17:57:34 +08003579 if (spec->codec_type == VT1802) {
3580 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3581 parm = AC_PWRST_D3;
3582 set_pin_power_state(codec, 0x28, &parm);
3583 snd_hda_codec_write(codec, 0x18, 0,
3584 AC_VERB_SET_POWER_STATE, parm);
3585 snd_hda_codec_write(codec, 0x38, 0,
3586 AC_VERB_SET_POWER_STATE, parm);
3587 } else {
3588 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3589 parm = AC_PWRST_D3;
3590 set_pin_power_state(codec, 0x26, &parm);
3591 snd_hda_codec_write(codec, 0x1c, 0,
3592 AC_VERB_SET_POWER_STATE, parm);
3593 snd_hda_codec_write(codec, 0x37, 0,
3594 AC_VERB_SET_POWER_STATE, parm);
3595 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003596
Lydia Wang118909562011-03-23 17:57:34 +08003597 if (spec->codec_type == VT1802) {
3598 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3599 parm = AC_PWRST_D3;
3600 set_pin_power_state(codec, 0x25, &parm);
3601 snd_hda_codec_write(codec, 0x15, 0,
3602 AC_VERB_SET_POWER_STATE, parm);
3603 snd_hda_codec_write(codec, 0x35, 0,
3604 AC_VERB_SET_POWER_STATE, parm);
3605 } else {
3606 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3607 parm = AC_PWRST_D3;
3608 set_pin_power_state(codec, 0x25, &parm);
3609 snd_hda_codec_write(codec, 0x19, 0,
3610 AC_VERB_SET_POWER_STATE, parm);
3611 snd_hda_codec_write(codec, 0x35, 0,
3612 AC_VERB_SET_POWER_STATE, parm);
3613 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003614
3615 if (spec->hp_independent_mode)
3616 snd_hda_codec_write(codec, 0x9, 0,
3617 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3618
3619 /* Class-D */
3620 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3621 present = snd_hda_jack_detect(codec, 0x25);
3622
3623 parm = AC_PWRST_D3;
3624 set_pin_power_state(codec, 0x24, &parm);
3625 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003626 if (spec->codec_type == VT1802)
3627 snd_hda_codec_write(codec, 0x14, 0,
3628 AC_VERB_SET_POWER_STATE, parm);
3629 else
3630 snd_hda_codec_write(codec, 0x18, 0,
3631 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003632 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3633
3634 /* Mono Out */
3635 present = snd_hda_jack_detect(codec, 0x26);
3636
3637 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003638 if (spec->codec_type == VT1802) {
3639 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3640 snd_hda_codec_write(codec, 0x33, 0,
3641 AC_VERB_SET_POWER_STATE, parm);
3642 snd_hda_codec_write(codec, 0x1c, 0,
3643 AC_VERB_SET_POWER_STATE, parm);
3644 snd_hda_codec_write(codec, 0x3c, 0,
3645 AC_VERB_SET_POWER_STATE, parm);
3646 } else {
3647 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3648 snd_hda_codec_write(codec, 0x31, 0,
3649 AC_VERB_SET_POWER_STATE, parm);
3650 snd_hda_codec_write(codec, 0x17, 0,
3651 AC_VERB_SET_POWER_STATE, parm);
3652 snd_hda_codec_write(codec, 0x3b, 0,
3653 AC_VERB_SET_POWER_STATE, parm);
3654 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003655 /* MW9 (21h) */
3656 if (imux_is_smixer || !is_aa_path_mute(codec))
3657 snd_hda_codec_write(codec, 0x21, 0,
3658 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3659 else
3660 snd_hda_codec_write(codec, 0x21, 0,
3661 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3662}
Lydia Wang25eaba22009-10-10 19:08:43 +08003663
3664/* patch for vt2002P */
3665static int patch_vt2002P(struct hda_codec *codec)
3666{
3667 struct via_spec *spec;
3668 int err;
3669
3670 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003671 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003672 if (spec == NULL)
3673 return -ENOMEM;
3674
Takashi Iwai620e2b22011-06-17 17:19:19 +02003675 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003676 override_mic_boost(codec, 0x2b, 0, 3, 40);
3677 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003678 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003679
Lydia Wang25eaba22009-10-10 19:08:43 +08003680 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003681 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003682 if (err < 0) {
3683 via_free(codec);
3684 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003685 }
3686
Lydia Wang118909562011-03-23 17:57:34 +08003687 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003688 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003689 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003690 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003691
Lydia Wang25eaba22009-10-10 19:08:43 +08003692 codec->patch_ops = via_patch_ops;
3693
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003694 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003695 return 0;
3696}
Lydia Wangab6734e2009-10-10 19:08:46 +08003697
3698/* for vt1812 */
3699
Takashi Iwai096a8852011-06-20 12:09:02 +02003700static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003701 /* Enable Boost Volume backdoor */
3702 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003703 /* Enable AOW0 to MW9 */
3704 {0x1, 0xfb8, 0xa8},
3705 { }
3706};
3707
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003708static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3709{
3710 struct via_spec *spec = codec->spec;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003711 unsigned int parm;
3712 unsigned int present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003713 /* inputs */
3714 /* PW 5/6/7 (29h/2ah/2bh) */
3715 parm = AC_PWRST_D3;
3716 set_pin_power_state(codec, 0x29, &parm);
3717 set_pin_power_state(codec, 0x2a, &parm);
3718 set_pin_power_state(codec, 0x2b, &parm);
3719 parm = AC_PWRST_D0;
3720 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3721 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3722 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3723 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3724 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3725
3726 /* outputs */
3727 /* AOW0 (8h)*/
3728 snd_hda_codec_write(codec, 0x8, 0,
3729 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3730
3731 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3732 parm = AC_PWRST_D3;
3733 set_pin_power_state(codec, 0x28, &parm);
3734 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3735 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3736
3737 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3738 parm = AC_PWRST_D3;
3739 set_pin_power_state(codec, 0x25, &parm);
3740 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3741 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3742 if (spec->hp_independent_mode)
3743 snd_hda_codec_write(codec, 0x9, 0,
3744 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3745
3746 /* Internal Speaker */
3747 /* PW0 (24h), MW0(14h), MUX0(34h) */
3748 present = snd_hda_jack_detect(codec, 0x25);
3749
3750 parm = AC_PWRST_D3;
3751 set_pin_power_state(codec, 0x24, &parm);
3752 if (present) {
3753 snd_hda_codec_write(codec, 0x14, 0,
3754 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3755 snd_hda_codec_write(codec, 0x34, 0,
3756 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3757 } else {
3758 snd_hda_codec_write(codec, 0x14, 0,
3759 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3760 snd_hda_codec_write(codec, 0x34, 0,
3761 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3762 }
3763
3764
3765 /* Mono Out */
3766 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3767 present = snd_hda_jack_detect(codec, 0x28);
3768
3769 parm = AC_PWRST_D3;
3770 set_pin_power_state(codec, 0x31, &parm);
3771 if (present) {
3772 snd_hda_codec_write(codec, 0x1c, 0,
3773 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3774 snd_hda_codec_write(codec, 0x3c, 0,
3775 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3776 snd_hda_codec_write(codec, 0x3e, 0,
3777 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3778 } else {
3779 snd_hda_codec_write(codec, 0x1c, 0,
3780 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3781 snd_hda_codec_write(codec, 0x3c, 0,
3782 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3783 snd_hda_codec_write(codec, 0x3e, 0,
3784 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3785 }
3786
3787 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3788 parm = AC_PWRST_D3;
3789 set_pin_power_state(codec, 0x33, &parm);
3790 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3791 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3792
3793}
Lydia Wangab6734e2009-10-10 19:08:46 +08003794
3795/* patch for vt1812 */
3796static int patch_vt1812(struct hda_codec *codec)
3797{
3798 struct via_spec *spec;
3799 int err;
3800
3801 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003802 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003803 if (spec == NULL)
3804 return -ENOMEM;
3805
Takashi Iwai620e2b22011-06-17 17:19:19 +02003806 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003807 override_mic_boost(codec, 0x2b, 0, 3, 40);
3808 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003809 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003810
Lydia Wangab6734e2009-10-10 19:08:46 +08003811 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003812 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003813 if (err < 0) {
3814 via_free(codec);
3815 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003816 }
3817
Takashi Iwai096a8852011-06-20 12:09:02 +02003818 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003819
Lydia Wangab6734e2009-10-10 19:08:46 +08003820 codec->patch_ops = via_patch_ops;
3821
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003822 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003823 return 0;
3824}
3825
Joseph Chanc577b8a2006-11-29 15:29:40 +01003826/*
3827 * patch entries
3828 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003829static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003830 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3831 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3832 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3833 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3834 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003835 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003836 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003837 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003838 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003839 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003840 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003841 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003842 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003843 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003844 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003845 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003846 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003847 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003848 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003849 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003850 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003851 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003852 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003853 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003854 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003855 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003856 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003857 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003858 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003859 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003860 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003861 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003862 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003863 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003864 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003865 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003866 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003867 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003868 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003869 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003870 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003871 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003872 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003873 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003874 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003875 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003876 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003877 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003878 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003879 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003880 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003881 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003882 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003883 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003884 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003885 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003886 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003887 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003888 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003889 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003890 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003891 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003892 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003893 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003894 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003895 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003896 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003897 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003898 { .id = 0x11060428, .name = "VT1718S",
3899 .patch = patch_vt1718S},
3900 { .id = 0x11064428, .name = "VT1718S",
3901 .patch = patch_vt1718S},
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08003902 { .id = 0x11060441, .name = "VT2020",
3903 .patch = patch_vt1718S},
3904 { .id = 0x11064441, .name = "VT1828S",
3905 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003906 { .id = 0x11060433, .name = "VT1716S",
3907 .patch = patch_vt1716S},
3908 { .id = 0x1106a721, .name = "VT1716S",
3909 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003910 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3911 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003912 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003913 { .id = 0x11060440, .name = "VT1818S",
3914 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003915 { .id = 0x11060446, .name = "VT1802",
3916 .patch = patch_vt2002P},
3917 { .id = 0x11068446, .name = "VT1802",
3918 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003919 {} /* terminator */
3920};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003921
3922MODULE_ALIAS("snd-hda-codec-id:1106*");
3923
3924static struct hda_codec_preset_list via_list = {
3925 .preset = snd_hda_preset_via,
3926 .owner = THIS_MODULE,
3927};
3928
3929MODULE_LICENSE("GPL");
3930MODULE_DESCRIPTION("VIA HD-audio codec");
3931
3932static int __init patch_via_init(void)
3933{
3934 return snd_hda_add_codec_preset(&via_list);
3935}
3936
3937static void __exit patch_via_exit(void)
3938{
3939 snd_hda_delete_codec_preset(&via_list);
3940}
3941
3942module_init(patch_via_init)
3943module_exit(patch_via_exit)