blob: 03e63fed9caf7d9e8342373901a5086f884122d9 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Paul Gortmakerda155d52011-07-15 12:38:28 -040052#include <linux/module.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010053#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080054#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010055#include "hda_codec.h"
56#include "hda_local.h"
Takashi Iwai1835a0f2011-10-27 22:12:46 +020057#include "hda_jack.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010058
Joseph Chanc577b8a2006-11-29 15:29:40 +010059/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080060#define VT1708_HP_PIN_NID 0x20
61#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010062
Harald Welted7426322008-09-15 22:43:23 +080063enum VIA_HDA_CODEC {
64 UNKNOWN = -1,
65 VT1708,
66 VT1709_10CH,
67 VT1709_6CH,
68 VT1708B_8CH,
69 VT1708B_4CH,
70 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080071 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080072 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080073 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080074 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080075 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080076 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080077 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080078 CODEC_TYPES,
79};
80
Lydia Wang118909562011-03-23 17:57:34 +080081#define VT2002P_COMPATIBLE(spec) \
82 ((spec)->codec_type == VT2002P ||\
83 (spec)->codec_type == VT1812 ||\
84 (spec)->codec_type == VT1802)
85
Takashi Iwai8e3679d2011-06-21 09:01:36 +020086#define MAX_NID_PATH_DEPTH 5
87
Takashi Iwai09a9ad692011-06-21 15:57:44 +020088/* output-path: DAC -> ... -> pin
89 * idx[] contains the source index number of the next widget;
90 * e.g. idx[0] is the index of the DAC selected by path[1] widget
91 * multi[] indicates whether it's a selector widget with multi-connectors
92 * (i.e. the connection selection is mandatory)
93 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
94 */
Takashi Iwai4a796162011-06-17 17:53:38 +020095struct nid_path {
96 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020097 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad692011-06-21 15:57:44 +020098 unsigned char idx[MAX_NID_PATH_DEPTH];
99 unsigned char multi[MAX_NID_PATH_DEPTH];
100 unsigned int vol_ctl;
101 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200102};
103
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200104/* input-path */
105struct via_input {
106 hda_nid_t pin; /* input-pin or aa-mix */
107 int adc_idx; /* ADC index to be used */
108 int mux_idx; /* MUX index (if any) */
109 const char *label; /* input-source label */
110};
111
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200112#define VIA_MAX_ADCS 3
113
Takashi Iwai3b607e32011-07-18 16:54:40 +0200114enum {
115 STREAM_MULTI_OUT = (1 << 0),
116 STREAM_INDEP_HP = (1 << 1),
117};
118
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800119struct via_spec {
120 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200121 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800122 unsigned int num_mixers;
123
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200124 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800125 unsigned int num_iverbs;
126
Takashi Iwai82673bc2011-06-17 16:24:21 +0200127 char stream_name_analog[32];
Takashi Iwai7eb56e842011-06-18 16:40:14 +0200128 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200129 const struct hda_pcm_stream *stream_analog_playback;
130 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800131
Takashi Iwai82673bc2011-06-17 16:24:21 +0200132 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200133 const struct hda_pcm_stream *stream_digital_playback;
134 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800135
136 /* playback */
137 struct hda_multi_out multiout;
138 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200139 hda_nid_t hp_dac_nid;
Takashi Iwai3214b962011-07-18 12:49:25 +0200140 hda_nid_t speaker_dac_nid;
141 int hp_indep_shared; /* indep HP-DAC is shared with side ch */
Takashi Iwai3b607e32011-07-18 16:54:40 +0200142 int opened_streams; /* STREAM_* bits */
143 int active_streams; /* STREAM_* bits */
Takashi Iwai3214b962011-07-18 12:49:25 +0200144 int aamix_mode; /* loopback is enabled for output-path? */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800145
Takashi Iwai3214b962011-07-18 12:49:25 +0200146 /* Output-paths:
147 * There are different output-paths depending on the setup.
148 * out_path, hp_path and speaker_path are primary paths. If both
149 * direct DAC and aa-loopback routes are available, these contain
150 * the former paths. Meanwhile *_mix_path contain the paths with
151 * loopback mixer. (Since the loopback is only for front channel,
152 * no out_mix_path for surround channels.)
153 * The HP output has another path, hp_indep_path, which is used in
154 * the independent-HP mode.
155 */
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200156 struct nid_path out_path[HDA_SIDE + 1];
Takashi Iwai3214b962011-07-18 12:49:25 +0200157 struct nid_path out_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200158 struct nid_path hp_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200159 struct nid_path hp_mix_path;
160 struct nid_path hp_indep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200161 struct nid_path speaker_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200162 struct nid_path speaker_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200163
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800164 /* capture */
165 unsigned int num_adc_nids;
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200166 hda_nid_t adc_nids[VIA_MAX_ADCS];
167 hda_nid_t mux_nids[VIA_MAX_ADCS];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200168 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800169 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800170
171 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200172 bool dyn_adc_switch;
173 int num_inputs;
174 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200175 unsigned int cur_mux[VIA_MAX_ADCS];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800176
Takashi Iwai3b607e32011-07-18 16:54:40 +0200177 /* dynamic DAC switching */
178 unsigned int cur_dac_stream_tag;
179 unsigned int cur_dac_format;
180 unsigned int cur_hp_stream_tag;
181 unsigned int cur_hp_format;
182
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200183 /* dynamic ADC switching */
184 hda_nid_t cur_adc;
185 unsigned int cur_adc_stream_tag;
186 unsigned int cur_adc_format;
187
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800188 /* PCM information */
189 struct hda_pcm pcm_rec[3];
190
191 /* dynamic controls, init_verbs and input_mux */
192 struct auto_pin_cfg autocfg;
193 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800194 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
195
196 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800197 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800198 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200199 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800200 enum VIA_HDA_CODEC codec_type;
201
Takashi 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 Iwai187d3332011-11-24 16:33:09 +0100212 int hp_work_active;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200213 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800214 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800215
216 void (*set_widgets_power_state)(struct hda_codec *codec);
217
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800218 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200219 int num_loopbacks;
220 struct hda_amp_list loopback_list[8];
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200221
222 /* bind capture-volume */
223 struct hda_bind_ctls *bind_cap_vol;
224 struct hda_bind_ctls *bind_cap_sw;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200225
226 struct mutex config_mutex;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800227};
228
Lydia Wang0341ccd2011-03-22 16:25:03 +0800229static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100230static struct via_spec * via_new_spec(struct hda_codec *codec)
231{
232 struct via_spec *spec;
233
234 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
235 if (spec == NULL)
236 return NULL;
237
Takashi Iwai3b607e32011-07-18 16:54:40 +0200238 mutex_init(&spec->config_mutex);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100239 codec->spec = spec;
240 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800241 spec->codec_type = get_codec_type(codec);
242 /* VT1708BCE & VT1708S are almost same */
243 if (spec->codec_type == VT1708BCE)
244 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100245 return spec;
246}
247
Lydia Wang744ff5f2009-10-10 19:07:26 +0800248static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800249{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800250 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800251 u16 ven_id = vendor_id >> 16;
252 u16 dev_id = vendor_id & 0xffff;
253 enum VIA_HDA_CODEC codec_type;
254
255 /* get codec type */
256 if (ven_id != 0x1106)
257 codec_type = UNKNOWN;
258 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
259 codec_type = VT1708;
260 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
261 codec_type = VT1709_10CH;
262 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
263 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800264 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800265 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800266 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
267 codec_type = VT1708BCE;
268 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800269 codec_type = VT1708B_4CH;
270 else if ((dev_id & 0xfff) == 0x397
271 && (dev_id >> 12) < 8)
272 codec_type = VT1708S;
273 else if ((dev_id & 0xfff) == 0x398
274 && (dev_id >> 12) < 8)
275 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800276 else if ((dev_id & 0xfff) == 0x428
277 && (dev_id >> 12) < 8)
278 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800279 else if (dev_id == 0x0433 || dev_id == 0xa721)
280 codec_type = VT1716S;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +0800281 else if (dev_id == 0x0441 || dev_id == 0x4441)
282 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800283 else if (dev_id == 0x0438 || dev_id == 0x4438)
284 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800285 else if (dev_id == 0x0448)
286 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800287 else if (dev_id == 0x0440)
288 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800289 else if ((dev_id & 0xfff) == 0x446)
290 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800291 else
292 codec_type = UNKNOWN;
293 return codec_type;
294};
295
Lydia Wangec7e7e42011-03-24 12:43:44 +0800296#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800297#define VIA_HP_EVENT 0x01
298#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200299#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800300
Joseph Chanc577b8a2006-11-29 15:29:40 +0100301enum {
302 VIA_CTL_WIDGET_VOL,
303 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800304 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100305};
306
Takashi Iwaiada509e2011-06-20 15:40:19 +0200307static void analog_low_current_mode(struct hda_codec *codec);
308static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800309
Takashi Iwai187d3332011-11-24 16:33:09 +0100310#define hp_detect_with_aa(codec) \
311 (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1 && \
312 !is_aa_path_mute(codec))
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800313
314static void vt1708_stop_hp_work(struct via_spec *spec)
315{
316 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
317 return;
Takashi Iwai187d3332011-11-24 16:33:09 +0100318 if (spec->hp_work_active) {
319 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 1);
320 cancel_delayed_work_sync(&spec->vt1708_hp_work);
321 spec->hp_work_active = 0;
322 }
323}
324
325static void vt1708_update_hp_work(struct via_spec *spec)
326{
327 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800328 return;
Takashi Iwai187d3332011-11-24 16:33:09 +0100329 if (spec->vt1708_jack_detect &&
330 (spec->active_streams || hp_detect_with_aa(spec->codec))) {
331 if (!spec->hp_work_active) {
332 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, 0);
333 schedule_delayed_work(&spec->vt1708_hp_work,
334 msecs_to_jiffies(100));
335 spec->hp_work_active = 1;
336 }
337 } else if (!hp_detect_with_aa(spec->codec))
338 vt1708_stop_hp_work(spec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800339}
Lydia Wangf5271102009-10-10 19:07:35 +0800340
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800341static void set_widgets_power_state(struct hda_codec *codec)
342{
343 struct via_spec *spec = codec->spec;
344 if (spec->set_widgets_power_state)
345 spec->set_widgets_power_state(codec);
346}
Lydia Wang25eaba22009-10-10 19:08:43 +0800347
Lydia Wangf5271102009-10-10 19:07:35 +0800348static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
349 struct snd_ctl_elem_value *ucontrol)
350{
351 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
352 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
353
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800354 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200355 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Takashi Iwai187d3332011-11-24 16:33:09 +0100356 vt1708_update_hp_work(codec->spec);
Lydia Wangf5271102009-10-10 19:07:35 +0800357 return change;
358}
359
360/* modify .put = snd_hda_mixer_amp_switch_put */
361#define ANALOG_INPUT_MUTE \
362 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
363 .name = NULL, \
364 .index = 0, \
365 .info = snd_hda_mixer_amp_switch_info, \
366 .get = snd_hda_mixer_amp_switch_get, \
367 .put = analog_input_switch_put, \
368 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
369
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200370static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100371 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
372 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800373 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100374};
375
Lydia Wangab6734e2009-10-10 19:08:46 +0800376
Joseph Chanc577b8a2006-11-29 15:29:40 +0100377/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200378static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
379 const struct snd_kcontrol_new *tmpl,
380 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100381{
382 struct snd_kcontrol_new *knew;
383
Takashi Iwai603c4012008-07-30 15:01:44 +0200384 snd_array_init(&spec->kctls, sizeof(*knew), 32);
385 knew = snd_array_new(&spec->kctls);
386 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200387 return NULL;
388 *knew = *tmpl;
389 if (!name)
390 name = tmpl->name;
391 if (name) {
392 knew->name = kstrdup(name, GFP_KERNEL);
393 if (!knew->name)
394 return NULL;
395 }
396 return knew;
397}
398
399static int __via_add_control(struct via_spec *spec, int type, const char *name,
400 int idx, unsigned long val)
401{
402 struct snd_kcontrol_new *knew;
403
404 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
405 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100406 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200407 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100408 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100409 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100410 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100411 return 0;
412}
413
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200414#define via_add_control(spec, type, name, val) \
415 __via_add_control(spec, type, name, 0, val)
416
Takashi Iwai291c9e32011-06-17 16:15:26 +0200417#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100418
Takashi Iwai603c4012008-07-30 15:01:44 +0200419static void via_free_kctls(struct hda_codec *codec)
420{
421 struct via_spec *spec = codec->spec;
422
423 if (spec->kctls.list) {
424 struct snd_kcontrol_new *kctl = spec->kctls.list;
425 int i;
426 for (i = 0; i < spec->kctls.used; i++)
427 kfree(kctl[i].name);
428 }
429 snd_array_free(&spec->kctls);
430}
431
Joseph Chanc577b8a2006-11-29 15:29:40 +0100432/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800433static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200434 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100435{
436 char name[32];
437 int err;
438
439 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200440 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100441 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
442 if (err < 0)
443 return err;
444 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200445 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100446 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
447 if (err < 0)
448 return err;
449 return 0;
450}
451
Takashi Iwai5d417622011-06-20 11:32:27 +0200452#define get_connection_index(codec, mux, nid) \
Takashi Iwai8d087c72011-06-28 12:45:47 +0200453 snd_hda_get_conn_index(codec, mux, nid, 0)
Takashi Iwai5d417622011-06-20 11:32:27 +0200454
Takashi Iwai8df2a312011-06-21 11:48:29 +0200455static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
456 unsigned int mask)
457{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200458 unsigned int caps;
459 if (!nid)
460 return false;
461 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200462 if (dir == HDA_INPUT)
463 caps &= AC_WCAP_IN_AMP;
464 else
465 caps &= AC_WCAP_OUT_AMP;
466 if (!caps)
467 return false;
468 if (query_amp_caps(codec, nid, dir) & mask)
469 return true;
470 return false;
471}
472
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200473#define have_mute(codec, nid, dir) \
474 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200475
Lydia Wangd69607b32011-07-08 14:02:52 +0800476/* enable/disable the output-route mixers */
477static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
Takashi Iwai3214b962011-07-18 12:49:25 +0200478 hda_nid_t mix_nid, int idx, bool enable)
Lydia Wangd69607b32011-07-08 14:02:52 +0800479{
480 int i, num, val;
Lydia Wangd69607b32011-07-08 14:02:52 +0800481
482 if (!path)
483 return;
484 num = snd_hda_get_conn_list(codec, mix_nid, NULL);
Lydia Wangd69607b32011-07-08 14:02:52 +0800485 for (i = 0; i < num; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +0200486 if (i == idx)
487 val = AMP_IN_UNMUTE(i);
488 else
489 val = AMP_IN_MUTE(i);
Lydia Wangd69607b32011-07-08 14:02:52 +0800490 snd_hda_codec_write(codec, mix_nid, 0,
491 AC_VERB_SET_AMP_GAIN_MUTE, val);
492 }
493}
494
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200495/* enable/disable the output-route */
496static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
497 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200498{
Lydia Wangd69607b32011-07-08 14:02:52 +0800499 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200500 int i;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200501 for (i = 0; i < path->depth; i++) {
502 hda_nid_t src, dst;
503 int idx = path->idx[i];
504 src = path->path[i];
505 if (i < path->depth - 1)
506 dst = path->path[i + 1];
507 else
508 dst = 0;
509 if (enable && path->multi[i])
510 snd_hda_codec_write(codec, dst, 0,
511 AC_VERB_SET_CONNECT_SEL, idx);
Takashi Iwai3214b962011-07-18 12:49:25 +0200512 if (!force && (dst == spec->aa_mix_nid))
Lydia Wange5e14682011-07-01 10:55:07 +0800513 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +0200514 if (have_mute(codec, dst, HDA_INPUT))
515 activate_output_mix(codec, path, dst, idx, enable);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200516 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
517 continue;
518 if (have_mute(codec, src, HDA_OUTPUT)) {
519 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
520 snd_hda_codec_write(codec, src, 0,
521 AC_VERB_SET_AMP_GAIN_MUTE, val);
522 }
523 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200524}
525
526/* set the given pin as output */
527static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
528 int pin_type)
529{
530 if (!pin)
531 return;
532 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
533 pin_type);
534 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
535 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200536 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100537}
538
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200539static void via_auto_init_output(struct hda_codec *codec,
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200540 struct nid_path *path, int pin_type)
Takashi Iwai5d417622011-06-20 11:32:27 +0200541{
Takashi Iwai5d417622011-06-20 11:32:27 +0200542 unsigned int caps;
Lydia Wangd69607b32011-07-08 14:02:52 +0800543 hda_nid_t pin;
Takashi Iwai5d417622011-06-20 11:32:27 +0200544
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200545 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200546 return;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200547 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200548
549 init_output_pin(codec, pin, pin_type);
550 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
551 if (caps & AC_AMPCAP_MUTE) {
552 unsigned int val;
553 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
554 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
555 AMP_OUT_MUTE | val);
556 }
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200557 activate_output_path(codec, path, true, true); /* force on */
Takashi Iwai5d417622011-06-20 11:32:27 +0200558}
559
Joseph Chanc577b8a2006-11-29 15:29:40 +0100560static void via_auto_init_multi_out(struct hda_codec *codec)
561{
562 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200563 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100564 int i;
565
Takashi Iwai3214b962011-07-18 12:49:25 +0200566 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) {
567 path = &spec->out_path[i];
568 if (!i && spec->aamix_mode && spec->out_mix_path.depth)
569 path = &spec->out_mix_path;
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200570 via_auto_init_output(codec, path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200571 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100572}
573
Takashi Iwai020066d2011-07-21 13:45:56 +0200574/* deactivate the inactive headphone-paths */
575static void deactivate_hp_paths(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100576{
577 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200578 int shared = spec->hp_indep_shared;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100579
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200580 if (spec->hp_independent_mode) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200581 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200582 activate_output_path(codec, &spec->hp_mix_path, false, false);
583 if (shared)
584 activate_output_path(codec, &spec->out_path[shared],
585 false, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200586 } else if (spec->aamix_mode || !spec->hp_path.depth) {
587 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200588 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200589 } else {
Takashi Iwai020066d2011-07-21 13:45:56 +0200590 activate_output_path(codec, &spec->hp_indep_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200591 activate_output_path(codec, &spec->hp_mix_path, false, false);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200592 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100593}
594
Takashi Iwai020066d2011-07-21 13:45:56 +0200595static void via_auto_init_hp_out(struct hda_codec *codec)
596{
597 struct via_spec *spec = codec->spec;
598
599 if (!spec->hp_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200600 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200601 return;
602 }
603 deactivate_hp_paths(codec);
604 if (spec->hp_independent_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200605 via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200606 else if (spec->aamix_mode)
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200607 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200608 else
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200609 via_auto_init_output(codec, &spec->hp_path, PIN_HP);
Takashi Iwai020066d2011-07-21 13:45:56 +0200610}
611
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200612static void via_auto_init_speaker_out(struct hda_codec *codec)
613{
614 struct via_spec *spec = codec->spec;
615
Takashi Iwai3214b962011-07-18 12:49:25 +0200616 if (!spec->autocfg.speaker_outs)
617 return;
618 if (!spec->speaker_path.depth) {
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200619 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200620 return;
621 }
622 if (!spec->aamix_mode) {
623 activate_output_path(codec, &spec->speaker_mix_path,
624 false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200625 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200626 } else {
627 activate_output_path(codec, &spec->speaker_path, false, false);
Takashi Iwaia353fbb2011-07-21 14:23:35 +0200628 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT);
Takashi Iwai3214b962011-07-18 12:49:25 +0200629 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200630}
631
Takashi Iwaif4a78282011-06-17 18:46:48 +0200632static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200633static void via_hp_automute(struct hda_codec *codec);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200634
Joseph Chanc577b8a2006-11-29 15:29:40 +0100635static void via_auto_init_analog_input(struct hda_codec *codec)
636{
637 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200638 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200639 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200640 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200641 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100642
Takashi Iwai096a8852011-06-20 12:09:02 +0200643 /* init ADCs */
644 for (i = 0; i < spec->num_adc_nids; i++) {
645 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
646 AC_VERB_SET_AMP_GAIN_MUTE,
647 AMP_IN_UNMUTE(0));
648 }
649
650 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200651 for (i = 0; i < cfg->num_inputs; i++) {
652 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200653 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200654 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100655 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200656 ctl = PIN_VREF50;
657 else
658 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100659 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200660 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100661 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200662
663 /* init input-src */
664 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200665 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
666 if (spec->mux_nids[adc_idx]) {
667 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
668 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
669 AC_VERB_SET_CONNECT_SEL,
670 mux_idx);
671 }
672 if (spec->dyn_adc_switch)
673 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200674 }
675
676 /* init aa-mixer */
677 if (!spec->aa_mix_nid)
678 return;
679 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
680 ARRAY_SIZE(conn));
681 for (i = 0; i < num_conns; i++) {
682 unsigned int caps = get_wcaps(codec, conn[i]);
683 if (get_wcaps_type(caps) == AC_WID_PIN)
684 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
685 AC_VERB_SET_AMP_GAIN_MUTE,
686 AMP_IN_MUTE(i));
687 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100688}
Lydia Wangf5271102009-10-10 19:07:35 +0800689
690static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
691 unsigned int *affected_parm)
692{
693 unsigned parm;
694 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
695 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
696 >> AC_DEFCFG_MISC_SHIFT
697 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800698 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200699 unsigned present = 0;
700
701 no_presence |= spec->no_pin_power_ctl;
702 if (!no_presence)
703 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200704 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800705 || ((no_presence || present)
706 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800707 *affected_parm = AC_PWRST_D0; /* if it's connected */
708 parm = AC_PWRST_D0;
709 } else
710 parm = AC_PWRST_D3;
711
712 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
713}
714
Takashi Iwai24088a52011-06-17 16:59:21 +0200715static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
716 struct snd_ctl_elem_info *uinfo)
717{
718 static const char * const texts[] = {
719 "Disabled", "Enabled"
720 };
721
722 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
723 uinfo->count = 1;
724 uinfo->value.enumerated.items = 2;
725 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
726 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
727 strcpy(uinfo->value.enumerated.name,
728 texts[uinfo->value.enumerated.item]);
729 return 0;
730}
731
732static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
733 struct snd_ctl_elem_value *ucontrol)
734{
735 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
736 struct via_spec *spec = codec->spec;
737 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
738 return 0;
739}
740
741static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
742 struct snd_ctl_elem_value *ucontrol)
743{
744 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
745 struct via_spec *spec = codec->spec;
746 unsigned int val = !ucontrol->value.enumerated.item[0];
747
748 if (val == spec->no_pin_power_ctl)
749 return 0;
750 spec->no_pin_power_ctl = val;
751 set_widgets_power_state(codec);
752 return 1;
753}
754
755static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
756 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
757 .name = "Dynamic Power-Control",
758 .info = via_pin_power_ctl_info,
759 .get = via_pin_power_ctl_get,
760 .put = via_pin_power_ctl_put,
761};
762
763
Harald Welte0aa62ae2008-09-09 15:58:27 +0800764static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
765 struct snd_ctl_elem_info *uinfo)
766{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200767 static const char * const texts[] = { "OFF", "ON" };
768
769 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
770 uinfo->count = 1;
771 uinfo->value.enumerated.items = 2;
772 if (uinfo->value.enumerated.item >= 2)
773 uinfo->value.enumerated.item = 1;
774 strcpy(uinfo->value.enumerated.name,
775 texts[uinfo->value.enumerated.item]);
776 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800777}
778
779static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
780 struct snd_ctl_elem_value *ucontrol)
781{
782 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800783 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800784
Takashi Iwaiece8d042011-06-19 16:24:21 +0200785 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800786 return 0;
787}
788
Takashi Iwai3b607e32011-07-18 16:54:40 +0200789/* adjust spec->multiout setup according to the current flags */
790static void setup_playback_multi_pcm(struct via_spec *spec)
791{
792 const struct auto_pin_cfg *cfg = &spec->autocfg;
793 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
794 spec->multiout.hp_nid = 0;
795 if (!spec->hp_independent_mode) {
796 if (!spec->hp_indep_shared)
797 spec->multiout.hp_nid = spec->hp_dac_nid;
798 } else {
799 if (spec->hp_indep_shared)
800 spec->multiout.num_dacs = cfg->line_outs - 1;
801 }
802}
803
804/* update DAC setups according to indep-HP switch;
805 * this function is called only when indep-HP is modified
806 */
807static void switch_indep_hp_dacs(struct hda_codec *codec)
808{
809 struct via_spec *spec = codec->spec;
810 int shared = spec->hp_indep_shared;
811 hda_nid_t shared_dac, hp_dac;
812
813 if (!spec->opened_streams)
814 return;
815
816 shared_dac = shared ? spec->multiout.dac_nids[shared] : 0;
817 hp_dac = spec->hp_dac_nid;
818 if (spec->hp_independent_mode) {
819 /* switch to indep-HP mode */
820 if (spec->active_streams & STREAM_MULTI_OUT) {
821 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
822 __snd_hda_codec_cleanup_stream(codec, shared_dac, 1);
823 }
824 if (spec->active_streams & STREAM_INDEP_HP)
825 snd_hda_codec_setup_stream(codec, hp_dac,
826 spec->cur_hp_stream_tag, 0,
827 spec->cur_hp_format);
828 } else {
829 /* back to HP or shared-DAC */
830 if (spec->active_streams & STREAM_INDEP_HP)
831 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
832 if (spec->active_streams & STREAM_MULTI_OUT) {
833 hda_nid_t dac;
834 int ch;
835 if (shared_dac) { /* reset mutli-ch DAC */
836 dac = shared_dac;
837 ch = shared * 2;
838 } else { /* reset HP DAC */
839 dac = hp_dac;
840 ch = 0;
841 }
842 snd_hda_codec_setup_stream(codec, dac,
843 spec->cur_dac_stream_tag, ch,
844 spec->cur_dac_format);
845 }
846 }
847 setup_playback_multi_pcm(spec);
848}
849
Harald Welte0aa62ae2008-09-09 15:58:27 +0800850static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
851 struct snd_ctl_elem_value *ucontrol)
852{
853 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
854 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200855 int cur, shared;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200856
Takashi Iwai3b607e32011-07-18 16:54:40 +0200857 mutex_lock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200858 cur = !!ucontrol->value.enumerated.item[0];
Takashi Iwai3b607e32011-07-18 16:54:40 +0200859 if (spec->hp_independent_mode == cur) {
860 mutex_unlock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200861 return 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200862 }
Takashi Iwai25250502011-06-30 17:24:47 +0200863 spec->hp_independent_mode = cur;
Takashi Iwai3214b962011-07-18 12:49:25 +0200864 shared = spec->hp_indep_shared;
Takashi Iwai020066d2011-07-21 13:45:56 +0200865 deactivate_hp_paths(codec);
866 if (cur)
867 activate_output_path(codec, &spec->hp_indep_path, true, false);
868 else {
Takashi Iwai3214b962011-07-18 12:49:25 +0200869 if (shared)
870 activate_output_path(codec, &spec->out_path[shared],
Takashi Iwai25250502011-06-30 17:24:47 +0200871 true, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200872 if (spec->aamix_mode || !spec->hp_path.depth)
873 activate_output_path(codec, &spec->hp_mix_path,
874 true, false);
875 else
876 activate_output_path(codec, &spec->hp_path,
877 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200878 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800879
Takashi Iwai3b607e32011-07-18 16:54:40 +0200880 switch_indep_hp_dacs(codec);
881 mutex_unlock(&spec->config_mutex);
882
Lydia Wangce0e5a92011-03-22 16:22:37 +0800883 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800884 set_widgets_power_state(codec);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200885 via_hp_automute(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200886 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800887}
888
Takashi Iwaiece8d042011-06-19 16:24:21 +0200889static const struct snd_kcontrol_new via_hp_mixer = {
890 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
891 .name = "Independent HP",
892 .info = via_independent_hp_info,
893 .get = via_independent_hp_get,
894 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800895};
896
Takashi Iwai3d83e572010-04-14 14:36:23 +0200897static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100898{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200899 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100900 struct snd_kcontrol_new *knew;
901 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100902
Takashi Iwaiece8d042011-06-19 16:24:21 +0200903 nid = spec->autocfg.hp_pins[0];
904 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200905 if (knew == NULL)
906 return -ENOMEM;
907
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100908 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100909
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100910 return 0;
911}
912
Lydia Wang1564b282009-10-10 19:07:52 +0800913static void notify_aa_path_ctls(struct hda_codec *codec)
914{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200915 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800916 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800917
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200918 for (i = 0; i < spec->smart51_nums; i++) {
919 struct snd_kcontrol *ctl;
920 struct snd_ctl_elem_id id;
921 memset(&id, 0, sizeof(id));
922 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
923 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800924 ctl = snd_hda_find_mixer_ctl(codec, id.name);
925 if (ctl)
926 snd_ctl_notify(codec->bus->card,
927 SNDRV_CTL_EVENT_MASK_VALUE,
928 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800929 }
930}
931
932static void mute_aa_path(struct hda_codec *codec, int mute)
933{
934 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200935 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800936 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200937
Lydia Wang1564b282009-10-10 19:07:52 +0800938 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200939 for (i = 0; i < spec->smart51_nums; i++) {
940 if (spec->smart51_idxs[i] < 0)
941 continue;
942 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
943 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800944 HDA_AMP_MUTE, val);
945 }
946}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200947
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200948static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
949{
950 struct via_spec *spec = codec->spec;
951 int i;
952
953 for (i = 0; i < spec->smart51_nums; i++)
954 if (spec->smart51_pins[i] == pin)
955 return true;
956 return false;
957}
958
Lydia Wang1564b282009-10-10 19:07:52 +0800959static int via_smart51_get(struct snd_kcontrol *kcontrol,
960 struct snd_ctl_elem_value *ucontrol)
961{
962 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
963 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800964
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200965 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800966 return 0;
967}
968
969static int via_smart51_put(struct snd_kcontrol *kcontrol,
970 struct snd_ctl_elem_value *ucontrol)
971{
972 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
973 struct via_spec *spec = codec->spec;
974 int out_in = *ucontrol->value.integer.value
975 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800976 int i;
977
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200978 for (i = 0; i < spec->smart51_nums; i++) {
979 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200980 unsigned int parm;
981
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200982 parm = snd_hda_codec_read(codec, nid, 0,
983 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
984 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
985 parm |= out_in;
986 snd_hda_codec_write(codec, nid, 0,
987 AC_VERB_SET_PIN_WIDGET_CONTROL,
988 parm);
989 if (out_in == AC_PINCTL_OUT_EN) {
990 mute_aa_path(codec, 1);
991 notify_aa_path_ctls(codec);
992 }
Lydia Wang1564b282009-10-10 19:07:52 +0800993 }
994 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800995 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800996 return 1;
997}
998
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200999static const struct snd_kcontrol_new via_smart51_mixer = {
1000 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1001 .name = "Smart 5.1",
1002 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001003 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +02001004 .get = via_smart51_get,
1005 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +08001006};
1007
Takashi Iwaif4a78282011-06-17 18:46:48 +02001008static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001009{
Takashi Iwaif4a78282011-06-17 18:46:48 +02001010 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001011
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001012 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +08001013 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001014 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001015 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001016 return 0;
1017}
1018
Takashi Iwaiada509e2011-06-20 15:40:19 +02001019/* check AA path's mute status */
1020static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001021{
Lydia Wangf5271102009-10-10 19:07:35 +08001022 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001023 const struct hda_amp_list *p;
1024 int i, ch, v;
1025
1026 for (i = 0; i < spec->num_loopbacks; i++) {
1027 p = &spec->loopback_list[i];
1028 for (ch = 0; ch < 2; ch++) {
1029 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
1030 p->idx);
1031 if (!(v & HDA_AMP_MUTE) && v > 0)
1032 return false;
Lydia Wangf5271102009-10-10 19:07:35 +08001033 }
1034 }
Takashi Iwaiada509e2011-06-20 15:40:19 +02001035 return true;
Lydia Wangf5271102009-10-10 19:07:35 +08001036}
1037
1038/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +02001039static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001040{
1041 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001042 bool enable;
1043 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +08001044
Takashi Iwai3b607e32011-07-18 16:54:40 +02001045 enable = is_aa_path_mute(codec) && (spec->opened_streams != 0);
Lydia Wangf5271102009-10-10 19:07:35 +08001046
1047 /* decide low current mode's verb & parameter */
1048 switch (spec->codec_type) {
1049 case VT1708B_8CH:
1050 case VT1708B_4CH:
1051 verb = 0xf70;
1052 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1053 break;
1054 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001055 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001056 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001057 verb = 0xf73;
1058 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1059 break;
1060 case VT1702:
1061 verb = 0xf73;
1062 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1063 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001064 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001065 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +08001066 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +08001067 verb = 0xf93;
1068 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1069 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001070 default:
1071 return; /* other codecs are not supported */
1072 }
1073 /* send verb */
1074 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1075}
1076
Joseph Chanc577b8a2006-11-29 15:29:40 +01001077/*
1078 * generic initialization of ADC, input mixers and output mixers
1079 */
Takashi Iwai096a8852011-06-20 12:09:02 +02001080static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001081 /* power down jack detect function */
1082 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001083 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001084};
1085
Takashi Iwai3b607e32011-07-18 16:54:40 +02001086static void set_stream_open(struct hda_codec *codec, int bit, bool active)
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001087{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001088 struct via_spec *spec = codec->spec;
1089
1090 if (active)
Takashi Iwai3b607e32011-07-18 16:54:40 +02001091 spec->opened_streams |= bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001092 else
Takashi Iwai3b607e32011-07-18 16:54:40 +02001093 spec->opened_streams &= ~bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001094 analog_low_current_mode(codec);
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001095}
1096
Takashi Iwaiece8d042011-06-19 16:24:21 +02001097static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001098 struct hda_codec *codec,
1099 struct snd_pcm_substream *substream)
1100{
1101 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001102 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001103 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001104
Takashi Iwai25250502011-06-30 17:24:47 +02001105 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
Takashi Iwai25250502011-06-30 17:24:47 +02001106 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001107 set_stream_open(codec, STREAM_MULTI_OUT, true);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001108 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1109 hinfo);
1110 if (err < 0) {
Takashi Iwai3b607e32011-07-18 16:54:40 +02001111 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001112 return err;
1113 }
1114 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001115}
1116
Takashi Iwaiece8d042011-06-19 16:24:21 +02001117static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001118 struct hda_codec *codec,
1119 struct snd_pcm_substream *substream)
1120{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001121 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001122 return 0;
1123}
1124
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001125static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1126 struct hda_codec *codec,
1127 struct snd_pcm_substream *substream)
1128{
1129 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001130
Takashi Iwaiece8d042011-06-19 16:24:21 +02001131 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001132 return -EINVAL;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001133 set_stream_open(codec, STREAM_INDEP_HP, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001134 return 0;
1135}
1136
1137static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1138 struct hda_codec *codec,
1139 struct snd_pcm_substream *substream)
1140{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001141 set_stream_open(codec, STREAM_INDEP_HP, false);
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001142 return 0;
1143}
1144
1145static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1146 struct hda_codec *codec,
1147 unsigned int stream_tag,
1148 unsigned int format,
1149 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001150{
1151 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001152
Takashi Iwai3b607e32011-07-18 16:54:40 +02001153 mutex_lock(&spec->config_mutex);
1154 setup_playback_multi_pcm(spec);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001155 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1156 format, substream);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001157 /* remember for dynamic DAC switch with indep-HP */
1158 spec->active_streams |= STREAM_MULTI_OUT;
1159 spec->cur_dac_stream_tag = stream_tag;
1160 spec->cur_dac_format = format;
1161 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001162 vt1708_update_hp_work(spec);
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001163 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001164}
1165
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001166static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1167 struct hda_codec *codec,
1168 unsigned int stream_tag,
1169 unsigned int format,
1170 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001171{
1172 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001173
Takashi Iwai3b607e32011-07-18 16:54:40 +02001174 mutex_lock(&spec->config_mutex);
1175 if (spec->hp_independent_mode)
1176 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1177 stream_tag, 0, format);
1178 spec->active_streams |= STREAM_INDEP_HP;
1179 spec->cur_hp_stream_tag = stream_tag;
1180 spec->cur_hp_format = format;
1181 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001182 vt1708_update_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001183 return 0;
1184}
1185
1186static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1187 struct hda_codec *codec,
1188 struct snd_pcm_substream *substream)
1189{
1190 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001191
Takashi Iwai3b607e32011-07-18 16:54:40 +02001192 mutex_lock(&spec->config_mutex);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001193 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001194 spec->active_streams &= ~STREAM_MULTI_OUT;
1195 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001196 vt1708_update_hp_work(spec);
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001197 return 0;
1198}
1199
1200static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1201 struct hda_codec *codec,
1202 struct snd_pcm_substream *substream)
1203{
1204 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001205
Takashi Iwai3b607e32011-07-18 16:54:40 +02001206 mutex_lock(&spec->config_mutex);
1207 if (spec->hp_independent_mode)
1208 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
1209 spec->active_streams &= ~STREAM_INDEP_HP;
1210 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001211 vt1708_update_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001212 return 0;
1213}
1214
Joseph Chanc577b8a2006-11-29 15:29:40 +01001215/*
1216 * Digital out
1217 */
1218static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1219 struct hda_codec *codec,
1220 struct snd_pcm_substream *substream)
1221{
1222 struct via_spec *spec = codec->spec;
1223 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1224}
1225
1226static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1227 struct hda_codec *codec,
1228 struct snd_pcm_substream *substream)
1229{
1230 struct via_spec *spec = codec->spec;
1231 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1232}
1233
Harald Welte5691ec72008-09-15 22:42:26 +08001234static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001235 struct hda_codec *codec,
1236 unsigned int stream_tag,
1237 unsigned int format,
1238 struct snd_pcm_substream *substream)
1239{
1240 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001241 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1242 stream_tag, format, substream);
1243}
Harald Welte5691ec72008-09-15 22:42:26 +08001244
Takashi Iwai9da29272009-05-07 16:31:14 +02001245static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1246 struct hda_codec *codec,
1247 struct snd_pcm_substream *substream)
1248{
1249 struct via_spec *spec = codec->spec;
1250 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001251 return 0;
1252}
1253
Joseph Chanc577b8a2006-11-29 15:29:40 +01001254/*
1255 * Analog capture
1256 */
1257static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1258 struct hda_codec *codec,
1259 unsigned int stream_tag,
1260 unsigned int format,
1261 struct snd_pcm_substream *substream)
1262{
1263 struct via_spec *spec = codec->spec;
1264
1265 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1266 stream_tag, 0, format);
1267 return 0;
1268}
1269
1270static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1271 struct hda_codec *codec,
1272 struct snd_pcm_substream *substream)
1273{
1274 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001275 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001276 return 0;
1277}
1278
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001279/* analog capture with dynamic ADC switching */
1280static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1281 struct hda_codec *codec,
1282 unsigned int stream_tag,
1283 unsigned int format,
1284 struct snd_pcm_substream *substream)
1285{
1286 struct via_spec *spec = codec->spec;
1287 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1288
Takashi Iwai3b607e32011-07-18 16:54:40 +02001289 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001290 spec->cur_adc = spec->adc_nids[adc_idx];
1291 spec->cur_adc_stream_tag = stream_tag;
1292 spec->cur_adc_format = format;
1293 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001294 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001295 return 0;
1296}
1297
1298static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1299 struct hda_codec *codec,
1300 struct snd_pcm_substream *substream)
1301{
1302 struct via_spec *spec = codec->spec;
1303
Takashi Iwai3b607e32011-07-18 16:54:40 +02001304 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001305 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1306 spec->cur_adc = 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001307 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001308 return 0;
1309}
1310
1311/* re-setup the stream if running; called from input-src put */
1312static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1313{
1314 struct via_spec *spec = codec->spec;
1315 int adc_idx = spec->inputs[cur].adc_idx;
1316 hda_nid_t adc = spec->adc_nids[adc_idx];
Takashi Iwai3b607e32011-07-18 16:54:40 +02001317 bool ret = false;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001318
Takashi Iwai3b607e32011-07-18 16:54:40 +02001319 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001320 if (spec->cur_adc && spec->cur_adc != adc) {
1321 /* stream is running, let's swap the current ADC */
1322 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1323 spec->cur_adc = adc;
1324 snd_hda_codec_setup_stream(codec, adc,
1325 spec->cur_adc_stream_tag, 0,
1326 spec->cur_adc_format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001327 ret = true;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001328 }
Takashi Iwai3b607e32011-07-18 16:54:40 +02001329 mutex_unlock(&spec->config_mutex);
1330 return ret;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001331}
1332
Takashi Iwai9af74212011-06-18 16:17:45 +02001333static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001334 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001335 .channels_min = 2,
1336 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001337 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001338 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001339 .open = via_playback_multi_pcm_open,
1340 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001341 .prepare = via_playback_multi_pcm_prepare,
1342 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001343 },
1344};
1345
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001346static const struct hda_pcm_stream via_pcm_hp_playback = {
1347 .substreams = 1,
1348 .channels_min = 2,
1349 .channels_max = 2,
1350 /* NID is set in via_build_pcms */
1351 .ops = {
1352 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001353 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001354 .prepare = via_playback_hp_pcm_prepare,
1355 .cleanup = via_playback_hp_pcm_cleanup
1356 },
1357};
1358
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001359static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001360 .substreams = 1,
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001361 .channels_min = 2,
1362 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001363 /* NID is set in via_build_pcms */
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001364 /* We got noisy outputs on the right channel on VT1708 when
1365 * 24bit samples are used. Until any workaround is found,
1366 * disable the 24bit format, so far.
1367 */
1368 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1369 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001370 .open = via_playback_multi_pcm_open,
1371 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001372 .prepare = via_playback_multi_pcm_prepare,
1373 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001374 },
1375};
1376
Takashi Iwai9af74212011-06-18 16:17:45 +02001377static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001378 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001379 .channels_min = 2,
1380 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001381 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001382 .ops = {
1383 .prepare = via_capture_pcm_prepare,
1384 .cleanup = via_capture_pcm_cleanup
1385 },
1386};
1387
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001388static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1389 .substreams = 1,
1390 .channels_min = 2,
1391 .channels_max = 2,
1392 /* NID is set in via_build_pcms */
1393 .ops = {
1394 .prepare = via_dyn_adc_capture_pcm_prepare,
1395 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1396 },
1397};
1398
Takashi Iwai9af74212011-06-18 16:17:45 +02001399static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001400 .substreams = 1,
1401 .channels_min = 2,
1402 .channels_max = 2,
1403 /* NID is set in via_build_pcms */
1404 .ops = {
1405 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001406 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001407 .prepare = via_dig_playback_pcm_prepare,
1408 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001409 },
1410};
1411
Takashi Iwai9af74212011-06-18 16:17:45 +02001412static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001413 .substreams = 1,
1414 .channels_min = 2,
1415 .channels_max = 2,
1416};
1417
Takashi Iwai370bafb2011-06-20 12:47:45 +02001418/*
1419 * slave controls for virtual master
1420 */
1421static const char * const via_slave_vols[] = {
1422 "Front Playback Volume",
1423 "Surround Playback Volume",
1424 "Center Playback Volume",
1425 "LFE Playback Volume",
1426 "Side Playback Volume",
1427 "Headphone Playback Volume",
1428 "Speaker Playback Volume",
1429 NULL,
1430};
1431
1432static const char * const via_slave_sws[] = {
1433 "Front Playback Switch",
1434 "Surround Playback Switch",
1435 "Center Playback Switch",
1436 "LFE Playback Switch",
1437 "Side Playback Switch",
1438 "Headphone Playback Switch",
1439 "Speaker Playback Switch",
1440 NULL,
1441};
1442
Joseph Chanc577b8a2006-11-29 15:29:40 +01001443static int via_build_controls(struct hda_codec *codec)
1444{
1445 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001446 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001447 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001448
Takashi Iwai24088a52011-06-17 16:59:21 +02001449 if (spec->set_widgets_power_state)
1450 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1451 return -ENOMEM;
1452
Joseph Chanc577b8a2006-11-29 15:29:40 +01001453 for (i = 0; i < spec->num_mixers; i++) {
1454 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1455 if (err < 0)
1456 return err;
1457 }
1458
1459 if (spec->multiout.dig_out_nid) {
1460 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001461 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001462 spec->multiout.dig_out_nid);
1463 if (err < 0)
1464 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001465 err = snd_hda_create_spdif_share_sw(codec,
1466 &spec->multiout);
1467 if (err < 0)
1468 return err;
1469 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001470 }
1471 if (spec->dig_in_nid) {
1472 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1473 if (err < 0)
1474 return err;
1475 }
Lydia Wang17314372009-10-10 19:07:37 +08001476
Takashi Iwai370bafb2011-06-20 12:47:45 +02001477 /* if we have no master control, let's create it */
1478 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1479 unsigned int vmaster_tlv[4];
1480 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1481 HDA_OUTPUT, vmaster_tlv);
1482 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1483 vmaster_tlv, via_slave_vols);
1484 if (err < 0)
1485 return err;
1486 }
1487 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1488 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1489 NULL, via_slave_sws);
1490 if (err < 0)
1491 return err;
1492 }
1493
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001494 /* assign Capture Source enums to NID */
1495 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1496 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001497 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001498 if (err < 0)
1499 return err;
1500 }
1501
Lydia Wang17314372009-10-10 19:07:37 +08001502 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001503 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001504 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001505
Takashi Iwai603c4012008-07-30 15:01:44 +02001506 via_free_kctls(codec); /* no longer needed */
Takashi Iwai01a61e12011-10-28 00:03:22 +02001507
1508 err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
1509 if (err < 0)
1510 return err;
1511
Joseph Chanc577b8a2006-11-29 15:29:40 +01001512 return 0;
1513}
1514
1515static int via_build_pcms(struct hda_codec *codec)
1516{
1517 struct via_spec *spec = codec->spec;
1518 struct hda_pcm *info = spec->pcm_rec;
1519
Takashi Iwaia5973102011-09-28 16:43:36 +02001520 codec->num_pcms = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001521 codec->pcm_info = info;
1522
Takashi Iwaia5973102011-09-28 16:43:36 +02001523 if (spec->multiout.num_dacs || spec->num_adc_nids) {
1524 snprintf(spec->stream_name_analog,
1525 sizeof(spec->stream_name_analog),
1526 "%s Analog", codec->chip_name);
1527 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001528
Takashi Iwaia5973102011-09-28 16:43:36 +02001529 if (spec->multiout.num_dacs) {
1530 if (!spec->stream_analog_playback)
1531 spec->stream_analog_playback =
1532 &via_pcm_analog_playback;
1533 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1534 *spec->stream_analog_playback;
1535 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1536 spec->multiout.dac_nids[0];
1537 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1538 spec->multiout.max_channels;
1539 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001540
Takashi Iwaia5973102011-09-28 16:43:36 +02001541 if (!spec->stream_analog_capture) {
1542 if (spec->dyn_adc_switch)
1543 spec->stream_analog_capture =
1544 &via_pcm_dyn_adc_analog_capture;
1545 else
1546 spec->stream_analog_capture =
1547 &via_pcm_analog_capture;
1548 }
1549 if (spec->num_adc_nids) {
1550 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1551 *spec->stream_analog_capture;
1552 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1553 spec->adc_nids[0];
1554 if (!spec->dyn_adc_switch)
1555 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1556 spec->num_adc_nids;
1557 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001558 codec->num_pcms++;
1559 info++;
Takashi Iwaia5973102011-09-28 16:43:36 +02001560 }
1561
1562 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
Takashi Iwai82673bc2011-06-17 16:24:21 +02001563 snprintf(spec->stream_name_digital,
1564 sizeof(spec->stream_name_digital),
1565 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001566 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001567 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001568 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001569 if (!spec->stream_digital_playback)
1570 spec->stream_digital_playback =
1571 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001572 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001573 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001574 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1575 spec->multiout.dig_out_nid;
1576 }
1577 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001578 if (!spec->stream_digital_capture)
1579 spec->stream_digital_capture =
1580 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001581 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001582 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001583 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1584 spec->dig_in_nid;
1585 }
Takashi Iwaia5973102011-09-28 16:43:36 +02001586 codec->num_pcms++;
1587 info++;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001588 }
1589
Takashi Iwaiece8d042011-06-19 16:24:21 +02001590 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001591 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1592 "%s HP", codec->chip_name);
1593 info->name = spec->stream_name_hp;
1594 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1595 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001596 spec->hp_dac_nid;
Takashi Iwaia5973102011-09-28 16:43:36 +02001597 codec->num_pcms++;
1598 info++;
Takashi Iwai7eb56e842011-06-18 16:40:14 +02001599 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001600 return 0;
1601}
1602
1603static void via_free(struct hda_codec *codec)
1604{
1605 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001606
1607 if (!spec)
1608 return;
1609
Takashi Iwai603c4012008-07-30 15:01:44 +02001610 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001611 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001612 kfree(spec->bind_cap_vol);
1613 kfree(spec->bind_cap_sw);
1614 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001615}
1616
Takashi Iwai64be2852011-06-17 16:51:39 +02001617/* mute/unmute outputs */
1618static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1619 hda_nid_t *pins, bool mute)
1620{
1621 int i;
Takashi Iwai94994732011-07-11 11:36:44 +02001622 for (i = 0; i < num_pins; i++) {
1623 unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
1624 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1625 if (parm & AC_PINCTL_IN_EN)
1626 continue;
1627 if (mute)
1628 parm &= ~AC_PINCTL_OUT_EN;
1629 else
1630 parm |= AC_PINCTL_OUT_EN;
Takashi Iwai64be2852011-06-17 16:51:39 +02001631 snd_hda_codec_write(codec, pins[i], 0,
Takashi Iwai94994732011-07-11 11:36:44 +02001632 AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
1633 }
Takashi Iwai64be2852011-06-17 16:51:39 +02001634}
1635
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001636/* mute internal speaker if line-out is plugged */
1637static void via_line_automute(struct hda_codec *codec, int present)
1638{
1639 struct via_spec *spec = codec->spec;
1640
1641 if (!spec->autocfg.speaker_outs)
1642 return;
1643 if (!present)
1644 present = snd_hda_jack_detect(codec,
1645 spec->autocfg.line_out_pins[0]);
1646 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1647 spec->autocfg.speaker_pins,
1648 present);
1649}
1650
Harald Welte69e52a82008-09-09 15:57:32 +08001651/* mute internal speaker if HP is plugged */
1652static void via_hp_automute(struct hda_codec *codec)
1653{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001654 int present = 0;
Takashi Iwai6e969d92011-07-11 11:28:13 +02001655 int nums;
Harald Welte69e52a82008-09-09 15:57:32 +08001656 struct via_spec *spec = codec->spec;
1657
Takashi Iwai187d3332011-11-24 16:33:09 +01001658 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0] &&
1659 (spec->codec_type != VT1708 || spec->vt1708_jack_detect))
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001660 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai6e969d92011-07-11 11:28:13 +02001661
1662 if (spec->smart51_enabled)
1663 nums = spec->autocfg.line_outs + spec->smart51_nums;
1664 else
1665 nums = spec->autocfg.line_outs;
1666 toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
1667
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001668 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001669}
1670
Harald Welte69e52a82008-09-09 15:57:32 +08001671static void via_gpio_control(struct hda_codec *codec)
1672{
1673 unsigned int gpio_data;
1674 unsigned int vol_counter;
1675 unsigned int vol;
1676 unsigned int master_vol;
1677
1678 struct via_spec *spec = codec->spec;
1679
1680 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1681 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1682
1683 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1684 0xF84, 0) & 0x3F0000) >> 16;
1685
1686 vol = vol_counter & 0x1F;
1687 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1688 AC_VERB_GET_AMP_GAIN_MUTE,
1689 AC_AMP_GET_INPUT);
1690
1691 if (gpio_data == 0x02) {
1692 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001693 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1694 AC_VERB_SET_PIN_WIDGET_CONTROL,
1695 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001696 if (vol_counter & 0x20) {
1697 /* decrease volume */
1698 if (vol > master_vol)
1699 vol = master_vol;
1700 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1701 0, HDA_AMP_VOLMASK,
1702 master_vol-vol);
1703 } else {
1704 /* increase volume */
1705 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1706 HDA_AMP_VOLMASK,
1707 ((master_vol+vol) > 0x2A) ? 0x2A :
1708 (master_vol+vol));
1709 }
1710 } else if (!(gpio_data & 0x02)) {
1711 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001712 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1713 AC_VERB_SET_PIN_WIDGET_CONTROL,
1714 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001715 }
1716}
1717
1718/* unsolicited event for jack sensing */
1719static void via_unsol_event(struct hda_codec *codec,
1720 unsigned int res)
1721{
1722 res >>= 26;
Takashi Iwai3a938972011-10-28 01:16:55 +02001723 res = snd_hda_jack_get_action(codec, res);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001724
Lydia Wanga34df192009-10-10 19:08:01 +08001725 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001726 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001727
1728 res &= ~VIA_JACK_EVENT;
1729
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001730 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001731 via_hp_automute(codec);
1732 else if (res == VIA_GPIO_EVENT)
1733 via_gpio_control(codec);
Takashi Iwai01a61e12011-10-28 00:03:22 +02001734 snd_hda_jack_report_sync(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001735}
1736
Takashi Iwai2a439522011-07-26 09:52:50 +02001737#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001738static int via_suspend(struct hda_codec *codec, pm_message_t state)
1739{
1740 struct via_spec *spec = codec->spec;
1741 vt1708_stop_hp_work(spec);
1742 return 0;
1743}
1744#endif
1745
Takashi Iwaicb53c622007-08-10 17:21:45 +02001746#ifdef CONFIG_SND_HDA_POWER_SAVE
1747static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1748{
1749 struct via_spec *spec = codec->spec;
1750 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1751}
1752#endif
1753
Joseph Chanc577b8a2006-11-29 15:29:40 +01001754/*
1755 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001756
1757static int via_init(struct hda_codec *codec);
1758
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001759static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001760 .build_controls = via_build_controls,
1761 .build_pcms = via_build_pcms,
1762 .init = via_init,
1763 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001764 .unsol_event = via_unsol_event,
Takashi Iwai2a439522011-07-26 09:52:50 +02001765#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001766 .suspend = via_suspend,
1767#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001768#ifdef CONFIG_SND_HDA_POWER_SAVE
1769 .check_power_status = via_check_power_status,
1770#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001771};
1772
Takashi Iwai4a796162011-06-17 17:53:38 +02001773static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001774{
Takashi Iwai4a796162011-06-17 17:53:38 +02001775 struct via_spec *spec = codec->spec;
1776 int i;
1777
1778 for (i = 0; i < spec->multiout.num_dacs; i++) {
1779 if (spec->multiout.dac_nids[i] == dac)
1780 return false;
1781 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001782 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001783 return false;
1784 return true;
1785}
1786
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001787static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001788 hda_nid_t target_dac, int with_aa_mix,
1789 struct nid_path *path, int depth)
Takashi Iwai4a796162011-06-17 17:53:38 +02001790{
Takashi Iwai3214b962011-07-18 12:49:25 +02001791 struct via_spec *spec = codec->spec;
Takashi Iwai4a796162011-06-17 17:53:38 +02001792 hda_nid_t conn[8];
1793 int i, nums;
1794
Takashi Iwai3214b962011-07-18 12:49:25 +02001795 if (nid == spec->aa_mix_nid) {
1796 if (!with_aa_mix)
1797 return false;
1798 with_aa_mix = 2; /* mark aa-mix is included */
1799 }
1800
Takashi Iwai4a796162011-06-17 17:53:38 +02001801 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1802 for (i = 0; i < nums; i++) {
1803 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1804 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001805 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1806 /* aa-mix is requested but not included? */
1807 if (!(spec->aa_mix_nid && with_aa_mix == 1))
1808 goto found;
1809 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001810 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001811 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001812 return false;
1813 for (i = 0; i < nums; i++) {
1814 unsigned int type;
1815 type = get_wcaps_type(get_wcaps(codec, conn[i]));
Takashi Iwai3214b962011-07-18 12:49:25 +02001816 if (type == AC_WID_AUD_OUT)
Takashi Iwai4a796162011-06-17 17:53:38 +02001817 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001818 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai3214b962011-07-18 12:49:25 +02001819 with_aa_mix, path, depth + 1))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001820 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001821 }
1822 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001823
1824 found:
1825 path->path[path->depth] = conn[i];
1826 path->idx[path->depth] = i;
1827 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1828 path->multi[path->depth] = 1;
1829 path->depth++;
1830 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001831}
1832
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001833static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001834 hda_nid_t target_dac, int with_aa_mix,
1835 struct nid_path *path)
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001836{
Takashi Iwai3214b962011-07-18 12:49:25 +02001837 if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001838 path->path[path->depth] = nid;
1839 path->depth++;
Takashi Iwai3214b962011-07-18 12:49:25 +02001840 snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
1841 path->depth, path->path[0], path->path[1],
1842 path->path[2], path->path[3], path->path[4]);
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001843 return true;
1844 }
1845 return false;
1846}
1847
Takashi Iwai4a796162011-06-17 17:53:38 +02001848static int via_auto_fill_dac_nids(struct hda_codec *codec)
1849{
1850 struct via_spec *spec = codec->spec;
1851 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001852 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001853 hda_nid_t nid;
1854
Joseph Chanc577b8a2006-11-29 15:29:40 +01001855 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001856 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001857 for (i = 0; i < cfg->line_outs; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001858 hda_nid_t dac = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001859 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001860 if (!nid)
1861 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001862 if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
1863 dac = spec->out_path[i].path[0];
1864 if (!i && parse_output_path(codec, nid, dac, 1,
1865 &spec->out_mix_path))
1866 dac = spec->out_mix_path.path[0];
1867 if (dac) {
1868 spec->private_dac_nids[i] = dac;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001869 dac_num++;
1870 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001871 }
Takashi Iwai3214b962011-07-18 12:49:25 +02001872 if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
1873 spec->out_path[0] = spec->out_mix_path;
1874 spec->out_mix_path.depth = 0;
1875 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001876 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001877 return 0;
1878}
1879
Takashi Iwai4a796162011-06-17 17:53:38 +02001880static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001881 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001882{
Takashi Iwai4a796162011-06-17 17:53:38 +02001883 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001884 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001885 hda_nid_t dac, pin, sel, nid;
1886 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001887
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001888 dac = check_dac ? path->path[0] : 0;
1889 pin = path->path[path->depth - 1];
1890 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001891
Takashi Iwai8df2a312011-06-21 11:48:29 +02001892 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001893 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001894 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001895 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001896 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1897 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001898 else
1899 nid = 0;
1900 if (nid) {
1901 sprintf(name, "%s Playback Volume", pfx);
1902 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001903 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001904 if (err < 0)
1905 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001906 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001907 }
1908
Takashi Iwai8df2a312011-06-21 11:48:29 +02001909 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001910 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001911 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001912 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001913 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1914 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001915 else
1916 nid = 0;
1917 if (nid) {
1918 sprintf(name, "%s Playback Switch", pfx);
1919 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1920 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1921 if (err < 0)
1922 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001923 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001924 }
1925 return 0;
1926}
1927
Takashi Iwaif4a78282011-06-17 18:46:48 +02001928static void mangle_smart51(struct hda_codec *codec)
1929{
1930 struct via_spec *spec = codec->spec;
1931 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001932 struct auto_pin_cfg_item *ins = cfg->inputs;
1933 int i, j, nums, attr;
1934 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001935
Takashi Iwai0f98c242011-06-21 12:51:33 +02001936 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1937 nums = 0;
1938 for (i = 0; i < cfg->num_inputs; i++) {
1939 unsigned int def;
1940 if (ins[i].type > AUTO_PIN_LINE_IN)
1941 continue;
1942 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1943 if (snd_hda_get_input_pin_attr(def) != attr)
1944 continue;
1945 for (j = 0; j < nums; j++)
1946 if (ins[pins[j]].type < ins[i].type) {
1947 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001948 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001949 break;
1950 }
1951 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001952 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001953 }
1954 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001955 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001956 for (i = 0; i < nums; i++) {
1957 hda_nid_t pin = ins[pins[i]].pin;
1958 spec->smart51_pins[spec->smart51_nums++] = pin;
1959 cfg->line_out_pins[cfg->line_outs++] = pin;
1960 if (cfg->line_outs == 3)
1961 break;
1962 }
1963 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001964 }
1965}
1966
Takashi Iwai020066d2011-07-21 13:45:56 +02001967static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src)
1968{
1969 dst->vol_ctl = src->vol_ctl;
1970 dst->mute_ctl = src->mute_ctl;
1971}
1972
Takashi Iwai4a796162011-06-17 17:53:38 +02001973/* add playback controls from the parsed DAC table */
1974static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1975{
1976 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001977 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai3214b962011-07-18 12:49:25 +02001978 struct nid_path *path;
Takashi Iwaiea734962011-01-17 11:29:34 +01001979 static const char * const chname[4] = {
1980 "Front", "Surround", "C/LFE", "Side"
1981 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001982 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001983 int old_line_outs;
1984
1985 /* check smart51 */
1986 old_line_outs = cfg->line_outs;
1987 if (cfg->line_outs == 1)
1988 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001989
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001990 err = via_auto_fill_dac_nids(codec);
1991 if (err < 0)
1992 return err;
1993
Lydia Wang5c9a5612011-07-08 14:03:43 +08001994 if (spec->multiout.num_dacs < 3) {
1995 spec->smart51_nums = 0;
1996 cfg->line_outs = old_line_outs;
1997 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001998 for (i = 0; i < cfg->line_outs; i++) {
1999 hda_nid_t pin, dac;
2000 pin = cfg->line_out_pins[i];
2001 dac = spec->multiout.dac_nids[i];
2002 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002003 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02002004 path = spec->out_path + i;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02002005 if (i == HDA_CLFE) {
Takashi Iwai3214b962011-07-18 12:49:25 +02002006 err = create_ch_ctls(codec, "Center", 1, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002007 if (err < 0)
2008 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002009 err = create_ch_ctls(codec, "LFE", 2, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002010 if (err < 0)
2011 return err;
2012 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02002013 const char *pfx = chname[i];
2014 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
2015 cfg->line_outs == 1)
2016 pfx = "Speaker";
Takashi Iwai3214b962011-07-18 12:49:25 +02002017 err = create_ch_ctls(codec, pfx, 3, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002018 if (err < 0)
2019 return err;
2020 }
Takashi Iwai020066d2011-07-21 13:45:56 +02002021 if (path != spec->out_path + i)
2022 copy_path_mixer_ctls(&spec->out_path[i], path);
2023 if (path == spec->out_path && spec->out_mix_path.depth)
2024 copy_path_mixer_ctls(&spec->out_mix_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002025 }
2026
Takashi Iwai4a796162011-06-17 17:53:38 +02002027 idx = get_connection_index(codec, spec->aa_mix_nid,
2028 spec->multiout.dac_nids[0]);
2029 if (idx >= 0) {
2030 /* add control to mixer */
Takashi Iwai3214b962011-07-18 12:49:25 +02002031 const char *name;
2032 name = spec->out_mix_path.depth ?
2033 "PCM Loopback Playback Volume" : "PCM Playback Volume";
2034 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002035 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2036 idx, HDA_INPUT));
2037 if (err < 0)
2038 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002039 name = spec->out_mix_path.depth ?
2040 "PCM Loopback Playback Switch" : "PCM Playback Switch";
2041 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002042 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2043 idx, HDA_INPUT));
2044 if (err < 0)
2045 return err;
2046 }
2047
Takashi Iwaif4a78282011-06-17 18:46:48 +02002048 cfg->line_outs = old_line_outs;
2049
Joseph Chanc577b8a2006-11-29 15:29:40 +01002050 return 0;
2051}
2052
Takashi Iwai4a796162011-06-17 17:53:38 +02002053static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002054{
Takashi Iwai4a796162011-06-17 17:53:38 +02002055 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002056 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002057 bool check_dac;
Takashi Iwai3214b962011-07-18 12:49:25 +02002058 int i, err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002059
2060 if (!pin)
2061 return 0;
2062
Takashi Iwai3214b962011-07-18 12:49:25 +02002063 if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
2064 for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
2065 if (i < spec->multiout.num_dacs &&
2066 parse_output_path(codec, pin,
2067 spec->multiout.dac_nids[i], 0,
2068 &spec->hp_indep_path)) {
2069 spec->hp_indep_shared = i;
2070 break;
2071 }
2072 }
Takashi Iwai25250502011-06-30 17:24:47 +02002073 }
Takashi Iwai3214b962011-07-18 12:49:25 +02002074 if (spec->hp_indep_path.depth) {
2075 spec->hp_dac_nid = spec->hp_indep_path.path[0];
2076 if (!spec->hp_indep_shared)
2077 spec->hp_path = spec->hp_indep_path;
2078 }
2079 /* optionally check front-path w/o AA-mix */
2080 if (!spec->hp_path.depth)
2081 parse_output_path(codec, pin,
2082 spec->multiout.dac_nids[HDA_FRONT], 0,
2083 &spec->hp_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002084
Takashi Iwaiece8d042011-06-19 16:24:21 +02002085 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai3214b962011-07-18 12:49:25 +02002086 1, &spec->hp_mix_path) && !spec->hp_path.depth)
Takashi Iwaiece8d042011-06-19 16:24:21 +02002087 return 0;
2088
Takashi Iwai3214b962011-07-18 12:49:25 +02002089 if (spec->hp_path.depth) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002090 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002091 check_dac = true;
2092 } else {
Takashi Iwai3214b962011-07-18 12:49:25 +02002093 path = &spec->hp_mix_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002094 check_dac = false;
2095 }
2096 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002097 if (err < 0)
2098 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002099 if (check_dac)
2100 copy_path_mixer_ctls(&spec->hp_mix_path, path);
2101 else
2102 copy_path_mixer_ctls(&spec->hp_path, path);
2103 if (spec->hp_indep_path.depth)
2104 copy_path_mixer_ctls(&spec->hp_indep_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002105 return 0;
2106}
2107
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002108static int via_auto_create_speaker_ctls(struct hda_codec *codec)
2109{
2110 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +02002111 struct nid_path *path;
2112 bool check_dac;
Wang Shaoyan81c0a782011-08-05 18:51:29 +08002113 hda_nid_t pin, dac = 0;
Takashi Iwai3214b962011-07-18 12:49:25 +02002114 int err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002115
2116 pin = spec->autocfg.speaker_pins[0];
2117 if (!spec->autocfg.speaker_outs || !pin)
2118 return 0;
2119
Takashi Iwai3214b962011-07-18 12:49:25 +02002120 if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02002121 dac = spec->speaker_path.path[0];
Takashi Iwai3214b962011-07-18 12:49:25 +02002122 if (!dac)
2123 parse_output_path(codec, pin,
2124 spec->multiout.dac_nids[HDA_FRONT], 0,
2125 &spec->speaker_path);
2126 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
2127 1, &spec->speaker_mix_path) && !dac)
2128 return 0;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002129
Takashi Iwai3214b962011-07-18 12:49:25 +02002130 /* no AA-path for front? */
2131 if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
2132 dac = 0;
2133
2134 spec->speaker_dac_nid = dac;
2135 spec->multiout.extra_out_nid[0] = dac;
2136 if (dac) {
2137 path = &spec->speaker_path;
2138 check_dac = true;
2139 } else {
2140 path = &spec->speaker_mix_path;
2141 check_dac = false;
2142 }
2143 err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
2144 if (err < 0)
2145 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002146 if (check_dac)
2147 copy_path_mixer_ctls(&spec->speaker_mix_path, path);
2148 else
2149 copy_path_mixer_ctls(&spec->speaker_path, path);
Takashi Iwai3214b962011-07-18 12:49:25 +02002150 return 0;
2151}
2152
2153#define via_aamix_ctl_info via_pin_power_ctl_info
2154
2155static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol,
2156 struct snd_ctl_elem_value *ucontrol)
2157{
2158 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2159 struct via_spec *spec = codec->spec;
2160 ucontrol->value.enumerated.item[0] = spec->aamix_mode;
2161 return 0;
2162}
2163
2164static void update_aamix_paths(struct hda_codec *codec, int do_mix,
2165 struct nid_path *nomix, struct nid_path *mix)
2166{
2167 if (do_mix) {
2168 activate_output_path(codec, nomix, false, false);
2169 activate_output_path(codec, mix, true, false);
2170 } else {
2171 activate_output_path(codec, mix, false, false);
2172 activate_output_path(codec, nomix, true, false);
2173 }
2174}
2175
2176static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol,
2177 struct snd_ctl_elem_value *ucontrol)
2178{
2179 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2180 struct via_spec *spec = codec->spec;
2181 unsigned int val = ucontrol->value.enumerated.item[0];
2182
2183 if (val == spec->aamix_mode)
2184 return 0;
2185 spec->aamix_mode = val;
2186 /* update front path */
2187 update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
2188 /* update HP path */
2189 if (!spec->hp_independent_mode) {
2190 update_aamix_paths(codec, val, &spec->hp_path,
2191 &spec->hp_mix_path);
2192 }
2193 /* update speaker path */
2194 update_aamix_paths(codec, val, &spec->speaker_path,
2195 &spec->speaker_mix_path);
2196 return 1;
2197}
2198
2199static const struct snd_kcontrol_new via_aamix_ctl_enum = {
2200 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2201 .name = "Loopback Mixing",
2202 .info = via_aamix_ctl_info,
2203 .get = via_aamix_ctl_get,
2204 .put = via_aamix_ctl_put,
2205};
2206
2207static int via_auto_create_loopback_switch(struct hda_codec *codec)
2208{
2209 struct via_spec *spec = codec->spec;
2210
Takashi Iwai4808d122012-01-10 15:16:02 +01002211 if (!spec->aa_mix_nid)
2212 return 0; /* no loopback switching available */
2213 if (!(spec->out_mix_path.depth || spec->hp_mix_path.depth ||
2214 spec->speaker_path.depth))
Takashi Iwai3214b962011-07-18 12:49:25 +02002215 return 0; /* no loopback switching available */
2216 if (!via_clone_control(spec, &via_aamix_ctl_enum))
2217 return -ENOMEM;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002218 return 0;
2219}
2220
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002221/* look for ADCs */
2222static int via_fill_adcs(struct hda_codec *codec)
2223{
2224 struct via_spec *spec = codec->spec;
2225 hda_nid_t nid = codec->start_nid;
2226 int i;
2227
2228 for (i = 0; i < codec->num_nodes; i++, nid++) {
2229 unsigned int wcaps = get_wcaps(codec, nid);
2230 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2231 continue;
2232 if (wcaps & AC_WCAP_DIGITAL)
2233 continue;
2234 if (!(wcaps & AC_WCAP_CONN_LIST))
2235 continue;
2236 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2237 return -ENOMEM;
2238 spec->adc_nids[spec->num_adc_nids++] = nid;
2239 }
2240 return 0;
2241}
2242
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002243/* input-src control */
2244static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2245 struct snd_ctl_elem_info *uinfo)
2246{
2247 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2248 struct via_spec *spec = codec->spec;
2249
2250 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2251 uinfo->count = 1;
2252 uinfo->value.enumerated.items = spec->num_inputs;
2253 if (uinfo->value.enumerated.item >= spec->num_inputs)
2254 uinfo->value.enumerated.item = spec->num_inputs - 1;
2255 strcpy(uinfo->value.enumerated.name,
2256 spec->inputs[uinfo->value.enumerated.item].label);
2257 return 0;
2258}
2259
2260static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2261 struct snd_ctl_elem_value *ucontrol)
2262{
2263 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2264 struct via_spec *spec = codec->spec;
2265 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2266
2267 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2268 return 0;
2269}
2270
2271static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2272 struct snd_ctl_elem_value *ucontrol)
2273{
2274 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2275 struct via_spec *spec = codec->spec;
2276 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2277 hda_nid_t mux;
2278 int cur;
2279
2280 cur = ucontrol->value.enumerated.item[0];
2281 if (cur < 0 || cur >= spec->num_inputs)
2282 return -EINVAL;
2283 if (spec->cur_mux[idx] == cur)
2284 return 0;
2285 spec->cur_mux[idx] = cur;
2286 if (spec->dyn_adc_switch) {
2287 int adc_idx = spec->inputs[cur].adc_idx;
2288 mux = spec->mux_nids[adc_idx];
2289 via_dyn_adc_pcm_resetup(codec, cur);
2290 } else {
2291 mux = spec->mux_nids[idx];
2292 if (snd_BUG_ON(!mux))
2293 return -EINVAL;
2294 }
2295
2296 if (mux) {
2297 /* switch to D0 beofre change index */
2298 if (snd_hda_codec_read(codec, mux, 0,
2299 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2300 snd_hda_codec_write(codec, mux, 0,
2301 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2302 snd_hda_codec_write(codec, mux, 0,
2303 AC_VERB_SET_CONNECT_SEL,
2304 spec->inputs[cur].mux_idx);
2305 }
2306
2307 /* update jack power state */
2308 set_widgets_power_state(codec);
2309 return 0;
2310}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002311
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002312static const struct snd_kcontrol_new via_input_src_ctl = {
2313 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2314 /* The multiple "Capture Source" controls confuse alsamixer
2315 * So call somewhat different..
2316 */
2317 /* .name = "Capture Source", */
2318 .name = "Input Source",
2319 .info = via_mux_enum_info,
2320 .get = via_mux_enum_get,
2321 .put = via_mux_enum_put,
2322};
2323
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002324static int create_input_src_ctls(struct hda_codec *codec, int count)
2325{
2326 struct via_spec *spec = codec->spec;
2327 struct snd_kcontrol_new *knew;
2328
2329 if (spec->num_inputs <= 1 || !count)
2330 return 0; /* no need for single src */
2331
2332 knew = via_clone_control(spec, &via_input_src_ctl);
2333 if (!knew)
2334 return -ENOMEM;
2335 knew->count = count;
2336 return 0;
2337}
2338
2339/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002340static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2341{
2342 struct hda_amp_list *list;
2343
2344 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2345 return;
2346 list = spec->loopback_list + spec->num_loopbacks;
2347 list->nid = mix;
2348 list->dir = HDA_INPUT;
2349 list->idx = idx;
2350 spec->num_loopbacks++;
2351 spec->loopback.amplist = spec->loopback_list;
2352}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002353
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002354static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002355 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002356{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002357 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002358}
2359
2360/* add the input-route to the given pin */
2361static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002362{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002363 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002364 int c, idx;
2365
2366 spec->inputs[spec->num_inputs].adc_idx = -1;
2367 spec->inputs[spec->num_inputs].pin = pin;
2368 for (c = 0; c < spec->num_adc_nids; c++) {
2369 if (spec->mux_nids[c]) {
2370 idx = get_connection_index(codec, spec->mux_nids[c],
2371 pin);
2372 if (idx < 0)
2373 continue;
2374 spec->inputs[spec->num_inputs].mux_idx = idx;
2375 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002376 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002377 continue;
2378 }
2379 spec->inputs[spec->num_inputs].adc_idx = c;
2380 /* Can primary ADC satisfy all inputs? */
2381 if (!spec->dyn_adc_switch &&
2382 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2383 snd_printd(KERN_INFO
2384 "via: dynamic ADC switching enabled\n");
2385 spec->dyn_adc_switch = 1;
2386 }
2387 return true;
2388 }
2389 return false;
2390}
2391
2392static int get_mux_nids(struct hda_codec *codec);
2393
2394/* parse input-routes; fill ADCs, MUXs and input-src entries */
2395static int parse_analog_inputs(struct hda_codec *codec)
2396{
2397 struct via_spec *spec = codec->spec;
2398 const struct auto_pin_cfg *cfg = &spec->autocfg;
2399 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002400
2401 err = via_fill_adcs(codec);
2402 if (err < 0)
2403 return err;
2404 err = get_mux_nids(codec);
2405 if (err < 0)
2406 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002407
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002408 /* fill all input-routes */
2409 for (i = 0; i < cfg->num_inputs; i++) {
2410 if (add_input_route(codec, cfg->inputs[i].pin))
2411 spec->inputs[spec->num_inputs++].label =
2412 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002413 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002414
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002415 /* check for internal loopback recording */
2416 if (spec->aa_mix_nid &&
2417 add_input_route(codec, spec->aa_mix_nid))
2418 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2419
2420 return 0;
2421}
2422
2423/* create analog-loopback volume/switch controls */
2424static int create_loopback_ctls(struct hda_codec *codec)
2425{
2426 struct via_spec *spec = codec->spec;
2427 const struct auto_pin_cfg *cfg = &spec->autocfg;
2428 const char *prev_label = NULL;
2429 int type_idx = 0;
2430 int i, j, err, idx;
2431
2432 if (!spec->aa_mix_nid)
2433 return 0;
2434
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002435 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002436 hda_nid_t pin = cfg->inputs[i].pin;
2437 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2438
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002439 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002440 type_idx++;
2441 else
2442 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002443 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002444 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2445 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002446 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002447 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002448 if (err < 0)
2449 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002450 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002451 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002452
2453 /* remember the label for smart51 control */
2454 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002455 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002456 spec->smart51_idxs[j] = idx;
2457 spec->smart51_labels[j] = label;
2458 break;
2459 }
2460 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002461 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002462 return 0;
2463}
2464
2465/* create mic-boost controls (if present) */
2466static int create_mic_boost_ctls(struct hda_codec *codec)
2467{
2468 struct via_spec *spec = codec->spec;
2469 const struct auto_pin_cfg *cfg = &spec->autocfg;
2470 int i, err;
2471
2472 for (i = 0; i < cfg->num_inputs; i++) {
2473 hda_nid_t pin = cfg->inputs[i].pin;
2474 unsigned int caps;
2475 const char *label;
2476 char name[32];
2477
2478 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2479 continue;
2480 caps = query_amp_caps(codec, pin, HDA_INPUT);
2481 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2482 continue;
2483 label = hda_get_autocfg_input_label(codec, cfg, i);
2484 snprintf(name, sizeof(name), "%s Boost Volume", label);
2485 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2486 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2487 if (err < 0)
2488 return err;
2489 }
2490 return 0;
2491}
2492
2493/* create capture and input-src controls for multiple streams */
2494static int create_multi_adc_ctls(struct hda_codec *codec)
2495{
2496 struct via_spec *spec = codec->spec;
2497 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002498
2499 /* create capture mixer elements */
2500 for (i = 0; i < spec->num_adc_nids; i++) {
2501 hda_nid_t adc = spec->adc_nids[i];
2502 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2503 "Capture Volume", i,
2504 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2505 HDA_INPUT));
2506 if (err < 0)
2507 return err;
2508 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2509 "Capture Switch", i,
2510 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2511 HDA_INPUT));
2512 if (err < 0)
2513 return err;
2514 }
2515
2516 /* input-source control */
2517 for (i = 0; i < spec->num_adc_nids; i++)
2518 if (!spec->mux_nids[i])
2519 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002520 err = create_input_src_ctls(codec, i);
2521 if (err < 0)
2522 return err;
2523 return 0;
2524}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002525
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002526/* bind capture volume/switch */
2527static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2528 HDA_BIND_VOL("Capture Volume", 0);
2529static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2530 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002531
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002532static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2533 struct hda_ctl_ops *ops)
2534{
2535 struct hda_bind_ctls *ctl;
2536 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002537
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002538 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2539 if (!ctl)
2540 return -ENOMEM;
2541 ctl->ops = ops;
2542 for (i = 0; i < spec->num_adc_nids; i++)
2543 ctl->values[i] =
2544 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2545 *ctl_ret = ctl;
2546 return 0;
2547}
2548
2549/* create capture and input-src controls for dynamic ADC-switch case */
2550static int create_dyn_adc_ctls(struct hda_codec *codec)
2551{
2552 struct via_spec *spec = codec->spec;
2553 struct snd_kcontrol_new *knew;
2554 int err;
2555
2556 /* set up the bind capture ctls */
2557 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2558 if (err < 0)
2559 return err;
2560 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2561 if (err < 0)
2562 return err;
2563
2564 /* create capture mixer elements */
2565 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2566 if (!knew)
2567 return -ENOMEM;
2568 knew->private_value = (long)spec->bind_cap_vol;
2569
2570 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2571 if (!knew)
2572 return -ENOMEM;
2573 knew->private_value = (long)spec->bind_cap_sw;
2574
2575 /* input-source control */
2576 err = create_input_src_ctls(codec, 1);
2577 if (err < 0)
2578 return err;
2579 return 0;
2580}
2581
2582/* parse and create capture-related stuff */
2583static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2584{
2585 struct via_spec *spec = codec->spec;
2586 int err;
2587
2588 err = parse_analog_inputs(codec);
2589 if (err < 0)
2590 return err;
2591 if (spec->dyn_adc_switch)
2592 err = create_dyn_adc_ctls(codec);
2593 else
2594 err = create_multi_adc_ctls(codec);
2595 if (err < 0)
2596 return err;
2597 err = create_loopback_ctls(codec);
2598 if (err < 0)
2599 return err;
2600 err = create_mic_boost_ctls(codec);
2601 if (err < 0)
2602 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002603 return 0;
2604}
2605
Harald Welte76d9b0d2008-09-09 15:50:37 +08002606static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2607{
2608 unsigned int def_conf;
2609 unsigned char seqassoc;
2610
Takashi Iwai2f334f92009-02-20 14:37:42 +01002611 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002612 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2613 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002614 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2615 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2616 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2617 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002618 }
2619
2620 return;
2621}
2622
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002623static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002624 struct snd_ctl_elem_value *ucontrol)
2625{
2626 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2627 struct via_spec *spec = codec->spec;
2628
2629 if (spec->codec_type != VT1708)
2630 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002631 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002632 return 0;
2633}
2634
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002635static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002636 struct snd_ctl_elem_value *ucontrol)
2637{
2638 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2639 struct via_spec *spec = codec->spec;
Takashi Iwai187d3332011-11-24 16:33:09 +01002640 int val;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002641
2642 if (spec->codec_type != VT1708)
2643 return 0;
Takashi Iwai187d3332011-11-24 16:33:09 +01002644 val = !!ucontrol->value.integer.value[0];
2645 if (spec->vt1708_jack_detect == val)
2646 return 0;
2647 spec->vt1708_jack_detect = val;
2648 if (spec->vt1708_jack_detect &&
2649 snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") != 1) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002650 mute_aa_path(codec, 1);
2651 notify_aa_path_ctls(codec);
2652 }
Takashi Iwai187d3332011-11-24 16:33:09 +01002653 via_hp_automute(codec);
2654 vt1708_update_hp_work(spec);
2655 return 1;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002656}
2657
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002658static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2659 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2660 .name = "Jack Detect",
2661 .count = 1,
2662 .info = snd_ctl_boolean_mono_info,
2663 .get = vt1708_jack_detect_get,
2664 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002665};
2666
Takashi Iwai12daef62011-06-18 17:45:49 +02002667static void fill_dig_outs(struct hda_codec *codec);
2668static void fill_dig_in(struct hda_codec *codec);
2669
2670static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002671{
2672 struct via_spec *spec = codec->spec;
2673 int err;
2674
2675 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2676 if (err < 0)
2677 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002678 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002679 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002680
Takashi Iwai4a796162011-06-17 17:53:38 +02002681 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002682 if (err < 0)
2683 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002684 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002685 if (err < 0)
2686 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002687 err = via_auto_create_speaker_ctls(codec);
2688 if (err < 0)
2689 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002690 err = via_auto_create_loopback_switch(codec);
2691 if (err < 0)
2692 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002693 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002694 if (err < 0)
2695 return err;
2696
2697 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2698
Takashi Iwai12daef62011-06-18 17:45:49 +02002699 fill_dig_outs(codec);
2700 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002701
Takashi Iwai603c4012008-07-30 15:01:44 +02002702 if (spec->kctls.list)
2703 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002704
Joseph Chanc577b8a2006-11-29 15:29:40 +01002705
Takashi Iwai3214b962011-07-18 12:49:25 +02002706 if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002707 err = via_hp_build(codec);
2708 if (err < 0)
2709 return err;
2710 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002711
Takashi Iwaif4a78282011-06-17 18:46:48 +02002712 err = via_smart51_build(codec);
2713 if (err < 0)
2714 return err;
2715
Takashi Iwai5d417622011-06-20 11:32:27 +02002716 /* assign slave outs */
2717 if (spec->slave_dig_outs[0])
2718 codec->slave_dig_outs = spec->slave_dig_outs;
2719
Joseph Chanc577b8a2006-11-29 15:29:40 +01002720 return 1;
2721}
2722
Takashi Iwai5d417622011-06-20 11:32:27 +02002723static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002724{
Lydia Wang25eaba22009-10-10 19:08:43 +08002725 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002726 if (spec->multiout.dig_out_nid)
2727 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2728 if (spec->slave_dig_outs[0])
2729 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2730}
Lydia Wang25eaba22009-10-10 19:08:43 +08002731
Takashi Iwai5d417622011-06-20 11:32:27 +02002732static void via_auto_init_dig_in(struct hda_codec *codec)
2733{
2734 struct via_spec *spec = codec->spec;
2735 if (!spec->dig_in_nid)
2736 return;
2737 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2738 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2739}
2740
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002741/* initialize the unsolicited events */
2742static void via_auto_init_unsol_event(struct hda_codec *codec)
2743{
2744 struct via_spec *spec = codec->spec;
2745 struct auto_pin_cfg *cfg = &spec->autocfg;
2746 unsigned int ev;
2747 int i;
2748
2749 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002750 snd_hda_jack_detect_enable(codec, cfg->hp_pins[0],
2751 VIA_HP_EVENT | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002752
2753 if (cfg->speaker_pins[0])
2754 ev = VIA_LINE_EVENT;
2755 else
2756 ev = 0;
2757 for (i = 0; i < cfg->line_outs; i++) {
2758 if (cfg->line_out_pins[i] &&
2759 is_jack_detectable(codec, cfg->line_out_pins[i]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002760 snd_hda_jack_detect_enable(codec, cfg->line_out_pins[i],
2761 ev | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002762 }
2763
2764 for (i = 0; i < cfg->num_inputs; i++) {
2765 if (is_jack_detectable(codec, cfg->inputs[i].pin))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002766 snd_hda_jack_detect_enable(codec, cfg->inputs[i].pin,
2767 VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002768 }
2769}
2770
Takashi Iwai5d417622011-06-20 11:32:27 +02002771static int via_init(struct hda_codec *codec)
2772{
2773 struct via_spec *spec = codec->spec;
2774 int i;
2775
2776 for (i = 0; i < spec->num_iverbs; i++)
2777 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2778
Joseph Chanc577b8a2006-11-29 15:29:40 +01002779 via_auto_init_multi_out(codec);
2780 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002781 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002782 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002783 via_auto_init_dig_outs(codec);
2784 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002785
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002786 via_auto_init_unsol_event(codec);
2787
2788 via_hp_automute(codec);
Takashi Iwai187d3332011-11-24 16:33:09 +01002789 vt1708_update_hp_work(spec);
Takashi Iwai01a61e12011-10-28 00:03:22 +02002790 snd_hda_jack_report_sync(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002791
Joseph Chanc577b8a2006-11-29 15:29:40 +01002792 return 0;
2793}
2794
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002795static void vt1708_update_hp_jack_state(struct work_struct *work)
2796{
2797 struct via_spec *spec = container_of(work, struct via_spec,
2798 vt1708_hp_work.work);
2799 if (spec->codec_type != VT1708)
2800 return;
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002801 snd_hda_jack_set_dirty_all(spec->codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002802 /* if jack state toggled */
2803 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002804 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002805 spec->vt1708_hp_present ^= 1;
2806 via_hp_automute(spec->codec);
2807 }
Takashi Iwai187d3332011-11-24 16:33:09 +01002808 if (spec->vt1708_jack_detect)
2809 schedule_delayed_work(&spec->vt1708_hp_work,
2810 msecs_to_jiffies(100));
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002811}
2812
Takashi Iwai337b9d02009-07-07 18:18:59 +02002813static int get_mux_nids(struct hda_codec *codec)
2814{
2815 struct via_spec *spec = codec->spec;
2816 hda_nid_t nid, conn[8];
2817 unsigned int type;
2818 int i, n;
2819
2820 for (i = 0; i < spec->num_adc_nids; i++) {
2821 nid = spec->adc_nids[i];
2822 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002823 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002824 if (type == AC_WID_PIN)
2825 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002826 n = snd_hda_get_connections(codec, nid, conn,
2827 ARRAY_SIZE(conn));
2828 if (n <= 0)
2829 break;
2830 if (n > 1) {
2831 spec->mux_nids[i] = nid;
2832 break;
2833 }
2834 nid = conn[0];
2835 }
2836 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002837 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002838}
2839
Joseph Chanc577b8a2006-11-29 15:29:40 +01002840static int patch_vt1708(struct hda_codec *codec)
2841{
2842 struct via_spec *spec;
2843 int err;
2844
2845 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002846 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002847 if (spec == NULL)
2848 return -ENOMEM;
2849
Takashi Iwai620e2b22011-06-17 17:19:19 +02002850 spec->aa_mix_nid = 0x17;
2851
Takashi Iwai12daef62011-06-18 17:45:49 +02002852 /* Add HP and CD pin config connect bit re-config action */
2853 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2854 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2855
Joseph Chanc577b8a2006-11-29 15:29:40 +01002856 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002857 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002858 if (err < 0) {
2859 via_free(codec);
2860 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002861 }
2862
Takashi Iwai12daef62011-06-18 17:45:49 +02002863 /* add jack detect on/off control */
2864 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2865 return -ENOMEM;
2866
Takashi Iwaibc9b562382008-05-23 17:50:27 +02002867 /* disable 32bit format on VT1708 */
2868 if (codec->vendor_id == 0x11061708)
2869 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002870
Lydia Wange322a362011-06-29 13:52:02 +08002871 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2872
Joseph Chanc577b8a2006-11-29 15:29:40 +01002873 codec->patch_ops = via_patch_ops;
2874
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002875 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002876 return 0;
2877}
2878
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002879static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002880{
2881 struct via_spec *spec;
2882 int err;
2883
2884 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002885 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002886 if (spec == NULL)
2887 return -ENOMEM;
2888
Takashi Iwai620e2b22011-06-17 17:19:19 +02002889 spec->aa_mix_nid = 0x18;
2890
Takashi Iwai12daef62011-06-18 17:45:49 +02002891 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002892 if (err < 0) {
2893 via_free(codec);
2894 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002895 }
2896
Joseph Chanc577b8a2006-11-29 15:29:40 +01002897 codec->patch_ops = via_patch_ops;
2898
Josepch Chanf7278fd2007-12-13 16:40:40 +01002899 return 0;
2900}
2901
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002902static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2903{
2904 struct via_spec *spec = codec->spec;
2905 int imux_is_smixer;
2906 unsigned int parm;
2907 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002908 if ((spec->codec_type != VT1708B_4CH) &&
2909 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002910 is_8ch = 1;
2911
2912 /* SW0 (17h) = stereo mixer */
2913 imux_is_smixer =
2914 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2915 == ((spec->codec_type == VT1708S) ? 5 : 0));
2916 /* inputs */
2917 /* PW 1/2/5 (1ah/1bh/1eh) */
2918 parm = AC_PWRST_D3;
2919 set_pin_power_state(codec, 0x1a, &parm);
2920 set_pin_power_state(codec, 0x1b, &parm);
2921 set_pin_power_state(codec, 0x1e, &parm);
2922 if (imux_is_smixer)
2923 parm = AC_PWRST_D0;
2924 /* SW0 (17h), AIW 0/1 (13h/14h) */
2925 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2926 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2927 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2928
2929 /* outputs */
2930 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2931 parm = AC_PWRST_D3;
2932 set_pin_power_state(codec, 0x19, &parm);
2933 if (spec->smart51_enabled)
2934 set_pin_power_state(codec, 0x1b, &parm);
2935 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2936 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2937
2938 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2939 if (is_8ch) {
2940 parm = AC_PWRST_D3;
2941 set_pin_power_state(codec, 0x22, &parm);
2942 if (spec->smart51_enabled)
2943 set_pin_power_state(codec, 0x1a, &parm);
2944 snd_hda_codec_write(codec, 0x26, 0,
2945 AC_VERB_SET_POWER_STATE, parm);
2946 snd_hda_codec_write(codec, 0x24, 0,
2947 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002948 } else if (codec->vendor_id == 0x11064397) {
2949 /* PW7(23h), SW2(27h), AOW2(25h) */
2950 parm = AC_PWRST_D3;
2951 set_pin_power_state(codec, 0x23, &parm);
2952 if (spec->smart51_enabled)
2953 set_pin_power_state(codec, 0x1a, &parm);
2954 snd_hda_codec_write(codec, 0x27, 0,
2955 AC_VERB_SET_POWER_STATE, parm);
2956 snd_hda_codec_write(codec, 0x25, 0,
2957 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002958 }
2959
2960 /* PW 3/4/7 (1ch/1dh/23h) */
2961 parm = AC_PWRST_D3;
2962 /* force to D0 for internal Speaker */
2963 set_pin_power_state(codec, 0x1c, &parm);
2964 set_pin_power_state(codec, 0x1d, &parm);
2965 if (is_8ch)
2966 set_pin_power_state(codec, 0x23, &parm);
2967
2968 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2969 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2970 imux_is_smixer ? AC_PWRST_D0 : parm);
2971 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2972 if (is_8ch) {
2973 snd_hda_codec_write(codec, 0x25, 0,
2974 AC_VERB_SET_POWER_STATE, parm);
2975 snd_hda_codec_write(codec, 0x27, 0,
2976 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002977 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2978 snd_hda_codec_write(codec, 0x25, 0,
2979 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002980}
2981
Lydia Wang518bf3b2009-10-10 19:07:29 +08002982static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002983static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002984{
2985 struct via_spec *spec;
2986 int err;
2987
Lydia Wang518bf3b2009-10-10 19:07:29 +08002988 if (get_codec_type(codec) == VT1708BCE)
2989 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002990
Josepch Chanf7278fd2007-12-13 16:40:40 +01002991 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002992 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002993 if (spec == NULL)
2994 return -ENOMEM;
2995
Takashi Iwai620e2b22011-06-17 17:19:19 +02002996 spec->aa_mix_nid = 0x16;
2997
Josepch Chanf7278fd2007-12-13 16:40:40 +01002998 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002999 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01003000 if (err < 0) {
3001 via_free(codec);
3002 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003003 }
3004
Josepch Chanf7278fd2007-12-13 16:40:40 +01003005 codec->patch_ops = via_patch_ops;
3006
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003007 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
3008
Josepch Chanf7278fd2007-12-13 16:40:40 +01003009 return 0;
3010}
3011
Harald Welted949cac2008-09-09 15:56:01 +08003012/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02003013static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08003014 /* Enable Mic Boost Volume backdoor */
3015 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003016 /* don't bybass mixer */
3017 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08003018 { }
3019};
3020
Takashi Iwai9da29272009-05-07 16:31:14 +02003021/* fill out digital output widgets; one for master and one for slave outputs */
3022static void fill_dig_outs(struct hda_codec *codec)
3023{
3024 struct via_spec *spec = codec->spec;
3025 int i;
3026
3027 for (i = 0; i < spec->autocfg.dig_outs; i++) {
3028 hda_nid_t nid;
3029 int conn;
3030
3031 nid = spec->autocfg.dig_out_pins[i];
3032 if (!nid)
3033 continue;
3034 conn = snd_hda_get_connections(codec, nid, &nid, 1);
3035 if (conn < 1)
3036 continue;
3037 if (!spec->multiout.dig_out_nid)
3038 spec->multiout.dig_out_nid = nid;
3039 else {
3040 spec->slave_dig_outs[0] = nid;
3041 break; /* at most two dig outs */
3042 }
3043 }
3044}
3045
Takashi Iwai12daef62011-06-18 17:45:49 +02003046static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08003047{
3048 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02003049 hda_nid_t dig_nid;
3050 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08003051
Takashi Iwai12daef62011-06-18 17:45:49 +02003052 if (!spec->autocfg.dig_in_pin)
3053 return;
Harald Welted949cac2008-09-09 15:56:01 +08003054
Takashi Iwai12daef62011-06-18 17:45:49 +02003055 dig_nid = codec->start_nid;
3056 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
3057 unsigned int wcaps = get_wcaps(codec, dig_nid);
3058 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
3059 continue;
3060 if (!(wcaps & AC_WCAP_DIGITAL))
3061 continue;
3062 if (!(wcaps & AC_WCAP_CONN_LIST))
3063 continue;
3064 err = get_connection_index(codec, dig_nid,
3065 spec->autocfg.dig_in_pin);
3066 if (err >= 0) {
3067 spec->dig_in_nid = dig_nid;
3068 break;
3069 }
3070 }
Harald Welted949cac2008-09-09 15:56:01 +08003071}
3072
Lydia Wang6369bcf2009-10-10 19:08:31 +08003073static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
3074 int offset, int num_steps, int step_size)
3075{
3076 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
3077 (offset << AC_AMPCAP_OFFSET_SHIFT) |
3078 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
3079 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
3080 (0 << AC_AMPCAP_MUTE_SHIFT));
3081}
3082
Harald Welted949cac2008-09-09 15:56:01 +08003083static int patch_vt1708S(struct hda_codec *codec)
3084{
3085 struct via_spec *spec;
3086 int err;
3087
3088 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003089 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003090 if (spec == NULL)
3091 return -ENOMEM;
3092
Takashi Iwai620e2b22011-06-17 17:19:19 +02003093 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003094 override_mic_boost(codec, 0x1a, 0, 3, 40);
3095 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003096
Harald Welted949cac2008-09-09 15:56:01 +08003097 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003098 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003099 if (err < 0) {
3100 via_free(codec);
3101 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003102 }
3103
Takashi Iwai096a8852011-06-20 12:09:02 +02003104 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003105
Harald Welted949cac2008-09-09 15:56:01 +08003106 codec->patch_ops = via_patch_ops;
3107
Lydia Wang518bf3b2009-10-10 19:07:29 +08003108 /* correct names for VT1708BCE */
3109 if (get_codec_type(codec) == VT1708BCE) {
3110 kfree(codec->chip_name);
3111 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3112 snprintf(codec->bus->card->mixername,
3113 sizeof(codec->bus->card->mixername),
3114 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f630f2011-03-22 16:25:56 +08003115 }
Lydia Wangbc92df72011-03-23 17:56:05 +08003116 /* correct names for VT1705 */
3117 if (codec->vendor_id == 0x11064397) {
3118 kfree(codec->chip_name);
3119 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3120 snprintf(codec->bus->card->mixername,
3121 sizeof(codec->bus->card->mixername),
3122 "%s %s", codec->vendor_name, codec->chip_name);
3123 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003124 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08003125 return 0;
3126}
3127
3128/* Patch for VT1702 */
3129
Takashi Iwai096a8852011-06-20 12:09:02 +02003130static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003131 /* mixer enable */
3132 {0x1, 0xF88, 0x3},
3133 /* GPIO 0~2 */
3134 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003135 { }
3136};
3137
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003138static void set_widgets_power_state_vt1702(struct hda_codec *codec)
3139{
3140 int imux_is_smixer =
3141 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3142 unsigned int parm;
3143 /* inputs */
3144 /* PW 1/2/5 (14h/15h/18h) */
3145 parm = AC_PWRST_D3;
3146 set_pin_power_state(codec, 0x14, &parm);
3147 set_pin_power_state(codec, 0x15, &parm);
3148 set_pin_power_state(codec, 0x18, &parm);
3149 if (imux_is_smixer)
3150 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
3151 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
3152 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3153 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
3154 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3155 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
3156
3157 /* outputs */
3158 /* PW 3/4 (16h/17h) */
3159 parm = AC_PWRST_D3;
3160 set_pin_power_state(codec, 0x17, &parm);
3161 set_pin_power_state(codec, 0x16, &parm);
3162 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
3163 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
3164 imux_is_smixer ? AC_PWRST_D0 : parm);
3165 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3166 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3167}
3168
Harald Welted949cac2008-09-09 15:56:01 +08003169static int patch_vt1702(struct hda_codec *codec)
3170{
3171 struct via_spec *spec;
3172 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003173
3174 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003175 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003176 if (spec == NULL)
3177 return -ENOMEM;
3178
Takashi Iwai620e2b22011-06-17 17:19:19 +02003179 spec->aa_mix_nid = 0x1a;
3180
Takashi Iwai12daef62011-06-18 17:45:49 +02003181 /* limit AA path volume to 0 dB */
3182 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3183 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3184 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3185 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3186 (1 << AC_AMPCAP_MUTE_SHIFT));
3187
Harald Welted949cac2008-09-09 15:56:01 +08003188 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003189 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003190 if (err < 0) {
3191 via_free(codec);
3192 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003193 }
3194
Takashi Iwai096a8852011-06-20 12:09:02 +02003195 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003196
Harald Welted949cac2008-09-09 15:56:01 +08003197 codec->patch_ops = via_patch_ops;
3198
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003199 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08003200 return 0;
3201}
3202
Lydia Wangeb7188c2009-10-10 19:08:34 +08003203/* Patch for VT1718S */
3204
Takashi Iwai096a8852011-06-20 12:09:02 +02003205static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08003206 /* Enable MW0 adjust Gain 5 */
3207 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003208 /* Enable Boost Volume backdoor */
3209 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02003210
Lydia Wangeb7188c2009-10-10 19:08:34 +08003211 { }
3212};
3213
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003214static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
3215{
3216 struct via_spec *spec = codec->spec;
3217 int imux_is_smixer;
3218 unsigned int parm;
3219 /* MUX6 (1eh) = stereo mixer */
3220 imux_is_smixer =
3221 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3222 /* inputs */
3223 /* PW 5/6/7 (29h/2ah/2bh) */
3224 parm = AC_PWRST_D3;
3225 set_pin_power_state(codec, 0x29, &parm);
3226 set_pin_power_state(codec, 0x2a, &parm);
3227 set_pin_power_state(codec, 0x2b, &parm);
3228 if (imux_is_smixer)
3229 parm = AC_PWRST_D0;
3230 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
3231 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3232 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3233 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3234 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3235
3236 /* outputs */
3237 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
3238 parm = AC_PWRST_D3;
3239 set_pin_power_state(codec, 0x27, &parm);
3240 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
3241 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
3242
3243 /* PW2 (26h), AOW2 (ah) */
3244 parm = AC_PWRST_D3;
3245 set_pin_power_state(codec, 0x26, &parm);
3246 if (spec->smart51_enabled)
3247 set_pin_power_state(codec, 0x2b, &parm);
3248 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
3249
3250 /* PW0 (24h), AOW0 (8h) */
3251 parm = AC_PWRST_D3;
3252 set_pin_power_state(codec, 0x24, &parm);
3253 if (!spec->hp_independent_mode) /* check for redirected HP */
3254 set_pin_power_state(codec, 0x28, &parm);
3255 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3256 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
3257 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
3258 imux_is_smixer ? AC_PWRST_D0 : parm);
3259
3260 /* PW1 (25h), AOW1 (9h) */
3261 parm = AC_PWRST_D3;
3262 set_pin_power_state(codec, 0x25, &parm);
3263 if (spec->smart51_enabled)
3264 set_pin_power_state(codec, 0x2a, &parm);
3265 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
3266
3267 if (spec->hp_independent_mode) {
3268 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3269 parm = AC_PWRST_D3;
3270 set_pin_power_state(codec, 0x28, &parm);
3271 snd_hda_codec_write(codec, 0x1b, 0,
3272 AC_VERB_SET_POWER_STATE, parm);
3273 snd_hda_codec_write(codec, 0x34, 0,
3274 AC_VERB_SET_POWER_STATE, parm);
3275 snd_hda_codec_write(codec, 0xc, 0,
3276 AC_VERB_SET_POWER_STATE, parm);
3277 }
3278}
3279
Takashi Iwai30b45032011-07-11 17:05:04 +02003280/* Add a connection to the primary DAC from AA-mixer for some codecs
3281 * This isn't listed from the raw info, but the chip has a secret connection.
3282 */
3283static int add_secret_dac_path(struct hda_codec *codec)
3284{
3285 struct via_spec *spec = codec->spec;
3286 int i, nums;
3287 hda_nid_t conn[8];
3288 hda_nid_t nid;
3289
3290 if (!spec->aa_mix_nid)
3291 return 0;
3292 nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
3293 ARRAY_SIZE(conn) - 1);
3294 for (i = 0; i < nums; i++) {
3295 if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
3296 return 0;
3297 }
3298
3299 /* find the primary DAC and add to the connection list */
3300 nid = codec->start_nid;
3301 for (i = 0; i < codec->num_nodes; i++, nid++) {
3302 unsigned int caps = get_wcaps(codec, nid);
3303 if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
3304 !(caps & AC_WCAP_DIGITAL)) {
3305 conn[nums++] = nid;
3306 return snd_hda_override_conn_list(codec,
3307 spec->aa_mix_nid,
3308 nums, conn);
3309 }
3310 }
3311 return 0;
3312}
3313
3314
Lydia Wangeb7188c2009-10-10 19:08:34 +08003315static int patch_vt1718S(struct hda_codec *codec)
3316{
3317 struct via_spec *spec;
3318 int err;
3319
3320 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003321 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003322 if (spec == NULL)
3323 return -ENOMEM;
3324
Takashi Iwai620e2b22011-06-17 17:19:19 +02003325 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003326 override_mic_boost(codec, 0x2b, 0, 3, 40);
3327 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003328 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003329
Lydia Wangeb7188c2009-10-10 19:08:34 +08003330 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003331 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003332 if (err < 0) {
3333 via_free(codec);
3334 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003335 }
3336
Takashi Iwai096a8852011-06-20 12:09:02 +02003337 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003338
Lydia Wangeb7188c2009-10-10 19:08:34 +08003339 codec->patch_ops = via_patch_ops;
3340
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003341 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3342
Lydia Wangeb7188c2009-10-10 19:08:34 +08003343 return 0;
3344}
Lydia Wangf3db4232009-10-10 19:08:41 +08003345
3346/* Patch for VT1716S */
3347
3348static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3349 struct snd_ctl_elem_info *uinfo)
3350{
3351 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3352 uinfo->count = 1;
3353 uinfo->value.integer.min = 0;
3354 uinfo->value.integer.max = 1;
3355 return 0;
3356}
3357
3358static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3359 struct snd_ctl_elem_value *ucontrol)
3360{
3361 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3362 int index = 0;
3363
3364 index = snd_hda_codec_read(codec, 0x26, 0,
3365 AC_VERB_GET_CONNECT_SEL, 0);
3366 if (index != -1)
3367 *ucontrol->value.integer.value = index;
3368
3369 return 0;
3370}
3371
3372static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3373 struct snd_ctl_elem_value *ucontrol)
3374{
3375 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3376 struct via_spec *spec = codec->spec;
3377 int index = *ucontrol->value.integer.value;
3378
3379 snd_hda_codec_write(codec, 0x26, 0,
3380 AC_VERB_SET_CONNECT_SEL, index);
3381 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003382 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003383 return 1;
3384}
3385
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003386static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003387 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3388 {
3389 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3390 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003391 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003392 .count = 1,
3393 .info = vt1716s_dmic_info,
3394 .get = vt1716s_dmic_get,
3395 .put = vt1716s_dmic_put,
3396 },
3397 {} /* end */
3398};
3399
3400
3401/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003402static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003403 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3404 { } /* end */
3405};
3406
Takashi Iwai096a8852011-06-20 12:09:02 +02003407static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003408 /* Enable Boost Volume backdoor */
3409 {0x1, 0xf8a, 0x80},
3410 /* don't bybass mixer */
3411 {0x1, 0xf88, 0xc0},
3412 /* Enable mono output */
3413 {0x1, 0xf90, 0x08},
3414 { }
3415};
3416
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003417static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3418{
3419 struct via_spec *spec = codec->spec;
3420 int imux_is_smixer;
3421 unsigned int parm;
3422 unsigned int mono_out, present;
3423 /* SW0 (17h) = stereo mixer */
3424 imux_is_smixer =
3425 (snd_hda_codec_read(codec, 0x17, 0,
3426 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3427 /* inputs */
3428 /* PW 1/2/5 (1ah/1bh/1eh) */
3429 parm = AC_PWRST_D3;
3430 set_pin_power_state(codec, 0x1a, &parm);
3431 set_pin_power_state(codec, 0x1b, &parm);
3432 set_pin_power_state(codec, 0x1e, &parm);
3433 if (imux_is_smixer)
3434 parm = AC_PWRST_D0;
3435 /* SW0 (17h), AIW0(13h) */
3436 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3437 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3438
3439 parm = AC_PWRST_D3;
3440 set_pin_power_state(codec, 0x1e, &parm);
3441 /* PW11 (22h) */
3442 if (spec->dmic_enabled)
3443 set_pin_power_state(codec, 0x22, &parm);
3444 else
3445 snd_hda_codec_write(codec, 0x22, 0,
3446 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3447
3448 /* SW2(26h), AIW1(14h) */
3449 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3450 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3451
3452 /* outputs */
3453 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3454 parm = AC_PWRST_D3;
3455 set_pin_power_state(codec, 0x19, &parm);
3456 /* Smart 5.1 PW2(1bh) */
3457 if (spec->smart51_enabled)
3458 set_pin_power_state(codec, 0x1b, &parm);
3459 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3460 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3461
3462 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3463 parm = AC_PWRST_D3;
3464 set_pin_power_state(codec, 0x23, &parm);
3465 /* Smart 5.1 PW1(1ah) */
3466 if (spec->smart51_enabled)
3467 set_pin_power_state(codec, 0x1a, &parm);
3468 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3469
3470 /* Smart 5.1 PW5(1eh) */
3471 if (spec->smart51_enabled)
3472 set_pin_power_state(codec, 0x1e, &parm);
3473 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3474
3475 /* Mono out */
3476 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3477 present = snd_hda_jack_detect(codec, 0x1c);
3478
3479 if (present)
3480 mono_out = 0;
3481 else {
3482 present = snd_hda_jack_detect(codec, 0x1d);
3483 if (!spec->hp_independent_mode && present)
3484 mono_out = 0;
3485 else
3486 mono_out = 1;
3487 }
3488 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3489 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3490 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3491 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3492
3493 /* PW 3/4 (1ch/1dh) */
3494 parm = AC_PWRST_D3;
3495 set_pin_power_state(codec, 0x1c, &parm);
3496 set_pin_power_state(codec, 0x1d, &parm);
3497 /* HP Independent Mode, power on AOW3 */
3498 if (spec->hp_independent_mode)
3499 snd_hda_codec_write(codec, 0x25, 0,
3500 AC_VERB_SET_POWER_STATE, parm);
3501
3502 /* force to D0 for internal Speaker */
3503 /* MW0 (16h), AOW0 (10h) */
3504 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3505 imux_is_smixer ? AC_PWRST_D0 : parm);
3506 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3507 mono_out ? AC_PWRST_D0 : parm);
3508}
3509
Lydia Wangf3db4232009-10-10 19:08:41 +08003510static int patch_vt1716S(struct hda_codec *codec)
3511{
3512 struct via_spec *spec;
3513 int err;
3514
3515 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003516 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003517 if (spec == NULL)
3518 return -ENOMEM;
3519
Takashi Iwai620e2b22011-06-17 17:19:19 +02003520 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003521 override_mic_boost(codec, 0x1a, 0, 3, 40);
3522 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003523
Lydia Wangf3db4232009-10-10 19:08:41 +08003524 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003525 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003526 if (err < 0) {
3527 via_free(codec);
3528 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003529 }
3530
Takashi Iwai096a8852011-06-20 12:09:02 +02003531 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003532
Lydia Wangf3db4232009-10-10 19:08:41 +08003533 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3534 spec->num_mixers++;
3535
3536 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3537
3538 codec->patch_ops = via_patch_ops;
3539
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003540 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003541 return 0;
3542}
Lydia Wang25eaba22009-10-10 19:08:43 +08003543
3544/* for vt2002P */
3545
Takashi Iwai096a8852011-06-20 12:09:02 +02003546static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003547 /* Class-D speaker related verbs */
3548 {0x1, 0xfe0, 0x4},
3549 {0x1, 0xfe9, 0x80},
3550 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003551 /* Enable Boost Volume backdoor */
3552 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003553 /* Enable AOW0 to MW9 */
3554 {0x1, 0xfb8, 0x88},
3555 { }
3556};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003557
Takashi Iwai096a8852011-06-20 12:09:02 +02003558static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003559 /* Enable Boost Volume backdoor */
3560 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003561 /* Enable AOW0 to MW9 */
3562 {0x1, 0xfb8, 0x88},
3563 { }
3564};
Lydia Wang25eaba22009-10-10 19:08:43 +08003565
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003566static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3567{
3568 struct via_spec *spec = codec->spec;
3569 int imux_is_smixer;
3570 unsigned int parm;
3571 unsigned int present;
3572 /* MUX9 (1eh) = stereo mixer */
3573 imux_is_smixer =
3574 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3575 /* inputs */
3576 /* PW 5/6/7 (29h/2ah/2bh) */
3577 parm = AC_PWRST_D3;
3578 set_pin_power_state(codec, 0x29, &parm);
3579 set_pin_power_state(codec, 0x2a, &parm);
3580 set_pin_power_state(codec, 0x2b, &parm);
3581 parm = AC_PWRST_D0;
3582 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3583 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3584 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3585 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3586 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3587
3588 /* outputs */
3589 /* AOW0 (8h)*/
3590 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3591
Lydia Wang118909562011-03-23 17:57:34 +08003592 if (spec->codec_type == VT1802) {
3593 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3594 parm = AC_PWRST_D3;
3595 set_pin_power_state(codec, 0x28, &parm);
3596 snd_hda_codec_write(codec, 0x18, 0,
3597 AC_VERB_SET_POWER_STATE, parm);
3598 snd_hda_codec_write(codec, 0x38, 0,
3599 AC_VERB_SET_POWER_STATE, parm);
3600 } else {
3601 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3602 parm = AC_PWRST_D3;
3603 set_pin_power_state(codec, 0x26, &parm);
3604 snd_hda_codec_write(codec, 0x1c, 0,
3605 AC_VERB_SET_POWER_STATE, parm);
3606 snd_hda_codec_write(codec, 0x37, 0,
3607 AC_VERB_SET_POWER_STATE, parm);
3608 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003609
Lydia Wang118909562011-03-23 17:57:34 +08003610 if (spec->codec_type == VT1802) {
3611 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3612 parm = AC_PWRST_D3;
3613 set_pin_power_state(codec, 0x25, &parm);
3614 snd_hda_codec_write(codec, 0x15, 0,
3615 AC_VERB_SET_POWER_STATE, parm);
3616 snd_hda_codec_write(codec, 0x35, 0,
3617 AC_VERB_SET_POWER_STATE, parm);
3618 } else {
3619 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3620 parm = AC_PWRST_D3;
3621 set_pin_power_state(codec, 0x25, &parm);
3622 snd_hda_codec_write(codec, 0x19, 0,
3623 AC_VERB_SET_POWER_STATE, parm);
3624 snd_hda_codec_write(codec, 0x35, 0,
3625 AC_VERB_SET_POWER_STATE, parm);
3626 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003627
3628 if (spec->hp_independent_mode)
3629 snd_hda_codec_write(codec, 0x9, 0,
3630 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3631
3632 /* Class-D */
3633 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3634 present = snd_hda_jack_detect(codec, 0x25);
3635
3636 parm = AC_PWRST_D3;
3637 set_pin_power_state(codec, 0x24, &parm);
3638 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003639 if (spec->codec_type == VT1802)
3640 snd_hda_codec_write(codec, 0x14, 0,
3641 AC_VERB_SET_POWER_STATE, parm);
3642 else
3643 snd_hda_codec_write(codec, 0x18, 0,
3644 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003645 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3646
3647 /* Mono Out */
3648 present = snd_hda_jack_detect(codec, 0x26);
3649
3650 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003651 if (spec->codec_type == VT1802) {
3652 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3653 snd_hda_codec_write(codec, 0x33, 0,
3654 AC_VERB_SET_POWER_STATE, parm);
3655 snd_hda_codec_write(codec, 0x1c, 0,
3656 AC_VERB_SET_POWER_STATE, parm);
3657 snd_hda_codec_write(codec, 0x3c, 0,
3658 AC_VERB_SET_POWER_STATE, parm);
3659 } else {
3660 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3661 snd_hda_codec_write(codec, 0x31, 0,
3662 AC_VERB_SET_POWER_STATE, parm);
3663 snd_hda_codec_write(codec, 0x17, 0,
3664 AC_VERB_SET_POWER_STATE, parm);
3665 snd_hda_codec_write(codec, 0x3b, 0,
3666 AC_VERB_SET_POWER_STATE, parm);
3667 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003668 /* MW9 (21h) */
3669 if (imux_is_smixer || !is_aa_path_mute(codec))
3670 snd_hda_codec_write(codec, 0x21, 0,
3671 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3672 else
3673 snd_hda_codec_write(codec, 0x21, 0,
3674 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3675}
Lydia Wang25eaba22009-10-10 19:08:43 +08003676
3677/* patch for vt2002P */
3678static int patch_vt2002P(struct hda_codec *codec)
3679{
3680 struct via_spec *spec;
3681 int err;
3682
3683 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003684 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003685 if (spec == NULL)
3686 return -ENOMEM;
3687
Takashi Iwai620e2b22011-06-17 17:19:19 +02003688 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003689 override_mic_boost(codec, 0x2b, 0, 3, 40);
3690 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003691 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003692
Lydia Wang25eaba22009-10-10 19:08:43 +08003693 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003694 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003695 if (err < 0) {
3696 via_free(codec);
3697 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003698 }
3699
Lydia Wang118909562011-03-23 17:57:34 +08003700 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003701 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003702 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003703 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003704
Lydia Wang25eaba22009-10-10 19:08:43 +08003705 codec->patch_ops = via_patch_ops;
3706
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003707 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003708 return 0;
3709}
Lydia Wangab6734e2009-10-10 19:08:46 +08003710
3711/* for vt1812 */
3712
Takashi Iwai096a8852011-06-20 12:09:02 +02003713static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003714 /* Enable Boost Volume backdoor */
3715 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003716 /* Enable AOW0 to MW9 */
3717 {0x1, 0xfb8, 0xa8},
3718 { }
3719};
3720
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003721static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3722{
3723 struct via_spec *spec = codec->spec;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003724 unsigned int parm;
3725 unsigned int present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003726 /* inputs */
3727 /* PW 5/6/7 (29h/2ah/2bh) */
3728 parm = AC_PWRST_D3;
3729 set_pin_power_state(codec, 0x29, &parm);
3730 set_pin_power_state(codec, 0x2a, &parm);
3731 set_pin_power_state(codec, 0x2b, &parm);
3732 parm = AC_PWRST_D0;
3733 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3734 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3735 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3736 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3737 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3738
3739 /* outputs */
3740 /* AOW0 (8h)*/
3741 snd_hda_codec_write(codec, 0x8, 0,
3742 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3743
3744 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3745 parm = AC_PWRST_D3;
3746 set_pin_power_state(codec, 0x28, &parm);
3747 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3748 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3749
3750 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3751 parm = AC_PWRST_D3;
3752 set_pin_power_state(codec, 0x25, &parm);
3753 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3754 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3755 if (spec->hp_independent_mode)
3756 snd_hda_codec_write(codec, 0x9, 0,
3757 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3758
3759 /* Internal Speaker */
3760 /* PW0 (24h), MW0(14h), MUX0(34h) */
3761 present = snd_hda_jack_detect(codec, 0x25);
3762
3763 parm = AC_PWRST_D3;
3764 set_pin_power_state(codec, 0x24, &parm);
3765 if (present) {
3766 snd_hda_codec_write(codec, 0x14, 0,
3767 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3768 snd_hda_codec_write(codec, 0x34, 0,
3769 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3770 } else {
3771 snd_hda_codec_write(codec, 0x14, 0,
3772 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3773 snd_hda_codec_write(codec, 0x34, 0,
3774 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3775 }
3776
3777
3778 /* Mono Out */
3779 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3780 present = snd_hda_jack_detect(codec, 0x28);
3781
3782 parm = AC_PWRST_D3;
3783 set_pin_power_state(codec, 0x31, &parm);
3784 if (present) {
3785 snd_hda_codec_write(codec, 0x1c, 0,
3786 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3787 snd_hda_codec_write(codec, 0x3c, 0,
3788 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3789 snd_hda_codec_write(codec, 0x3e, 0,
3790 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3791 } else {
3792 snd_hda_codec_write(codec, 0x1c, 0,
3793 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3794 snd_hda_codec_write(codec, 0x3c, 0,
3795 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3796 snd_hda_codec_write(codec, 0x3e, 0,
3797 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3798 }
3799
3800 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3801 parm = AC_PWRST_D3;
3802 set_pin_power_state(codec, 0x33, &parm);
3803 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3804 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3805
3806}
Lydia Wangab6734e2009-10-10 19:08:46 +08003807
3808/* patch for vt1812 */
3809static int patch_vt1812(struct hda_codec *codec)
3810{
3811 struct via_spec *spec;
3812 int err;
3813
3814 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003815 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003816 if (spec == NULL)
3817 return -ENOMEM;
3818
Takashi Iwai620e2b22011-06-17 17:19:19 +02003819 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003820 override_mic_boost(codec, 0x2b, 0, 3, 40);
3821 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003822 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003823
Lydia Wangab6734e2009-10-10 19:08:46 +08003824 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003825 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003826 if (err < 0) {
3827 via_free(codec);
3828 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003829 }
3830
Takashi Iwai096a8852011-06-20 12:09:02 +02003831 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003832
Lydia Wangab6734e2009-10-10 19:08:46 +08003833 codec->patch_ops = via_patch_ops;
3834
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003835 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003836 return 0;
3837}
3838
Joseph Chanc577b8a2006-11-29 15:29:40 +01003839/*
3840 * patch entries
3841 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003842static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003843 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3844 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3845 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3846 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3847 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003848 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003849 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003850 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003851 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003852 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003853 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003854 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003855 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003856 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003857 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003858 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003859 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003860 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003861 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003862 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003863 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003864 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003865 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003866 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003867 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003868 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003869 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003870 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003871 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003872 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003873 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003874 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003875 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003876 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003877 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003878 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003879 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003880 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003881 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003882 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003883 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003884 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003885 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003886 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003887 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003888 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003889 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003890 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003891 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003892 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003893 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003894 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003895 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003896 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003897 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003898 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003899 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003900 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003901 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003902 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003903 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003904 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003905 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003906 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003907 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003908 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003909 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003910 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003911 { .id = 0x11060428, .name = "VT1718S",
3912 .patch = patch_vt1718S},
3913 { .id = 0x11064428, .name = "VT1718S",
3914 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003915 { .id = 0x11060441, .name = "VT2020",
3916 .patch = patch_vt1718S},
3917 { .id = 0x11064441, .name = "VT1828S",
3918 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003919 { .id = 0x11060433, .name = "VT1716S",
3920 .patch = patch_vt1716S},
3921 { .id = 0x1106a721, .name = "VT1716S",
3922 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003923 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3924 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003925 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003926 { .id = 0x11060440, .name = "VT1818S",
3927 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003928 { .id = 0x11060446, .name = "VT1802",
3929 .patch = patch_vt2002P},
3930 { .id = 0x11068446, .name = "VT1802",
3931 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003932 {} /* terminator */
3933};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003934
3935MODULE_ALIAS("snd-hda-codec-id:1106*");
3936
3937static struct hda_codec_preset_list via_list = {
3938 .preset = snd_hda_preset_via,
3939 .owner = THIS_MODULE,
3940};
3941
3942MODULE_LICENSE("GPL");
3943MODULE_DESCRIPTION("VIA HD-audio codec");
3944
3945static int __init patch_via_init(void)
3946{
3947 return snd_hda_add_codec_preset(&via_list);
3948}
3949
3950static void __exit patch_via_exit(void)
3951{
3952 snd_hda_delete_codec_preset(&via_list);
3953}
3954
3955module_init(patch_via_init)
3956module_exit(patch_via_exit)