blob: ab56866cc94e495f1a13847d662e691afc5edb90 [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
2211 if (!spec->aa_mix_nid || !spec->out_mix_path.depth)
2212 return 0; /* no loopback switching available */
2213 if (!via_clone_control(spec, &via_aamix_ctl_enum))
2214 return -ENOMEM;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002215 return 0;
2216}
2217
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002218/* look for ADCs */
2219static int via_fill_adcs(struct hda_codec *codec)
2220{
2221 struct via_spec *spec = codec->spec;
2222 hda_nid_t nid = codec->start_nid;
2223 int i;
2224
2225 for (i = 0; i < codec->num_nodes; i++, nid++) {
2226 unsigned int wcaps = get_wcaps(codec, nid);
2227 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2228 continue;
2229 if (wcaps & AC_WCAP_DIGITAL)
2230 continue;
2231 if (!(wcaps & AC_WCAP_CONN_LIST))
2232 continue;
2233 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2234 return -ENOMEM;
2235 spec->adc_nids[spec->num_adc_nids++] = nid;
2236 }
2237 return 0;
2238}
2239
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002240/* input-src control */
2241static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2242 struct snd_ctl_elem_info *uinfo)
2243{
2244 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2245 struct via_spec *spec = codec->spec;
2246
2247 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2248 uinfo->count = 1;
2249 uinfo->value.enumerated.items = spec->num_inputs;
2250 if (uinfo->value.enumerated.item >= spec->num_inputs)
2251 uinfo->value.enumerated.item = spec->num_inputs - 1;
2252 strcpy(uinfo->value.enumerated.name,
2253 spec->inputs[uinfo->value.enumerated.item].label);
2254 return 0;
2255}
2256
2257static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2258 struct snd_ctl_elem_value *ucontrol)
2259{
2260 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2261 struct via_spec *spec = codec->spec;
2262 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2263
2264 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2265 return 0;
2266}
2267
2268static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2269 struct snd_ctl_elem_value *ucontrol)
2270{
2271 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2272 struct via_spec *spec = codec->spec;
2273 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2274 hda_nid_t mux;
2275 int cur;
2276
2277 cur = ucontrol->value.enumerated.item[0];
2278 if (cur < 0 || cur >= spec->num_inputs)
2279 return -EINVAL;
2280 if (spec->cur_mux[idx] == cur)
2281 return 0;
2282 spec->cur_mux[idx] = cur;
2283 if (spec->dyn_adc_switch) {
2284 int adc_idx = spec->inputs[cur].adc_idx;
2285 mux = spec->mux_nids[adc_idx];
2286 via_dyn_adc_pcm_resetup(codec, cur);
2287 } else {
2288 mux = spec->mux_nids[idx];
2289 if (snd_BUG_ON(!mux))
2290 return -EINVAL;
2291 }
2292
2293 if (mux) {
2294 /* switch to D0 beofre change index */
2295 if (snd_hda_codec_read(codec, mux, 0,
2296 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2297 snd_hda_codec_write(codec, mux, 0,
2298 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2299 snd_hda_codec_write(codec, mux, 0,
2300 AC_VERB_SET_CONNECT_SEL,
2301 spec->inputs[cur].mux_idx);
2302 }
2303
2304 /* update jack power state */
2305 set_widgets_power_state(codec);
2306 return 0;
2307}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002308
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002309static const struct snd_kcontrol_new via_input_src_ctl = {
2310 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2311 /* The multiple "Capture Source" controls confuse alsamixer
2312 * So call somewhat different..
2313 */
2314 /* .name = "Capture Source", */
2315 .name = "Input Source",
2316 .info = via_mux_enum_info,
2317 .get = via_mux_enum_get,
2318 .put = via_mux_enum_put,
2319};
2320
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002321static int create_input_src_ctls(struct hda_codec *codec, int count)
2322{
2323 struct via_spec *spec = codec->spec;
2324 struct snd_kcontrol_new *knew;
2325
2326 if (spec->num_inputs <= 1 || !count)
2327 return 0; /* no need for single src */
2328
2329 knew = via_clone_control(spec, &via_input_src_ctl);
2330 if (!knew)
2331 return -ENOMEM;
2332 knew->count = count;
2333 return 0;
2334}
2335
2336/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002337static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2338{
2339 struct hda_amp_list *list;
2340
2341 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2342 return;
2343 list = spec->loopback_list + spec->num_loopbacks;
2344 list->nid = mix;
2345 list->dir = HDA_INPUT;
2346 list->idx = idx;
2347 spec->num_loopbacks++;
2348 spec->loopback.amplist = spec->loopback_list;
2349}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002350
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002351static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002352 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002353{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002354 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002355}
2356
2357/* add the input-route to the given pin */
2358static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002359{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002360 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002361 int c, idx;
2362
2363 spec->inputs[spec->num_inputs].adc_idx = -1;
2364 spec->inputs[spec->num_inputs].pin = pin;
2365 for (c = 0; c < spec->num_adc_nids; c++) {
2366 if (spec->mux_nids[c]) {
2367 idx = get_connection_index(codec, spec->mux_nids[c],
2368 pin);
2369 if (idx < 0)
2370 continue;
2371 spec->inputs[spec->num_inputs].mux_idx = idx;
2372 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002373 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002374 continue;
2375 }
2376 spec->inputs[spec->num_inputs].adc_idx = c;
2377 /* Can primary ADC satisfy all inputs? */
2378 if (!spec->dyn_adc_switch &&
2379 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2380 snd_printd(KERN_INFO
2381 "via: dynamic ADC switching enabled\n");
2382 spec->dyn_adc_switch = 1;
2383 }
2384 return true;
2385 }
2386 return false;
2387}
2388
2389static int get_mux_nids(struct hda_codec *codec);
2390
2391/* parse input-routes; fill ADCs, MUXs and input-src entries */
2392static int parse_analog_inputs(struct hda_codec *codec)
2393{
2394 struct via_spec *spec = codec->spec;
2395 const struct auto_pin_cfg *cfg = &spec->autocfg;
2396 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002397
2398 err = via_fill_adcs(codec);
2399 if (err < 0)
2400 return err;
2401 err = get_mux_nids(codec);
2402 if (err < 0)
2403 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002404
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002405 /* fill all input-routes */
2406 for (i = 0; i < cfg->num_inputs; i++) {
2407 if (add_input_route(codec, cfg->inputs[i].pin))
2408 spec->inputs[spec->num_inputs++].label =
2409 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002410 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002411
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002412 /* check for internal loopback recording */
2413 if (spec->aa_mix_nid &&
2414 add_input_route(codec, spec->aa_mix_nid))
2415 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2416
2417 return 0;
2418}
2419
2420/* create analog-loopback volume/switch controls */
2421static int create_loopback_ctls(struct hda_codec *codec)
2422{
2423 struct via_spec *spec = codec->spec;
2424 const struct auto_pin_cfg *cfg = &spec->autocfg;
2425 const char *prev_label = NULL;
2426 int type_idx = 0;
2427 int i, j, err, idx;
2428
2429 if (!spec->aa_mix_nid)
2430 return 0;
2431
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002432 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002433 hda_nid_t pin = cfg->inputs[i].pin;
2434 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2435
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002436 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002437 type_idx++;
2438 else
2439 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002440 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002441 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2442 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002443 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002444 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002445 if (err < 0)
2446 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002447 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002448 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002449
2450 /* remember the label for smart51 control */
2451 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002452 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002453 spec->smart51_idxs[j] = idx;
2454 spec->smart51_labels[j] = label;
2455 break;
2456 }
2457 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002458 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002459 return 0;
2460}
2461
2462/* create mic-boost controls (if present) */
2463static int create_mic_boost_ctls(struct hda_codec *codec)
2464{
2465 struct via_spec *spec = codec->spec;
2466 const struct auto_pin_cfg *cfg = &spec->autocfg;
2467 int i, err;
2468
2469 for (i = 0; i < cfg->num_inputs; i++) {
2470 hda_nid_t pin = cfg->inputs[i].pin;
2471 unsigned int caps;
2472 const char *label;
2473 char name[32];
2474
2475 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2476 continue;
2477 caps = query_amp_caps(codec, pin, HDA_INPUT);
2478 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2479 continue;
2480 label = hda_get_autocfg_input_label(codec, cfg, i);
2481 snprintf(name, sizeof(name), "%s Boost Volume", label);
2482 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2483 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2484 if (err < 0)
2485 return err;
2486 }
2487 return 0;
2488}
2489
2490/* create capture and input-src controls for multiple streams */
2491static int create_multi_adc_ctls(struct hda_codec *codec)
2492{
2493 struct via_spec *spec = codec->spec;
2494 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002495
2496 /* create capture mixer elements */
2497 for (i = 0; i < spec->num_adc_nids; i++) {
2498 hda_nid_t adc = spec->adc_nids[i];
2499 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2500 "Capture Volume", i,
2501 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2502 HDA_INPUT));
2503 if (err < 0)
2504 return err;
2505 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2506 "Capture Switch", i,
2507 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2508 HDA_INPUT));
2509 if (err < 0)
2510 return err;
2511 }
2512
2513 /* input-source control */
2514 for (i = 0; i < spec->num_adc_nids; i++)
2515 if (!spec->mux_nids[i])
2516 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002517 err = create_input_src_ctls(codec, i);
2518 if (err < 0)
2519 return err;
2520 return 0;
2521}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002522
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002523/* bind capture volume/switch */
2524static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2525 HDA_BIND_VOL("Capture Volume", 0);
2526static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2527 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002528
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002529static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2530 struct hda_ctl_ops *ops)
2531{
2532 struct hda_bind_ctls *ctl;
2533 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002534
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002535 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2536 if (!ctl)
2537 return -ENOMEM;
2538 ctl->ops = ops;
2539 for (i = 0; i < spec->num_adc_nids; i++)
2540 ctl->values[i] =
2541 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2542 *ctl_ret = ctl;
2543 return 0;
2544}
2545
2546/* create capture and input-src controls for dynamic ADC-switch case */
2547static int create_dyn_adc_ctls(struct hda_codec *codec)
2548{
2549 struct via_spec *spec = codec->spec;
2550 struct snd_kcontrol_new *knew;
2551 int err;
2552
2553 /* set up the bind capture ctls */
2554 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2555 if (err < 0)
2556 return err;
2557 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2558 if (err < 0)
2559 return err;
2560
2561 /* create capture mixer elements */
2562 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2563 if (!knew)
2564 return -ENOMEM;
2565 knew->private_value = (long)spec->bind_cap_vol;
2566
2567 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2568 if (!knew)
2569 return -ENOMEM;
2570 knew->private_value = (long)spec->bind_cap_sw;
2571
2572 /* input-source control */
2573 err = create_input_src_ctls(codec, 1);
2574 if (err < 0)
2575 return err;
2576 return 0;
2577}
2578
2579/* parse and create capture-related stuff */
2580static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2581{
2582 struct via_spec *spec = codec->spec;
2583 int err;
2584
2585 err = parse_analog_inputs(codec);
2586 if (err < 0)
2587 return err;
2588 if (spec->dyn_adc_switch)
2589 err = create_dyn_adc_ctls(codec);
2590 else
2591 err = create_multi_adc_ctls(codec);
2592 if (err < 0)
2593 return err;
2594 err = create_loopback_ctls(codec);
2595 if (err < 0)
2596 return err;
2597 err = create_mic_boost_ctls(codec);
2598 if (err < 0)
2599 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002600 return 0;
2601}
2602
Harald Welte76d9b0d2008-09-09 15:50:37 +08002603static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2604{
2605 unsigned int def_conf;
2606 unsigned char seqassoc;
2607
Takashi Iwai2f334f92009-02-20 14:37:42 +01002608 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002609 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2610 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002611 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2612 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2613 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2614 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002615 }
2616
2617 return;
2618}
2619
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002620static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002621 struct snd_ctl_elem_value *ucontrol)
2622{
2623 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2624 struct via_spec *spec = codec->spec;
2625
2626 if (spec->codec_type != VT1708)
2627 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002628 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002629 return 0;
2630}
2631
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002632static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002633 struct snd_ctl_elem_value *ucontrol)
2634{
2635 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2636 struct via_spec *spec = codec->spec;
Takashi Iwai187d3332011-11-24 16:33:09 +01002637 int val;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002638
2639 if (spec->codec_type != VT1708)
2640 return 0;
Takashi Iwai187d3332011-11-24 16:33:09 +01002641 val = !!ucontrol->value.integer.value[0];
2642 if (spec->vt1708_jack_detect == val)
2643 return 0;
2644 spec->vt1708_jack_detect = val;
2645 if (spec->vt1708_jack_detect &&
2646 snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") != 1) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002647 mute_aa_path(codec, 1);
2648 notify_aa_path_ctls(codec);
2649 }
Takashi Iwai187d3332011-11-24 16:33:09 +01002650 via_hp_automute(codec);
2651 vt1708_update_hp_work(spec);
2652 return 1;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002653}
2654
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002655static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2656 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2657 .name = "Jack Detect",
2658 .count = 1,
2659 .info = snd_ctl_boolean_mono_info,
2660 .get = vt1708_jack_detect_get,
2661 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002662};
2663
Takashi Iwai12daef62011-06-18 17:45:49 +02002664static void fill_dig_outs(struct hda_codec *codec);
2665static void fill_dig_in(struct hda_codec *codec);
2666
2667static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002668{
2669 struct via_spec *spec = codec->spec;
2670 int err;
2671
2672 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2673 if (err < 0)
2674 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002675 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002676 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002677
Takashi Iwai4a796162011-06-17 17:53:38 +02002678 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002679 if (err < 0)
2680 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002681 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002682 if (err < 0)
2683 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002684 err = via_auto_create_speaker_ctls(codec);
2685 if (err < 0)
2686 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002687 err = via_auto_create_loopback_switch(codec);
2688 if (err < 0)
2689 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002690 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002691 if (err < 0)
2692 return err;
2693
2694 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2695
Takashi Iwai12daef62011-06-18 17:45:49 +02002696 fill_dig_outs(codec);
2697 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002698
Takashi Iwai603c4012008-07-30 15:01:44 +02002699 if (spec->kctls.list)
2700 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002701
Joseph Chanc577b8a2006-11-29 15:29:40 +01002702
Takashi Iwai3214b962011-07-18 12:49:25 +02002703 if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002704 err = via_hp_build(codec);
2705 if (err < 0)
2706 return err;
2707 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002708
Takashi Iwaif4a78282011-06-17 18:46:48 +02002709 err = via_smart51_build(codec);
2710 if (err < 0)
2711 return err;
2712
Takashi Iwai5d417622011-06-20 11:32:27 +02002713 /* assign slave outs */
2714 if (spec->slave_dig_outs[0])
2715 codec->slave_dig_outs = spec->slave_dig_outs;
2716
Joseph Chanc577b8a2006-11-29 15:29:40 +01002717 return 1;
2718}
2719
Takashi Iwai5d417622011-06-20 11:32:27 +02002720static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002721{
Lydia Wang25eaba22009-10-10 19:08:43 +08002722 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002723 if (spec->multiout.dig_out_nid)
2724 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2725 if (spec->slave_dig_outs[0])
2726 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2727}
Lydia Wang25eaba22009-10-10 19:08:43 +08002728
Takashi Iwai5d417622011-06-20 11:32:27 +02002729static void via_auto_init_dig_in(struct hda_codec *codec)
2730{
2731 struct via_spec *spec = codec->spec;
2732 if (!spec->dig_in_nid)
2733 return;
2734 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2735 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2736}
2737
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002738/* initialize the unsolicited events */
2739static void via_auto_init_unsol_event(struct hda_codec *codec)
2740{
2741 struct via_spec *spec = codec->spec;
2742 struct auto_pin_cfg *cfg = &spec->autocfg;
2743 unsigned int ev;
2744 int i;
2745
2746 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002747 snd_hda_jack_detect_enable(codec, cfg->hp_pins[0],
2748 VIA_HP_EVENT | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002749
2750 if (cfg->speaker_pins[0])
2751 ev = VIA_LINE_EVENT;
2752 else
2753 ev = 0;
2754 for (i = 0; i < cfg->line_outs; i++) {
2755 if (cfg->line_out_pins[i] &&
2756 is_jack_detectable(codec, cfg->line_out_pins[i]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002757 snd_hda_jack_detect_enable(codec, cfg->line_out_pins[i],
2758 ev | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002759 }
2760
2761 for (i = 0; i < cfg->num_inputs; i++) {
2762 if (is_jack_detectable(codec, cfg->inputs[i].pin))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002763 snd_hda_jack_detect_enable(codec, cfg->inputs[i].pin,
2764 VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002765 }
2766}
2767
Takashi Iwai5d417622011-06-20 11:32:27 +02002768static int via_init(struct hda_codec *codec)
2769{
2770 struct via_spec *spec = codec->spec;
2771 int i;
2772
2773 for (i = 0; i < spec->num_iverbs; i++)
2774 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2775
Joseph Chanc577b8a2006-11-29 15:29:40 +01002776 via_auto_init_multi_out(codec);
2777 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002778 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002779 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002780 via_auto_init_dig_outs(codec);
2781 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002782
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002783 via_auto_init_unsol_event(codec);
2784
2785 via_hp_automute(codec);
Takashi Iwai187d3332011-11-24 16:33:09 +01002786 vt1708_update_hp_work(spec);
Takashi Iwai01a61e12011-10-28 00:03:22 +02002787 snd_hda_jack_report_sync(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002788
Joseph Chanc577b8a2006-11-29 15:29:40 +01002789 return 0;
2790}
2791
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002792static void vt1708_update_hp_jack_state(struct work_struct *work)
2793{
2794 struct via_spec *spec = container_of(work, struct via_spec,
2795 vt1708_hp_work.work);
2796 if (spec->codec_type != VT1708)
2797 return;
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002798 snd_hda_jack_set_dirty_all(spec->codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002799 /* if jack state toggled */
2800 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002801 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002802 spec->vt1708_hp_present ^= 1;
2803 via_hp_automute(spec->codec);
2804 }
Takashi Iwai187d3332011-11-24 16:33:09 +01002805 if (spec->vt1708_jack_detect)
2806 schedule_delayed_work(&spec->vt1708_hp_work,
2807 msecs_to_jiffies(100));
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002808}
2809
Takashi Iwai337b9d02009-07-07 18:18:59 +02002810static int get_mux_nids(struct hda_codec *codec)
2811{
2812 struct via_spec *spec = codec->spec;
2813 hda_nid_t nid, conn[8];
2814 unsigned int type;
2815 int i, n;
2816
2817 for (i = 0; i < spec->num_adc_nids; i++) {
2818 nid = spec->adc_nids[i];
2819 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002820 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002821 if (type == AC_WID_PIN)
2822 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002823 n = snd_hda_get_connections(codec, nid, conn,
2824 ARRAY_SIZE(conn));
2825 if (n <= 0)
2826 break;
2827 if (n > 1) {
2828 spec->mux_nids[i] = nid;
2829 break;
2830 }
2831 nid = conn[0];
2832 }
2833 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002834 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002835}
2836
Joseph Chanc577b8a2006-11-29 15:29:40 +01002837static int patch_vt1708(struct hda_codec *codec)
2838{
2839 struct via_spec *spec;
2840 int err;
2841
2842 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002843 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002844 if (spec == NULL)
2845 return -ENOMEM;
2846
Takashi Iwai620e2b22011-06-17 17:19:19 +02002847 spec->aa_mix_nid = 0x17;
2848
Takashi Iwai12daef62011-06-18 17:45:49 +02002849 /* Add HP and CD pin config connect bit re-config action */
2850 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2851 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2852
Joseph Chanc577b8a2006-11-29 15:29:40 +01002853 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002854 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002855 if (err < 0) {
2856 via_free(codec);
2857 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002858 }
2859
Takashi Iwai12daef62011-06-18 17:45:49 +02002860 /* add jack detect on/off control */
2861 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2862 return -ENOMEM;
2863
Takashi Iwaibc9b562382008-05-23 17:50:27 +02002864 /* disable 32bit format on VT1708 */
2865 if (codec->vendor_id == 0x11061708)
2866 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002867
Lydia Wange322a362011-06-29 13:52:02 +08002868 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2869
Joseph Chanc577b8a2006-11-29 15:29:40 +01002870 codec->patch_ops = via_patch_ops;
2871
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002872 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002873 return 0;
2874}
2875
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002876static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002877{
2878 struct via_spec *spec;
2879 int err;
2880
2881 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002882 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002883 if (spec == NULL)
2884 return -ENOMEM;
2885
Takashi Iwai620e2b22011-06-17 17:19:19 +02002886 spec->aa_mix_nid = 0x18;
2887
Takashi Iwai12daef62011-06-18 17:45:49 +02002888 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002889 if (err < 0) {
2890 via_free(codec);
2891 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002892 }
2893
Joseph Chanc577b8a2006-11-29 15:29:40 +01002894 codec->patch_ops = via_patch_ops;
2895
Josepch Chanf7278fd2007-12-13 16:40:40 +01002896 return 0;
2897}
2898
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002899static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2900{
2901 struct via_spec *spec = codec->spec;
2902 int imux_is_smixer;
2903 unsigned int parm;
2904 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002905 if ((spec->codec_type != VT1708B_4CH) &&
2906 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002907 is_8ch = 1;
2908
2909 /* SW0 (17h) = stereo mixer */
2910 imux_is_smixer =
2911 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2912 == ((spec->codec_type == VT1708S) ? 5 : 0));
2913 /* inputs */
2914 /* PW 1/2/5 (1ah/1bh/1eh) */
2915 parm = AC_PWRST_D3;
2916 set_pin_power_state(codec, 0x1a, &parm);
2917 set_pin_power_state(codec, 0x1b, &parm);
2918 set_pin_power_state(codec, 0x1e, &parm);
2919 if (imux_is_smixer)
2920 parm = AC_PWRST_D0;
2921 /* SW0 (17h), AIW 0/1 (13h/14h) */
2922 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2923 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2924 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2925
2926 /* outputs */
2927 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2928 parm = AC_PWRST_D3;
2929 set_pin_power_state(codec, 0x19, &parm);
2930 if (spec->smart51_enabled)
2931 set_pin_power_state(codec, 0x1b, &parm);
2932 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2933 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2934
2935 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2936 if (is_8ch) {
2937 parm = AC_PWRST_D3;
2938 set_pin_power_state(codec, 0x22, &parm);
2939 if (spec->smart51_enabled)
2940 set_pin_power_state(codec, 0x1a, &parm);
2941 snd_hda_codec_write(codec, 0x26, 0,
2942 AC_VERB_SET_POWER_STATE, parm);
2943 snd_hda_codec_write(codec, 0x24, 0,
2944 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002945 } else if (codec->vendor_id == 0x11064397) {
2946 /* PW7(23h), SW2(27h), AOW2(25h) */
2947 parm = AC_PWRST_D3;
2948 set_pin_power_state(codec, 0x23, &parm);
2949 if (spec->smart51_enabled)
2950 set_pin_power_state(codec, 0x1a, &parm);
2951 snd_hda_codec_write(codec, 0x27, 0,
2952 AC_VERB_SET_POWER_STATE, parm);
2953 snd_hda_codec_write(codec, 0x25, 0,
2954 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002955 }
2956
2957 /* PW 3/4/7 (1ch/1dh/23h) */
2958 parm = AC_PWRST_D3;
2959 /* force to D0 for internal Speaker */
2960 set_pin_power_state(codec, 0x1c, &parm);
2961 set_pin_power_state(codec, 0x1d, &parm);
2962 if (is_8ch)
2963 set_pin_power_state(codec, 0x23, &parm);
2964
2965 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2966 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2967 imux_is_smixer ? AC_PWRST_D0 : parm);
2968 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2969 if (is_8ch) {
2970 snd_hda_codec_write(codec, 0x25, 0,
2971 AC_VERB_SET_POWER_STATE, parm);
2972 snd_hda_codec_write(codec, 0x27, 0,
2973 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002974 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2975 snd_hda_codec_write(codec, 0x25, 0,
2976 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002977}
2978
Lydia Wang518bf3b2009-10-10 19:07:29 +08002979static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002980static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002981{
2982 struct via_spec *spec;
2983 int err;
2984
Lydia Wang518bf3b2009-10-10 19:07:29 +08002985 if (get_codec_type(codec) == VT1708BCE)
2986 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002987
Josepch Chanf7278fd2007-12-13 16:40:40 +01002988 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002989 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002990 if (spec == NULL)
2991 return -ENOMEM;
2992
Takashi Iwai620e2b22011-06-17 17:19:19 +02002993 spec->aa_mix_nid = 0x16;
2994
Josepch Chanf7278fd2007-12-13 16:40:40 +01002995 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002996 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002997 if (err < 0) {
2998 via_free(codec);
2999 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003000 }
3001
Josepch Chanf7278fd2007-12-13 16:40:40 +01003002 codec->patch_ops = via_patch_ops;
3003
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003004 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
3005
Josepch Chanf7278fd2007-12-13 16:40:40 +01003006 return 0;
3007}
3008
Harald Welted949cac2008-09-09 15:56:01 +08003009/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02003010static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08003011 /* Enable Mic Boost Volume backdoor */
3012 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003013 /* don't bybass mixer */
3014 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08003015 { }
3016};
3017
Takashi Iwai9da29272009-05-07 16:31:14 +02003018/* fill out digital output widgets; one for master and one for slave outputs */
3019static void fill_dig_outs(struct hda_codec *codec)
3020{
3021 struct via_spec *spec = codec->spec;
3022 int i;
3023
3024 for (i = 0; i < spec->autocfg.dig_outs; i++) {
3025 hda_nid_t nid;
3026 int conn;
3027
3028 nid = spec->autocfg.dig_out_pins[i];
3029 if (!nid)
3030 continue;
3031 conn = snd_hda_get_connections(codec, nid, &nid, 1);
3032 if (conn < 1)
3033 continue;
3034 if (!spec->multiout.dig_out_nid)
3035 spec->multiout.dig_out_nid = nid;
3036 else {
3037 spec->slave_dig_outs[0] = nid;
3038 break; /* at most two dig outs */
3039 }
3040 }
3041}
3042
Takashi Iwai12daef62011-06-18 17:45:49 +02003043static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08003044{
3045 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02003046 hda_nid_t dig_nid;
3047 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08003048
Takashi Iwai12daef62011-06-18 17:45:49 +02003049 if (!spec->autocfg.dig_in_pin)
3050 return;
Harald Welted949cac2008-09-09 15:56:01 +08003051
Takashi Iwai12daef62011-06-18 17:45:49 +02003052 dig_nid = codec->start_nid;
3053 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
3054 unsigned int wcaps = get_wcaps(codec, dig_nid);
3055 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
3056 continue;
3057 if (!(wcaps & AC_WCAP_DIGITAL))
3058 continue;
3059 if (!(wcaps & AC_WCAP_CONN_LIST))
3060 continue;
3061 err = get_connection_index(codec, dig_nid,
3062 spec->autocfg.dig_in_pin);
3063 if (err >= 0) {
3064 spec->dig_in_nid = dig_nid;
3065 break;
3066 }
3067 }
Harald Welted949cac2008-09-09 15:56:01 +08003068}
3069
Lydia Wang6369bcf2009-10-10 19:08:31 +08003070static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
3071 int offset, int num_steps, int step_size)
3072{
3073 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
3074 (offset << AC_AMPCAP_OFFSET_SHIFT) |
3075 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
3076 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
3077 (0 << AC_AMPCAP_MUTE_SHIFT));
3078}
3079
Harald Welted949cac2008-09-09 15:56:01 +08003080static int patch_vt1708S(struct hda_codec *codec)
3081{
3082 struct via_spec *spec;
3083 int err;
3084
3085 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003086 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003087 if (spec == NULL)
3088 return -ENOMEM;
3089
Takashi Iwai620e2b22011-06-17 17:19:19 +02003090 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003091 override_mic_boost(codec, 0x1a, 0, 3, 40);
3092 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003093
Harald Welted949cac2008-09-09 15:56:01 +08003094 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003095 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003096 if (err < 0) {
3097 via_free(codec);
3098 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003099 }
3100
Takashi Iwai096a8852011-06-20 12:09:02 +02003101 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003102
Harald Welted949cac2008-09-09 15:56:01 +08003103 codec->patch_ops = via_patch_ops;
3104
Lydia Wang518bf3b2009-10-10 19:07:29 +08003105 /* correct names for VT1708BCE */
3106 if (get_codec_type(codec) == VT1708BCE) {
3107 kfree(codec->chip_name);
3108 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3109 snprintf(codec->bus->card->mixername,
3110 sizeof(codec->bus->card->mixername),
3111 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f630f2011-03-22 16:25:56 +08003112 }
Lydia Wangbc92df72011-03-23 17:56:05 +08003113 /* correct names for VT1705 */
3114 if (codec->vendor_id == 0x11064397) {
3115 kfree(codec->chip_name);
3116 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3117 snprintf(codec->bus->card->mixername,
3118 sizeof(codec->bus->card->mixername),
3119 "%s %s", codec->vendor_name, codec->chip_name);
3120 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003121 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08003122 return 0;
3123}
3124
3125/* Patch for VT1702 */
3126
Takashi Iwai096a8852011-06-20 12:09:02 +02003127static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003128 /* mixer enable */
3129 {0x1, 0xF88, 0x3},
3130 /* GPIO 0~2 */
3131 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003132 { }
3133};
3134
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003135static void set_widgets_power_state_vt1702(struct hda_codec *codec)
3136{
3137 int imux_is_smixer =
3138 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3139 unsigned int parm;
3140 /* inputs */
3141 /* PW 1/2/5 (14h/15h/18h) */
3142 parm = AC_PWRST_D3;
3143 set_pin_power_state(codec, 0x14, &parm);
3144 set_pin_power_state(codec, 0x15, &parm);
3145 set_pin_power_state(codec, 0x18, &parm);
3146 if (imux_is_smixer)
3147 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
3148 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
3149 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3150 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
3151 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3152 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
3153
3154 /* outputs */
3155 /* PW 3/4 (16h/17h) */
3156 parm = AC_PWRST_D3;
3157 set_pin_power_state(codec, 0x17, &parm);
3158 set_pin_power_state(codec, 0x16, &parm);
3159 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
3160 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
3161 imux_is_smixer ? AC_PWRST_D0 : parm);
3162 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3163 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3164}
3165
Harald Welted949cac2008-09-09 15:56:01 +08003166static int patch_vt1702(struct hda_codec *codec)
3167{
3168 struct via_spec *spec;
3169 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003170
3171 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003172 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003173 if (spec == NULL)
3174 return -ENOMEM;
3175
Takashi Iwai620e2b22011-06-17 17:19:19 +02003176 spec->aa_mix_nid = 0x1a;
3177
Takashi Iwai12daef62011-06-18 17:45:49 +02003178 /* limit AA path volume to 0 dB */
3179 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3180 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3181 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3182 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3183 (1 << AC_AMPCAP_MUTE_SHIFT));
3184
Harald Welted949cac2008-09-09 15:56:01 +08003185 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003186 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003187 if (err < 0) {
3188 via_free(codec);
3189 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003190 }
3191
Takashi Iwai096a8852011-06-20 12:09:02 +02003192 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003193
Harald Welted949cac2008-09-09 15:56:01 +08003194 codec->patch_ops = via_patch_ops;
3195
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003196 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08003197 return 0;
3198}
3199
Lydia Wangeb7188c2009-10-10 19:08:34 +08003200/* Patch for VT1718S */
3201
Takashi Iwai096a8852011-06-20 12:09:02 +02003202static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08003203 /* Enable MW0 adjust Gain 5 */
3204 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003205 /* Enable Boost Volume backdoor */
3206 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02003207
Lydia Wangeb7188c2009-10-10 19:08:34 +08003208 { }
3209};
3210
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003211static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
3212{
3213 struct via_spec *spec = codec->spec;
3214 int imux_is_smixer;
3215 unsigned int parm;
3216 /* MUX6 (1eh) = stereo mixer */
3217 imux_is_smixer =
3218 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3219 /* inputs */
3220 /* PW 5/6/7 (29h/2ah/2bh) */
3221 parm = AC_PWRST_D3;
3222 set_pin_power_state(codec, 0x29, &parm);
3223 set_pin_power_state(codec, 0x2a, &parm);
3224 set_pin_power_state(codec, 0x2b, &parm);
3225 if (imux_is_smixer)
3226 parm = AC_PWRST_D0;
3227 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
3228 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3229 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3230 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3231 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3232
3233 /* outputs */
3234 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
3235 parm = AC_PWRST_D3;
3236 set_pin_power_state(codec, 0x27, &parm);
3237 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
3238 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
3239
3240 /* PW2 (26h), AOW2 (ah) */
3241 parm = AC_PWRST_D3;
3242 set_pin_power_state(codec, 0x26, &parm);
3243 if (spec->smart51_enabled)
3244 set_pin_power_state(codec, 0x2b, &parm);
3245 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
3246
3247 /* PW0 (24h), AOW0 (8h) */
3248 parm = AC_PWRST_D3;
3249 set_pin_power_state(codec, 0x24, &parm);
3250 if (!spec->hp_independent_mode) /* check for redirected HP */
3251 set_pin_power_state(codec, 0x28, &parm);
3252 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3253 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
3254 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
3255 imux_is_smixer ? AC_PWRST_D0 : parm);
3256
3257 /* PW1 (25h), AOW1 (9h) */
3258 parm = AC_PWRST_D3;
3259 set_pin_power_state(codec, 0x25, &parm);
3260 if (spec->smart51_enabled)
3261 set_pin_power_state(codec, 0x2a, &parm);
3262 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
3263
3264 if (spec->hp_independent_mode) {
3265 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3266 parm = AC_PWRST_D3;
3267 set_pin_power_state(codec, 0x28, &parm);
3268 snd_hda_codec_write(codec, 0x1b, 0,
3269 AC_VERB_SET_POWER_STATE, parm);
3270 snd_hda_codec_write(codec, 0x34, 0,
3271 AC_VERB_SET_POWER_STATE, parm);
3272 snd_hda_codec_write(codec, 0xc, 0,
3273 AC_VERB_SET_POWER_STATE, parm);
3274 }
3275}
3276
Takashi Iwai30b45032011-07-11 17:05:04 +02003277/* Add a connection to the primary DAC from AA-mixer for some codecs
3278 * This isn't listed from the raw info, but the chip has a secret connection.
3279 */
3280static int add_secret_dac_path(struct hda_codec *codec)
3281{
3282 struct via_spec *spec = codec->spec;
3283 int i, nums;
3284 hda_nid_t conn[8];
3285 hda_nid_t nid;
3286
3287 if (!spec->aa_mix_nid)
3288 return 0;
3289 nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
3290 ARRAY_SIZE(conn) - 1);
3291 for (i = 0; i < nums; i++) {
3292 if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
3293 return 0;
3294 }
3295
3296 /* find the primary DAC and add to the connection list */
3297 nid = codec->start_nid;
3298 for (i = 0; i < codec->num_nodes; i++, nid++) {
3299 unsigned int caps = get_wcaps(codec, nid);
3300 if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
3301 !(caps & AC_WCAP_DIGITAL)) {
3302 conn[nums++] = nid;
3303 return snd_hda_override_conn_list(codec,
3304 spec->aa_mix_nid,
3305 nums, conn);
3306 }
3307 }
3308 return 0;
3309}
3310
3311
Lydia Wangeb7188c2009-10-10 19:08:34 +08003312static int patch_vt1718S(struct hda_codec *codec)
3313{
3314 struct via_spec *spec;
3315 int err;
3316
3317 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003318 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003319 if (spec == NULL)
3320 return -ENOMEM;
3321
Takashi Iwai620e2b22011-06-17 17:19:19 +02003322 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003323 override_mic_boost(codec, 0x2b, 0, 3, 40);
3324 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003325 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003326
Lydia Wangeb7188c2009-10-10 19:08:34 +08003327 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003328 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003329 if (err < 0) {
3330 via_free(codec);
3331 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003332 }
3333
Takashi Iwai096a8852011-06-20 12:09:02 +02003334 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003335
Lydia Wangeb7188c2009-10-10 19:08:34 +08003336 codec->patch_ops = via_patch_ops;
3337
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003338 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3339
Lydia Wangeb7188c2009-10-10 19:08:34 +08003340 return 0;
3341}
Lydia Wangf3db4232009-10-10 19:08:41 +08003342
3343/* Patch for VT1716S */
3344
3345static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3346 struct snd_ctl_elem_info *uinfo)
3347{
3348 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3349 uinfo->count = 1;
3350 uinfo->value.integer.min = 0;
3351 uinfo->value.integer.max = 1;
3352 return 0;
3353}
3354
3355static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3356 struct snd_ctl_elem_value *ucontrol)
3357{
3358 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3359 int index = 0;
3360
3361 index = snd_hda_codec_read(codec, 0x26, 0,
3362 AC_VERB_GET_CONNECT_SEL, 0);
3363 if (index != -1)
3364 *ucontrol->value.integer.value = index;
3365
3366 return 0;
3367}
3368
3369static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3370 struct snd_ctl_elem_value *ucontrol)
3371{
3372 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3373 struct via_spec *spec = codec->spec;
3374 int index = *ucontrol->value.integer.value;
3375
3376 snd_hda_codec_write(codec, 0x26, 0,
3377 AC_VERB_SET_CONNECT_SEL, index);
3378 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003379 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003380 return 1;
3381}
3382
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003383static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003384 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3385 {
3386 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3387 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003388 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003389 .count = 1,
3390 .info = vt1716s_dmic_info,
3391 .get = vt1716s_dmic_get,
3392 .put = vt1716s_dmic_put,
3393 },
3394 {} /* end */
3395};
3396
3397
3398/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003399static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003400 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3401 { } /* end */
3402};
3403
Takashi Iwai096a8852011-06-20 12:09:02 +02003404static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003405 /* Enable Boost Volume backdoor */
3406 {0x1, 0xf8a, 0x80},
3407 /* don't bybass mixer */
3408 {0x1, 0xf88, 0xc0},
3409 /* Enable mono output */
3410 {0x1, 0xf90, 0x08},
3411 { }
3412};
3413
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003414static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3415{
3416 struct via_spec *spec = codec->spec;
3417 int imux_is_smixer;
3418 unsigned int parm;
3419 unsigned int mono_out, present;
3420 /* SW0 (17h) = stereo mixer */
3421 imux_is_smixer =
3422 (snd_hda_codec_read(codec, 0x17, 0,
3423 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3424 /* inputs */
3425 /* PW 1/2/5 (1ah/1bh/1eh) */
3426 parm = AC_PWRST_D3;
3427 set_pin_power_state(codec, 0x1a, &parm);
3428 set_pin_power_state(codec, 0x1b, &parm);
3429 set_pin_power_state(codec, 0x1e, &parm);
3430 if (imux_is_smixer)
3431 parm = AC_PWRST_D0;
3432 /* SW0 (17h), AIW0(13h) */
3433 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3434 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3435
3436 parm = AC_PWRST_D3;
3437 set_pin_power_state(codec, 0x1e, &parm);
3438 /* PW11 (22h) */
3439 if (spec->dmic_enabled)
3440 set_pin_power_state(codec, 0x22, &parm);
3441 else
3442 snd_hda_codec_write(codec, 0x22, 0,
3443 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3444
3445 /* SW2(26h), AIW1(14h) */
3446 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3447 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3448
3449 /* outputs */
3450 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3451 parm = AC_PWRST_D3;
3452 set_pin_power_state(codec, 0x19, &parm);
3453 /* Smart 5.1 PW2(1bh) */
3454 if (spec->smart51_enabled)
3455 set_pin_power_state(codec, 0x1b, &parm);
3456 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3457 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3458
3459 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3460 parm = AC_PWRST_D3;
3461 set_pin_power_state(codec, 0x23, &parm);
3462 /* Smart 5.1 PW1(1ah) */
3463 if (spec->smart51_enabled)
3464 set_pin_power_state(codec, 0x1a, &parm);
3465 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3466
3467 /* Smart 5.1 PW5(1eh) */
3468 if (spec->smart51_enabled)
3469 set_pin_power_state(codec, 0x1e, &parm);
3470 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3471
3472 /* Mono out */
3473 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3474 present = snd_hda_jack_detect(codec, 0x1c);
3475
3476 if (present)
3477 mono_out = 0;
3478 else {
3479 present = snd_hda_jack_detect(codec, 0x1d);
3480 if (!spec->hp_independent_mode && present)
3481 mono_out = 0;
3482 else
3483 mono_out = 1;
3484 }
3485 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3486 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3487 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3488 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3489
3490 /* PW 3/4 (1ch/1dh) */
3491 parm = AC_PWRST_D3;
3492 set_pin_power_state(codec, 0x1c, &parm);
3493 set_pin_power_state(codec, 0x1d, &parm);
3494 /* HP Independent Mode, power on AOW3 */
3495 if (spec->hp_independent_mode)
3496 snd_hda_codec_write(codec, 0x25, 0,
3497 AC_VERB_SET_POWER_STATE, parm);
3498
3499 /* force to D0 for internal Speaker */
3500 /* MW0 (16h), AOW0 (10h) */
3501 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3502 imux_is_smixer ? AC_PWRST_D0 : parm);
3503 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3504 mono_out ? AC_PWRST_D0 : parm);
3505}
3506
Lydia Wangf3db4232009-10-10 19:08:41 +08003507static int patch_vt1716S(struct hda_codec *codec)
3508{
3509 struct via_spec *spec;
3510 int err;
3511
3512 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003513 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003514 if (spec == NULL)
3515 return -ENOMEM;
3516
Takashi Iwai620e2b22011-06-17 17:19:19 +02003517 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003518 override_mic_boost(codec, 0x1a, 0, 3, 40);
3519 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003520
Lydia Wangf3db4232009-10-10 19:08:41 +08003521 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003522 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003523 if (err < 0) {
3524 via_free(codec);
3525 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003526 }
3527
Takashi Iwai096a8852011-06-20 12:09:02 +02003528 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003529
Lydia Wangf3db4232009-10-10 19:08:41 +08003530 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3531 spec->num_mixers++;
3532
3533 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3534
3535 codec->patch_ops = via_patch_ops;
3536
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003537 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003538 return 0;
3539}
Lydia Wang25eaba22009-10-10 19:08:43 +08003540
3541/* for vt2002P */
3542
Takashi Iwai096a8852011-06-20 12:09:02 +02003543static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003544 /* Class-D speaker related verbs */
3545 {0x1, 0xfe0, 0x4},
3546 {0x1, 0xfe9, 0x80},
3547 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003548 /* Enable Boost Volume backdoor */
3549 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003550 /* Enable AOW0 to MW9 */
3551 {0x1, 0xfb8, 0x88},
3552 { }
3553};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003554
Takashi Iwai096a8852011-06-20 12:09:02 +02003555static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003556 /* Enable Boost Volume backdoor */
3557 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003558 /* Enable AOW0 to MW9 */
3559 {0x1, 0xfb8, 0x88},
3560 { }
3561};
Lydia Wang25eaba22009-10-10 19:08:43 +08003562
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003563static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3564{
3565 struct via_spec *spec = codec->spec;
3566 int imux_is_smixer;
3567 unsigned int parm;
3568 unsigned int present;
3569 /* MUX9 (1eh) = stereo mixer */
3570 imux_is_smixer =
3571 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3572 /* inputs */
3573 /* PW 5/6/7 (29h/2ah/2bh) */
3574 parm = AC_PWRST_D3;
3575 set_pin_power_state(codec, 0x29, &parm);
3576 set_pin_power_state(codec, 0x2a, &parm);
3577 set_pin_power_state(codec, 0x2b, &parm);
3578 parm = AC_PWRST_D0;
3579 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3580 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3581 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3582 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3583 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3584
3585 /* outputs */
3586 /* AOW0 (8h)*/
3587 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3588
Lydia Wang118909562011-03-23 17:57:34 +08003589 if (spec->codec_type == VT1802) {
3590 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3591 parm = AC_PWRST_D3;
3592 set_pin_power_state(codec, 0x28, &parm);
3593 snd_hda_codec_write(codec, 0x18, 0,
3594 AC_VERB_SET_POWER_STATE, parm);
3595 snd_hda_codec_write(codec, 0x38, 0,
3596 AC_VERB_SET_POWER_STATE, parm);
3597 } else {
3598 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3599 parm = AC_PWRST_D3;
3600 set_pin_power_state(codec, 0x26, &parm);
3601 snd_hda_codec_write(codec, 0x1c, 0,
3602 AC_VERB_SET_POWER_STATE, parm);
3603 snd_hda_codec_write(codec, 0x37, 0,
3604 AC_VERB_SET_POWER_STATE, parm);
3605 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003606
Lydia Wang118909562011-03-23 17:57:34 +08003607 if (spec->codec_type == VT1802) {
3608 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3609 parm = AC_PWRST_D3;
3610 set_pin_power_state(codec, 0x25, &parm);
3611 snd_hda_codec_write(codec, 0x15, 0,
3612 AC_VERB_SET_POWER_STATE, parm);
3613 snd_hda_codec_write(codec, 0x35, 0,
3614 AC_VERB_SET_POWER_STATE, parm);
3615 } else {
3616 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3617 parm = AC_PWRST_D3;
3618 set_pin_power_state(codec, 0x25, &parm);
3619 snd_hda_codec_write(codec, 0x19, 0,
3620 AC_VERB_SET_POWER_STATE, parm);
3621 snd_hda_codec_write(codec, 0x35, 0,
3622 AC_VERB_SET_POWER_STATE, parm);
3623 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003624
3625 if (spec->hp_independent_mode)
3626 snd_hda_codec_write(codec, 0x9, 0,
3627 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3628
3629 /* Class-D */
3630 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3631 present = snd_hda_jack_detect(codec, 0x25);
3632
3633 parm = AC_PWRST_D3;
3634 set_pin_power_state(codec, 0x24, &parm);
3635 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003636 if (spec->codec_type == VT1802)
3637 snd_hda_codec_write(codec, 0x14, 0,
3638 AC_VERB_SET_POWER_STATE, parm);
3639 else
3640 snd_hda_codec_write(codec, 0x18, 0,
3641 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003642 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3643
3644 /* Mono Out */
3645 present = snd_hda_jack_detect(codec, 0x26);
3646
3647 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003648 if (spec->codec_type == VT1802) {
3649 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3650 snd_hda_codec_write(codec, 0x33, 0,
3651 AC_VERB_SET_POWER_STATE, parm);
3652 snd_hda_codec_write(codec, 0x1c, 0,
3653 AC_VERB_SET_POWER_STATE, parm);
3654 snd_hda_codec_write(codec, 0x3c, 0,
3655 AC_VERB_SET_POWER_STATE, parm);
3656 } else {
3657 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3658 snd_hda_codec_write(codec, 0x31, 0,
3659 AC_VERB_SET_POWER_STATE, parm);
3660 snd_hda_codec_write(codec, 0x17, 0,
3661 AC_VERB_SET_POWER_STATE, parm);
3662 snd_hda_codec_write(codec, 0x3b, 0,
3663 AC_VERB_SET_POWER_STATE, parm);
3664 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003665 /* MW9 (21h) */
3666 if (imux_is_smixer || !is_aa_path_mute(codec))
3667 snd_hda_codec_write(codec, 0x21, 0,
3668 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3669 else
3670 snd_hda_codec_write(codec, 0x21, 0,
3671 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3672}
Lydia Wang25eaba22009-10-10 19:08:43 +08003673
3674/* patch for vt2002P */
3675static int patch_vt2002P(struct hda_codec *codec)
3676{
3677 struct via_spec *spec;
3678 int err;
3679
3680 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003681 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003682 if (spec == NULL)
3683 return -ENOMEM;
3684
Takashi Iwai620e2b22011-06-17 17:19:19 +02003685 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003686 override_mic_boost(codec, 0x2b, 0, 3, 40);
3687 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003688 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003689
Lydia Wang25eaba22009-10-10 19:08:43 +08003690 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003691 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003692 if (err < 0) {
3693 via_free(codec);
3694 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003695 }
3696
Lydia Wang118909562011-03-23 17:57:34 +08003697 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003698 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003699 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003700 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003701
Lydia Wang25eaba22009-10-10 19:08:43 +08003702 codec->patch_ops = via_patch_ops;
3703
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003704 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003705 return 0;
3706}
Lydia Wangab6734e2009-10-10 19:08:46 +08003707
3708/* for vt1812 */
3709
Takashi Iwai096a8852011-06-20 12:09:02 +02003710static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003711 /* Enable Boost Volume backdoor */
3712 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003713 /* Enable AOW0 to MW9 */
3714 {0x1, 0xfb8, 0xa8},
3715 { }
3716};
3717
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003718static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3719{
3720 struct via_spec *spec = codec->spec;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003721 unsigned int parm;
3722 unsigned int present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003723 /* inputs */
3724 /* PW 5/6/7 (29h/2ah/2bh) */
3725 parm = AC_PWRST_D3;
3726 set_pin_power_state(codec, 0x29, &parm);
3727 set_pin_power_state(codec, 0x2a, &parm);
3728 set_pin_power_state(codec, 0x2b, &parm);
3729 parm = AC_PWRST_D0;
3730 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3731 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3732 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3733 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3734 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3735
3736 /* outputs */
3737 /* AOW0 (8h)*/
3738 snd_hda_codec_write(codec, 0x8, 0,
3739 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3740
3741 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3742 parm = AC_PWRST_D3;
3743 set_pin_power_state(codec, 0x28, &parm);
3744 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3745 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3746
3747 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3748 parm = AC_PWRST_D3;
3749 set_pin_power_state(codec, 0x25, &parm);
3750 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3751 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3752 if (spec->hp_independent_mode)
3753 snd_hda_codec_write(codec, 0x9, 0,
3754 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3755
3756 /* Internal Speaker */
3757 /* PW0 (24h), MW0(14h), MUX0(34h) */
3758 present = snd_hda_jack_detect(codec, 0x25);
3759
3760 parm = AC_PWRST_D3;
3761 set_pin_power_state(codec, 0x24, &parm);
3762 if (present) {
3763 snd_hda_codec_write(codec, 0x14, 0,
3764 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3765 snd_hda_codec_write(codec, 0x34, 0,
3766 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3767 } else {
3768 snd_hda_codec_write(codec, 0x14, 0,
3769 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3770 snd_hda_codec_write(codec, 0x34, 0,
3771 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3772 }
3773
3774
3775 /* Mono Out */
3776 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3777 present = snd_hda_jack_detect(codec, 0x28);
3778
3779 parm = AC_PWRST_D3;
3780 set_pin_power_state(codec, 0x31, &parm);
3781 if (present) {
3782 snd_hda_codec_write(codec, 0x1c, 0,
3783 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3784 snd_hda_codec_write(codec, 0x3c, 0,
3785 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3786 snd_hda_codec_write(codec, 0x3e, 0,
3787 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3788 } else {
3789 snd_hda_codec_write(codec, 0x1c, 0,
3790 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3791 snd_hda_codec_write(codec, 0x3c, 0,
3792 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3793 snd_hda_codec_write(codec, 0x3e, 0,
3794 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3795 }
3796
3797 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3798 parm = AC_PWRST_D3;
3799 set_pin_power_state(codec, 0x33, &parm);
3800 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3801 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3802
3803}
Lydia Wangab6734e2009-10-10 19:08:46 +08003804
3805/* patch for vt1812 */
3806static int patch_vt1812(struct hda_codec *codec)
3807{
3808 struct via_spec *spec;
3809 int err;
3810
3811 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003812 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003813 if (spec == NULL)
3814 return -ENOMEM;
3815
Takashi Iwai620e2b22011-06-17 17:19:19 +02003816 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003817 override_mic_boost(codec, 0x2b, 0, 3, 40);
3818 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003819 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003820
Lydia Wangab6734e2009-10-10 19:08:46 +08003821 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003822 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003823 if (err < 0) {
3824 via_free(codec);
3825 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003826 }
3827
Takashi Iwai096a8852011-06-20 12:09:02 +02003828 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003829
Lydia Wangab6734e2009-10-10 19:08:46 +08003830 codec->patch_ops = via_patch_ops;
3831
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003832 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003833 return 0;
3834}
3835
Joseph Chanc577b8a2006-11-29 15:29:40 +01003836/*
3837 * patch entries
3838 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003839static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003840 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3841 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3842 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3843 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3844 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003845 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003846 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003847 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003848 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003849 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003850 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003851 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003852 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003853 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003854 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003855 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003856 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003857 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003858 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003859 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003860 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003861 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003862 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003863 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003864 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003865 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003866 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003867 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003868 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003869 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003870 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003871 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003872 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003873 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003874 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003875 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003876 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003877 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003878 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003879 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003880 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003881 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003882 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003883 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003884 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003885 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003886 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003887 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003888 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003889 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003890 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003891 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003892 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003893 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003894 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003895 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003896 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003897 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003898 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003899 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003900 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003901 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003902 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003903 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003904 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003905 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003906 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003907 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003908 { .id = 0x11060428, .name = "VT1718S",
3909 .patch = patch_vt1718S},
3910 { .id = 0x11064428, .name = "VT1718S",
3911 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003912 { .id = 0x11060441, .name = "VT2020",
3913 .patch = patch_vt1718S},
3914 { .id = 0x11064441, .name = "VT1828S",
3915 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003916 { .id = 0x11060433, .name = "VT1716S",
3917 .patch = patch_vt1716S},
3918 { .id = 0x1106a721, .name = "VT1716S",
3919 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003920 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3921 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003922 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003923 { .id = 0x11060440, .name = "VT1818S",
3924 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003925 { .id = 0x11060446, .name = "VT1802",
3926 .patch = patch_vt2002P},
3927 { .id = 0x11068446, .name = "VT1802",
3928 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003929 {} /* terminator */
3930};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003931
3932MODULE_ALIAS("snd-hda-codec-id:1106*");
3933
3934static struct hda_codec_preset_list via_list = {
3935 .preset = snd_hda_preset_via,
3936 .owner = THIS_MODULE,
3937};
3938
3939MODULE_LICENSE("GPL");
3940MODULE_DESCRIPTION("VIA HD-audio codec");
3941
3942static int __init patch_via_init(void)
3943{
3944 return snd_hda_add_codec_preset(&via_list);
3945}
3946
3947static void __exit patch_via_exit(void)
3948{
3949 snd_hda_delete_codec_preset(&via_list);
3950}
3951
3952module_init(patch_via_init)
3953module_exit(patch_via_exit)