blob: fb1f0ffc556b8c9325837bc2d4841b838a87a6bb [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Paul Gortmakerda155d52011-07-15 12:38:28 -040052#include <linux/module.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010053#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080054#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010055#include "hda_codec.h"
56#include "hda_local.h"
Takashi Iwai1835a0f2011-10-27 22:12:46 +020057#include "hda_jack.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010058
Joseph Chanc577b8a2006-11-29 15:29:40 +010059/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080060#define VT1708_HP_PIN_NID 0x20
61#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010062
Harald Welted7426322008-09-15 22:43:23 +080063enum VIA_HDA_CODEC {
64 UNKNOWN = -1,
65 VT1708,
66 VT1709_10CH,
67 VT1709_6CH,
68 VT1708B_8CH,
69 VT1708B_4CH,
70 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080071 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080072 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080073 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080074 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080075 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080076 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080077 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080078 CODEC_TYPES,
79};
80
Lydia Wang118909562011-03-23 17:57:34 +080081#define VT2002P_COMPATIBLE(spec) \
82 ((spec)->codec_type == VT2002P ||\
83 (spec)->codec_type == VT1812 ||\
84 (spec)->codec_type == VT1802)
85
Takashi Iwai8e3679d2011-06-21 09:01:36 +020086#define MAX_NID_PATH_DEPTH 5
87
Takashi Iwai09a9ad692011-06-21 15:57:44 +020088/* output-path: DAC -> ... -> pin
89 * idx[] contains the source index number of the next widget;
90 * e.g. idx[0] is the index of the DAC selected by path[1] widget
91 * multi[] indicates whether it's a selector widget with multi-connectors
92 * (i.e. the connection selection is mandatory)
93 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
94 */
Takashi Iwai4a796162011-06-17 17:53:38 +020095struct nid_path {
96 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020097 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad692011-06-21 15:57:44 +020098 unsigned char idx[MAX_NID_PATH_DEPTH];
99 unsigned char multi[MAX_NID_PATH_DEPTH];
100 unsigned int vol_ctl;
101 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200102};
103
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200104/* input-path */
105struct via_input {
106 hda_nid_t pin; /* input-pin or aa-mix */
107 int adc_idx; /* ADC index to be used */
108 int mux_idx; /* MUX index (if any) */
109 const char *label; /* input-source label */
110};
111
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200112#define VIA_MAX_ADCS 3
113
Takashi Iwai3b607e32011-07-18 16:54:40 +0200114enum {
115 STREAM_MULTI_OUT = (1 << 0),
116 STREAM_INDEP_HP = (1 << 1),
117};
118
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800119struct via_spec {
120 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200121 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800122 unsigned int num_mixers;
123
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200124 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800125 unsigned int num_iverbs;
126
Takashi Iwai82673bc2011-06-17 16:24:21 +0200127 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200128 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200129 const struct hda_pcm_stream *stream_analog_playback;
130 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800131
Takashi Iwai82673bc2011-06-17 16:24:21 +0200132 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200133 const struct hda_pcm_stream *stream_digital_playback;
134 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800135
136 /* playback */
137 struct hda_multi_out multiout;
138 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200139 hda_nid_t hp_dac_nid;
Takashi Iwai3214b962011-07-18 12:49:25 +0200140 hda_nid_t speaker_dac_nid;
141 int hp_indep_shared; /* indep HP-DAC is shared with side ch */
Takashi Iwai3b607e32011-07-18 16:54:40 +0200142 int opened_streams; /* STREAM_* bits */
143 int active_streams; /* STREAM_* bits */
Takashi Iwai3214b962011-07-18 12:49:25 +0200144 int aamix_mode; /* loopback is enabled for output-path? */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800145
Takashi Iwai3214b962011-07-18 12:49:25 +0200146 /* Output-paths:
147 * There are different output-paths depending on the setup.
148 * out_path, hp_path and speaker_path are primary paths. If both
149 * direct DAC and aa-loopback routes are available, these contain
150 * the former paths. Meanwhile *_mix_path contain the paths with
151 * loopback mixer. (Since the loopback is only for front channel,
152 * no out_mix_path for surround channels.)
153 * The HP output has another path, hp_indep_path, which is used in
154 * the independent-HP mode.
155 */
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200156 struct nid_path out_path[HDA_SIDE + 1];
Takashi Iwai3214b962011-07-18 12:49:25 +0200157 struct nid_path out_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200158 struct nid_path hp_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200159 struct nid_path hp_mix_path;
160 struct nid_path hp_indep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200161 struct nid_path speaker_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200162 struct nid_path speaker_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200163
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800164 /* capture */
165 unsigned int num_adc_nids;
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200166 hda_nid_t adc_nids[VIA_MAX_ADCS];
167 hda_nid_t mux_nids[VIA_MAX_ADCS];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200168 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800169 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800170
171 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200172 bool dyn_adc_switch;
173 int num_inputs;
174 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200175 unsigned int cur_mux[VIA_MAX_ADCS];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800176
Takashi Iwai3b607e32011-07-18 16:54:40 +0200177 /* dynamic DAC switching */
178 unsigned int cur_dac_stream_tag;
179 unsigned int cur_dac_format;
180 unsigned int cur_hp_stream_tag;
181 unsigned int cur_hp_format;
182
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200183 /* dynamic ADC switching */
184 hda_nid_t cur_adc;
185 unsigned int cur_adc_stream_tag;
186 unsigned int cur_adc_format;
187
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800188 /* PCM information */
189 struct hda_pcm pcm_rec[3];
190
191 /* dynamic controls, init_verbs and input_mux */
192 struct auto_pin_cfg autocfg;
193 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800194 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
195
196 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800197 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800198 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200199 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800200 enum VIA_HDA_CODEC codec_type;
201
Takashi 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 Wangd69607b2011-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 Wangd69607b2011-07-08 14:02:52 +0800479{
480 int i, num, val;
Lydia Wangd69607b2011-07-08 14:02:52 +0800481
482 if (!path)
483 return;
484 num = snd_hda_get_conn_list(codec, mix_nid, NULL);
Lydia Wangd69607b2011-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 Wangd69607b2011-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 Wangd69607b2011-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 Wangd69607b2011-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
Takashi Iwai054d8672012-01-24 12:25:50 +0100690static void update_power_state(struct hda_codec *codec, hda_nid_t nid,
691 unsigned int parm)
692{
693 if (snd_hda_codec_read(codec, nid, 0,
694 AC_VERB_GET_POWER_STATE, 0) == parm)
695 return;
696 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
697}
698
Lydia Wangf5271102009-10-10 19:07:35 +0800699static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
700 unsigned int *affected_parm)
701{
702 unsigned parm;
703 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
704 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
705 >> AC_DEFCFG_MISC_SHIFT
706 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800707 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200708 unsigned present = 0;
709
710 no_presence |= spec->no_pin_power_ctl;
711 if (!no_presence)
712 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200713 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800714 || ((no_presence || present)
715 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800716 *affected_parm = AC_PWRST_D0; /* if it's connected */
717 parm = AC_PWRST_D0;
718 } else
719 parm = AC_PWRST_D3;
720
Takashi Iwai054d8672012-01-24 12:25:50 +0100721 update_power_state(codec, nid, parm);
Lydia Wangf5271102009-10-10 19:07:35 +0800722}
723
Takashi Iwai24088a52011-06-17 16:59:21 +0200724static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
725 struct snd_ctl_elem_info *uinfo)
726{
727 static const char * const texts[] = {
728 "Disabled", "Enabled"
729 };
730
731 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
732 uinfo->count = 1;
733 uinfo->value.enumerated.items = 2;
734 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
735 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
736 strcpy(uinfo->value.enumerated.name,
737 texts[uinfo->value.enumerated.item]);
738 return 0;
739}
740
741static int via_pin_power_ctl_get(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 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
747 return 0;
748}
749
750static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
751 struct snd_ctl_elem_value *ucontrol)
752{
753 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
754 struct via_spec *spec = codec->spec;
755 unsigned int val = !ucontrol->value.enumerated.item[0];
756
757 if (val == spec->no_pin_power_ctl)
758 return 0;
759 spec->no_pin_power_ctl = val;
760 set_widgets_power_state(codec);
761 return 1;
762}
763
764static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
765 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
766 .name = "Dynamic Power-Control",
767 .info = via_pin_power_ctl_info,
768 .get = via_pin_power_ctl_get,
769 .put = via_pin_power_ctl_put,
770};
771
772
Harald Welte0aa62ae2008-09-09 15:58:27 +0800773static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
774 struct snd_ctl_elem_info *uinfo)
775{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200776 static const char * const texts[] = { "OFF", "ON" };
777
778 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
779 uinfo->count = 1;
780 uinfo->value.enumerated.items = 2;
781 if (uinfo->value.enumerated.item >= 2)
782 uinfo->value.enumerated.item = 1;
783 strcpy(uinfo->value.enumerated.name,
784 texts[uinfo->value.enumerated.item]);
785 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800786}
787
788static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
789 struct snd_ctl_elem_value *ucontrol)
790{
791 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800792 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800793
Takashi Iwaiece8d042011-06-19 16:24:21 +0200794 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800795 return 0;
796}
797
Takashi Iwai3b607e32011-07-18 16:54:40 +0200798/* adjust spec->multiout setup according to the current flags */
799static void setup_playback_multi_pcm(struct via_spec *spec)
800{
801 const struct auto_pin_cfg *cfg = &spec->autocfg;
802 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
803 spec->multiout.hp_nid = 0;
804 if (!spec->hp_independent_mode) {
805 if (!spec->hp_indep_shared)
806 spec->multiout.hp_nid = spec->hp_dac_nid;
807 } else {
808 if (spec->hp_indep_shared)
809 spec->multiout.num_dacs = cfg->line_outs - 1;
810 }
811}
812
813/* update DAC setups according to indep-HP switch;
814 * this function is called only when indep-HP is modified
815 */
816static void switch_indep_hp_dacs(struct hda_codec *codec)
817{
818 struct via_spec *spec = codec->spec;
819 int shared = spec->hp_indep_shared;
820 hda_nid_t shared_dac, hp_dac;
821
822 if (!spec->opened_streams)
823 return;
824
825 shared_dac = shared ? spec->multiout.dac_nids[shared] : 0;
826 hp_dac = spec->hp_dac_nid;
827 if (spec->hp_independent_mode) {
828 /* switch to indep-HP mode */
829 if (spec->active_streams & STREAM_MULTI_OUT) {
830 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
831 __snd_hda_codec_cleanup_stream(codec, shared_dac, 1);
832 }
833 if (spec->active_streams & STREAM_INDEP_HP)
834 snd_hda_codec_setup_stream(codec, hp_dac,
835 spec->cur_hp_stream_tag, 0,
836 spec->cur_hp_format);
837 } else {
838 /* back to HP or shared-DAC */
839 if (spec->active_streams & STREAM_INDEP_HP)
840 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
841 if (spec->active_streams & STREAM_MULTI_OUT) {
842 hda_nid_t dac;
843 int ch;
844 if (shared_dac) { /* reset mutli-ch DAC */
845 dac = shared_dac;
846 ch = shared * 2;
847 } else { /* reset HP DAC */
848 dac = hp_dac;
849 ch = 0;
850 }
851 snd_hda_codec_setup_stream(codec, dac,
852 spec->cur_dac_stream_tag, ch,
853 spec->cur_dac_format);
854 }
855 }
856 setup_playback_multi_pcm(spec);
857}
858
Harald Welte0aa62ae2008-09-09 15:58:27 +0800859static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
860 struct snd_ctl_elem_value *ucontrol)
861{
862 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
863 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200864 int cur, shared;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200865
Takashi Iwai3b607e32011-07-18 16:54:40 +0200866 mutex_lock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200867 cur = !!ucontrol->value.enumerated.item[0];
Takashi Iwai3b607e32011-07-18 16:54:40 +0200868 if (spec->hp_independent_mode == cur) {
869 mutex_unlock(&spec->config_mutex);
Takashi Iwai25250502011-06-30 17:24:47 +0200870 return 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +0200871 }
Takashi Iwai25250502011-06-30 17:24:47 +0200872 spec->hp_independent_mode = cur;
Takashi Iwai3214b962011-07-18 12:49:25 +0200873 shared = spec->hp_indep_shared;
Takashi Iwai020066d2011-07-21 13:45:56 +0200874 deactivate_hp_paths(codec);
875 if (cur)
876 activate_output_path(codec, &spec->hp_indep_path, true, false);
877 else {
Takashi Iwai3214b962011-07-18 12:49:25 +0200878 if (shared)
879 activate_output_path(codec, &spec->out_path[shared],
Takashi Iwai25250502011-06-30 17:24:47 +0200880 true, false);
Takashi Iwai020066d2011-07-21 13:45:56 +0200881 if (spec->aamix_mode || !spec->hp_path.depth)
882 activate_output_path(codec, &spec->hp_mix_path,
883 true, false);
884 else
885 activate_output_path(codec, &spec->hp_path,
886 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200887 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800888
Takashi Iwai3b607e32011-07-18 16:54:40 +0200889 switch_indep_hp_dacs(codec);
890 mutex_unlock(&spec->config_mutex);
891
Lydia Wangce0e5a92011-03-22 16:22:37 +0800892 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800893 set_widgets_power_state(codec);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200894 via_hp_automute(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200895 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800896}
897
Takashi Iwaiece8d042011-06-19 16:24:21 +0200898static const struct snd_kcontrol_new via_hp_mixer = {
899 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
900 .name = "Independent HP",
901 .info = via_independent_hp_info,
902 .get = via_independent_hp_get,
903 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800904};
905
Takashi Iwai3d83e572010-04-14 14:36:23 +0200906static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100907{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200908 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100909 struct snd_kcontrol_new *knew;
910 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100911
Takashi Iwaiece8d042011-06-19 16:24:21 +0200912 nid = spec->autocfg.hp_pins[0];
913 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200914 if (knew == NULL)
915 return -ENOMEM;
916
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100917 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100918
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100919 return 0;
920}
921
Lydia Wang1564b282009-10-10 19:07:52 +0800922static void notify_aa_path_ctls(struct hda_codec *codec)
923{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200924 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800925 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800926
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200927 for (i = 0; i < spec->smart51_nums; i++) {
928 struct snd_kcontrol *ctl;
929 struct snd_ctl_elem_id id;
930 memset(&id, 0, sizeof(id));
931 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
932 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800933 ctl = snd_hda_find_mixer_ctl(codec, id.name);
934 if (ctl)
935 snd_ctl_notify(codec->bus->card,
936 SNDRV_CTL_EVENT_MASK_VALUE,
937 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800938 }
939}
940
941static void mute_aa_path(struct hda_codec *codec, int mute)
942{
943 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200944 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800945 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200946
Lydia Wang1564b282009-10-10 19:07:52 +0800947 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200948 for (i = 0; i < spec->smart51_nums; i++) {
949 if (spec->smart51_idxs[i] < 0)
950 continue;
951 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
952 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800953 HDA_AMP_MUTE, val);
954 }
955}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200956
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200957static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
958{
959 struct via_spec *spec = codec->spec;
960 int i;
961
962 for (i = 0; i < spec->smart51_nums; i++)
963 if (spec->smart51_pins[i] == pin)
964 return true;
965 return false;
966}
967
Lydia Wang1564b282009-10-10 19:07:52 +0800968static int via_smart51_get(struct snd_kcontrol *kcontrol,
969 struct snd_ctl_elem_value *ucontrol)
970{
971 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
972 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800973
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200974 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800975 return 0;
976}
977
978static int via_smart51_put(struct snd_kcontrol *kcontrol,
979 struct snd_ctl_elem_value *ucontrol)
980{
981 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
982 struct via_spec *spec = codec->spec;
983 int out_in = *ucontrol->value.integer.value
984 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800985 int i;
986
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200987 for (i = 0; i < spec->smart51_nums; i++) {
988 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200989 unsigned int parm;
990
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200991 parm = snd_hda_codec_read(codec, nid, 0,
992 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
993 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
994 parm |= out_in;
995 snd_hda_codec_write(codec, nid, 0,
996 AC_VERB_SET_PIN_WIDGET_CONTROL,
997 parm);
998 if (out_in == AC_PINCTL_OUT_EN) {
999 mute_aa_path(codec, 1);
1000 notify_aa_path_ctls(codec);
1001 }
Lydia Wang1564b282009-10-10 19:07:52 +08001002 }
1003 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001004 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +08001005 return 1;
1006}
1007
Takashi Iwai5f4b36d2011-06-17 14:55:02 +02001008static const struct snd_kcontrol_new via_smart51_mixer = {
1009 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1010 .name = "Smart 5.1",
1011 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001012 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +02001013 .get = via_smart51_get,
1014 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +08001015};
1016
Takashi Iwaif4a78282011-06-17 18:46:48 +02001017static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001018{
Takashi Iwaif4a78282011-06-17 18:46:48 +02001019 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001020
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001021 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +08001022 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001023 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001024 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001025 return 0;
1026}
1027
Takashi Iwaiada509e2011-06-20 15:40:19 +02001028/* check AA path's mute status */
1029static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001030{
Lydia Wangf5271102009-10-10 19:07:35 +08001031 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001032 const struct hda_amp_list *p;
1033 int i, ch, v;
1034
1035 for (i = 0; i < spec->num_loopbacks; i++) {
1036 p = &spec->loopback_list[i];
1037 for (ch = 0; ch < 2; ch++) {
1038 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
1039 p->idx);
1040 if (!(v & HDA_AMP_MUTE) && v > 0)
1041 return false;
Lydia Wangf5271102009-10-10 19:07:35 +08001042 }
1043 }
Takashi Iwaiada509e2011-06-20 15:40:19 +02001044 return true;
Lydia Wangf5271102009-10-10 19:07:35 +08001045}
1046
1047/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +02001048static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +08001049{
1050 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001051 bool enable;
1052 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +08001053
Takashi Iwai3b607e32011-07-18 16:54:40 +02001054 enable = is_aa_path_mute(codec) && (spec->opened_streams != 0);
Lydia Wangf5271102009-10-10 19:07:35 +08001055
1056 /* decide low current mode's verb & parameter */
1057 switch (spec->codec_type) {
1058 case VT1708B_8CH:
1059 case VT1708B_4CH:
1060 verb = 0xf70;
1061 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
1062 break;
1063 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +08001064 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +08001065 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +08001066 verb = 0xf73;
1067 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
1068 break;
1069 case VT1702:
1070 verb = 0xf73;
1071 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
1072 break;
Lydia Wang25eaba22009-10-10 19:08:43 +08001073 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +08001074 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +08001075 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +08001076 verb = 0xf93;
1077 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
1078 break;
Lydia Wangf5271102009-10-10 19:07:35 +08001079 default:
1080 return; /* other codecs are not supported */
1081 }
1082 /* send verb */
1083 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
1084}
1085
Joseph Chanc577b8a2006-11-29 15:29:40 +01001086/*
1087 * generic initialization of ADC, input mixers and output mixers
1088 */
Takashi Iwai096a8852011-06-20 12:09:02 +02001089static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001090 /* power down jack detect function */
1091 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001092 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001093};
1094
Takashi Iwai3b607e32011-07-18 16:54:40 +02001095static void set_stream_open(struct hda_codec *codec, int bit, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001096{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001097 struct via_spec *spec = codec->spec;
1098
1099 if (active)
Takashi Iwai3b607e32011-07-18 16:54:40 +02001100 spec->opened_streams |= bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001101 else
Takashi Iwai3b607e32011-07-18 16:54:40 +02001102 spec->opened_streams &= ~bit;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001103 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001104}
1105
Takashi Iwaiece8d042011-06-19 16:24:21 +02001106static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001107 struct hda_codec *codec,
1108 struct snd_pcm_substream *substream)
1109{
1110 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001111 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001112 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001113
Takashi Iwai25250502011-06-30 17:24:47 +02001114 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
Takashi Iwai25250502011-06-30 17:24:47 +02001115 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001116 set_stream_open(codec, STREAM_MULTI_OUT, true);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001117 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1118 hinfo);
1119 if (err < 0) {
Takashi Iwai3b607e32011-07-18 16:54:40 +02001120 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001121 return err;
1122 }
1123 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001124}
1125
Takashi Iwaiece8d042011-06-19 16:24:21 +02001126static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001127 struct hda_codec *codec,
1128 struct snd_pcm_substream *substream)
1129{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001130 set_stream_open(codec, STREAM_MULTI_OUT, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001131 return 0;
1132}
1133
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001134static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1135 struct hda_codec *codec,
1136 struct snd_pcm_substream *substream)
1137{
1138 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001139
Takashi Iwaiece8d042011-06-19 16:24:21 +02001140 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001141 return -EINVAL;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001142 set_stream_open(codec, STREAM_INDEP_HP, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001143 return 0;
1144}
1145
1146static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1147 struct hda_codec *codec,
1148 struct snd_pcm_substream *substream)
1149{
Takashi Iwai3b607e32011-07-18 16:54:40 +02001150 set_stream_open(codec, STREAM_INDEP_HP, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001151 return 0;
1152}
1153
1154static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1155 struct hda_codec *codec,
1156 unsigned int stream_tag,
1157 unsigned int format,
1158 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001159{
1160 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001161
Takashi Iwai3b607e32011-07-18 16:54:40 +02001162 mutex_lock(&spec->config_mutex);
1163 setup_playback_multi_pcm(spec);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001164 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1165 format, substream);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001166 /* remember for dynamic DAC switch with indep-HP */
1167 spec->active_streams |= STREAM_MULTI_OUT;
1168 spec->cur_dac_stream_tag = stream_tag;
1169 spec->cur_dac_format = format;
1170 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001171 vt1708_update_hp_work(spec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001172 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001173}
1174
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001175static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1176 struct hda_codec *codec,
1177 unsigned int stream_tag,
1178 unsigned int format,
1179 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001180{
1181 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001182
Takashi Iwai3b607e32011-07-18 16:54:40 +02001183 mutex_lock(&spec->config_mutex);
1184 if (spec->hp_independent_mode)
1185 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1186 stream_tag, 0, format);
1187 spec->active_streams |= STREAM_INDEP_HP;
1188 spec->cur_hp_stream_tag = stream_tag;
1189 spec->cur_hp_format = format;
1190 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001191 vt1708_update_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001192 return 0;
1193}
1194
1195static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1196 struct hda_codec *codec,
1197 struct snd_pcm_substream *substream)
1198{
1199 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001200
Takashi Iwai3b607e32011-07-18 16:54:40 +02001201 mutex_lock(&spec->config_mutex);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001202 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001203 spec->active_streams &= ~STREAM_MULTI_OUT;
1204 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001205 vt1708_update_hp_work(spec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001206 return 0;
1207}
1208
1209static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1210 struct hda_codec *codec,
1211 struct snd_pcm_substream *substream)
1212{
1213 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001214
Takashi Iwai3b607e32011-07-18 16:54:40 +02001215 mutex_lock(&spec->config_mutex);
1216 if (spec->hp_independent_mode)
1217 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
1218 spec->active_streams &= ~STREAM_INDEP_HP;
1219 mutex_unlock(&spec->config_mutex);
Takashi Iwai187d3332011-11-24 16:33:09 +01001220 vt1708_update_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001221 return 0;
1222}
1223
Joseph Chanc577b8a2006-11-29 15:29:40 +01001224/*
1225 * Digital out
1226 */
1227static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1228 struct hda_codec *codec,
1229 struct snd_pcm_substream *substream)
1230{
1231 struct via_spec *spec = codec->spec;
1232 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1233}
1234
1235static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1236 struct hda_codec *codec,
1237 struct snd_pcm_substream *substream)
1238{
1239 struct via_spec *spec = codec->spec;
1240 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1241}
1242
Harald Welte5691ec72008-09-15 22:42:26 +08001243static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001244 struct hda_codec *codec,
1245 unsigned int stream_tag,
1246 unsigned int format,
1247 struct snd_pcm_substream *substream)
1248{
1249 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001250 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1251 stream_tag, format, substream);
1252}
Harald Welte5691ec72008-09-15 22:42:26 +08001253
Takashi Iwai9da29272009-05-07 16:31:14 +02001254static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1255 struct hda_codec *codec,
1256 struct snd_pcm_substream *substream)
1257{
1258 struct via_spec *spec = codec->spec;
1259 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001260 return 0;
1261}
1262
Joseph Chanc577b8a2006-11-29 15:29:40 +01001263/*
1264 * Analog capture
1265 */
1266static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1267 struct hda_codec *codec,
1268 unsigned int stream_tag,
1269 unsigned int format,
1270 struct snd_pcm_substream *substream)
1271{
1272 struct via_spec *spec = codec->spec;
1273
1274 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1275 stream_tag, 0, format);
1276 return 0;
1277}
1278
1279static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1280 struct hda_codec *codec,
1281 struct snd_pcm_substream *substream)
1282{
1283 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001284 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001285 return 0;
1286}
1287
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001288/* analog capture with dynamic ADC switching */
1289static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1290 struct hda_codec *codec,
1291 unsigned int stream_tag,
1292 unsigned int format,
1293 struct snd_pcm_substream *substream)
1294{
1295 struct via_spec *spec = codec->spec;
1296 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1297
Takashi Iwai3b607e32011-07-18 16:54:40 +02001298 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001299 spec->cur_adc = spec->adc_nids[adc_idx];
1300 spec->cur_adc_stream_tag = stream_tag;
1301 spec->cur_adc_format = format;
1302 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001303 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001304 return 0;
1305}
1306
1307static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1308 struct hda_codec *codec,
1309 struct snd_pcm_substream *substream)
1310{
1311 struct via_spec *spec = codec->spec;
1312
Takashi Iwai3b607e32011-07-18 16:54:40 +02001313 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001314 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1315 spec->cur_adc = 0;
Takashi Iwai3b607e32011-07-18 16:54:40 +02001316 mutex_unlock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001317 return 0;
1318}
1319
1320/* re-setup the stream if running; called from input-src put */
1321static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1322{
1323 struct via_spec *spec = codec->spec;
1324 int adc_idx = spec->inputs[cur].adc_idx;
1325 hda_nid_t adc = spec->adc_nids[adc_idx];
Takashi Iwai3b607e32011-07-18 16:54:40 +02001326 bool ret = false;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001327
Takashi Iwai3b607e32011-07-18 16:54:40 +02001328 mutex_lock(&spec->config_mutex);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001329 if (spec->cur_adc && spec->cur_adc != adc) {
1330 /* stream is running, let's swap the current ADC */
1331 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1332 spec->cur_adc = adc;
1333 snd_hda_codec_setup_stream(codec, adc,
1334 spec->cur_adc_stream_tag, 0,
1335 spec->cur_adc_format);
Takashi Iwai3b607e32011-07-18 16:54:40 +02001336 ret = true;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001337 }
Takashi Iwai3b607e32011-07-18 16:54:40 +02001338 mutex_unlock(&spec->config_mutex);
1339 return ret;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001340}
1341
Takashi Iwai9af74212011-06-18 16:17:45 +02001342static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001343 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001344 .channels_min = 2,
1345 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001346 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001347 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001348 .open = via_playback_multi_pcm_open,
1349 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001350 .prepare = via_playback_multi_pcm_prepare,
1351 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001352 },
1353};
1354
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001355static const struct hda_pcm_stream via_pcm_hp_playback = {
1356 .substreams = 1,
1357 .channels_min = 2,
1358 .channels_max = 2,
1359 /* NID is set in via_build_pcms */
1360 .ops = {
1361 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001362 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001363 .prepare = via_playback_hp_pcm_prepare,
1364 .cleanup = via_playback_hp_pcm_cleanup
1365 },
1366};
1367
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001368static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001369 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001370 .channels_min = 2,
1371 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001372 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001373 /* We got noisy outputs on the right channel on VT1708 when
1374 * 24bit samples are used. Until any workaround is found,
1375 * disable the 24bit format, so far.
1376 */
1377 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1378 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001379 .open = via_playback_multi_pcm_open,
1380 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001381 .prepare = via_playback_multi_pcm_prepare,
1382 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001383 },
1384};
1385
Takashi Iwai9af74212011-06-18 16:17:45 +02001386static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001387 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001388 .channels_min = 2,
1389 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001390 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001391 .ops = {
1392 .prepare = via_capture_pcm_prepare,
1393 .cleanup = via_capture_pcm_cleanup
1394 },
1395};
1396
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001397static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1398 .substreams = 1,
1399 .channels_min = 2,
1400 .channels_max = 2,
1401 /* NID is set in via_build_pcms */
1402 .ops = {
1403 .prepare = via_dyn_adc_capture_pcm_prepare,
1404 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1405 },
1406};
1407
Takashi Iwai9af74212011-06-18 16:17:45 +02001408static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001409 .substreams = 1,
1410 .channels_min = 2,
1411 .channels_max = 2,
1412 /* NID is set in via_build_pcms */
1413 .ops = {
1414 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001415 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001416 .prepare = via_dig_playback_pcm_prepare,
1417 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001418 },
1419};
1420
Takashi Iwai9af74212011-06-18 16:17:45 +02001421static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001422 .substreams = 1,
1423 .channels_min = 2,
1424 .channels_max = 2,
1425};
1426
Takashi Iwai370bafb2011-06-20 12:47:45 +02001427/*
1428 * slave controls for virtual master
1429 */
1430static const char * const via_slave_vols[] = {
1431 "Front Playback Volume",
1432 "Surround Playback Volume",
1433 "Center Playback Volume",
1434 "LFE Playback Volume",
1435 "Side Playback Volume",
1436 "Headphone Playback Volume",
1437 "Speaker Playback Volume",
1438 NULL,
1439};
1440
1441static const char * const via_slave_sws[] = {
1442 "Front Playback Switch",
1443 "Surround Playback Switch",
1444 "Center Playback Switch",
1445 "LFE Playback Switch",
1446 "Side Playback Switch",
1447 "Headphone Playback Switch",
1448 "Speaker Playback Switch",
1449 NULL,
1450};
1451
Joseph Chanc577b8a2006-11-29 15:29:40 +01001452static int via_build_controls(struct hda_codec *codec)
1453{
1454 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001455 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001456 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001457
Takashi Iwai24088a52011-06-17 16:59:21 +02001458 if (spec->set_widgets_power_state)
1459 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1460 return -ENOMEM;
1461
Joseph Chanc577b8a2006-11-29 15:29:40 +01001462 for (i = 0; i < spec->num_mixers; i++) {
1463 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1464 if (err < 0)
1465 return err;
1466 }
1467
1468 if (spec->multiout.dig_out_nid) {
1469 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001470 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001471 spec->multiout.dig_out_nid);
1472 if (err < 0)
1473 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001474 err = snd_hda_create_spdif_share_sw(codec,
1475 &spec->multiout);
1476 if (err < 0)
1477 return err;
1478 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001479 }
1480 if (spec->dig_in_nid) {
1481 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1482 if (err < 0)
1483 return err;
1484 }
Lydia Wang17314372009-10-10 19:07:37 +08001485
Takashi Iwai370bafb2011-06-20 12:47:45 +02001486 /* if we have no master control, let's create it */
1487 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1488 unsigned int vmaster_tlv[4];
1489 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1490 HDA_OUTPUT, vmaster_tlv);
1491 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1492 vmaster_tlv, via_slave_vols);
1493 if (err < 0)
1494 return err;
1495 }
1496 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1497 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1498 NULL, via_slave_sws);
1499 if (err < 0)
1500 return err;
1501 }
1502
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001503 /* assign Capture Source enums to NID */
1504 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1505 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001506 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001507 if (err < 0)
1508 return err;
1509 }
1510
Lydia Wang17314372009-10-10 19:07:37 +08001511 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001512 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001513 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001514
Takashi Iwai603c4012008-07-30 15:01:44 +02001515 via_free_kctls(codec); /* no longer needed */
Takashi Iwai01a61e12011-10-28 00:03:22 +02001516
1517 err = snd_hda_jack_add_kctls(codec, &spec->autocfg);
1518 if (err < 0)
1519 return err;
1520
Joseph Chanc577b8a2006-11-29 15:29:40 +01001521 return 0;
1522}
1523
1524static int via_build_pcms(struct hda_codec *codec)
1525{
1526 struct via_spec *spec = codec->spec;
1527 struct hda_pcm *info = spec->pcm_rec;
1528
Takashi Iwaia5973102011-09-28 16:43:36 +02001529 codec->num_pcms = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001530 codec->pcm_info = info;
1531
Takashi Iwaia5973102011-09-28 16:43:36 +02001532 if (spec->multiout.num_dacs || spec->num_adc_nids) {
1533 snprintf(spec->stream_name_analog,
1534 sizeof(spec->stream_name_analog),
1535 "%s Analog", codec->chip_name);
1536 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001537
Takashi Iwaia5973102011-09-28 16:43:36 +02001538 if (spec->multiout.num_dacs) {
1539 if (!spec->stream_analog_playback)
1540 spec->stream_analog_playback =
1541 &via_pcm_analog_playback;
1542 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
1543 *spec->stream_analog_playback;
1544 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1545 spec->multiout.dac_nids[0];
1546 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1547 spec->multiout.max_channels;
1548 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001549
Takashi Iwaia5973102011-09-28 16:43:36 +02001550 if (!spec->stream_analog_capture) {
1551 if (spec->dyn_adc_switch)
1552 spec->stream_analog_capture =
1553 &via_pcm_dyn_adc_analog_capture;
1554 else
1555 spec->stream_analog_capture =
1556 &via_pcm_analog_capture;
1557 }
1558 if (spec->num_adc_nids) {
1559 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1560 *spec->stream_analog_capture;
1561 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1562 spec->adc_nids[0];
1563 if (!spec->dyn_adc_switch)
1564 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1565 spec->num_adc_nids;
1566 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001567 codec->num_pcms++;
1568 info++;
Takashi Iwaia5973102011-09-28 16:43:36 +02001569 }
1570
1571 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
Takashi Iwai82673bc2011-06-17 16:24:21 +02001572 snprintf(spec->stream_name_digital,
1573 sizeof(spec->stream_name_digital),
1574 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001575 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001576 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001577 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001578 if (!spec->stream_digital_playback)
1579 spec->stream_digital_playback =
1580 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001581 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001582 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001583 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1584 spec->multiout.dig_out_nid;
1585 }
1586 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001587 if (!spec->stream_digital_capture)
1588 spec->stream_digital_capture =
1589 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001590 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001591 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001592 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1593 spec->dig_in_nid;
1594 }
Takashi Iwaia5973102011-09-28 16:43:36 +02001595 codec->num_pcms++;
1596 info++;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001597 }
1598
Takashi Iwaiece8d042011-06-19 16:24:21 +02001599 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001600 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1601 "%s HP", codec->chip_name);
1602 info->name = spec->stream_name_hp;
1603 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1604 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001605 spec->hp_dac_nid;
Takashi Iwaia5973102011-09-28 16:43:36 +02001606 codec->num_pcms++;
1607 info++;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001608 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001609 return 0;
1610}
1611
1612static void via_free(struct hda_codec *codec)
1613{
1614 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001615
1616 if (!spec)
1617 return;
1618
Takashi Iwai603c4012008-07-30 15:01:44 +02001619 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001620 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001621 kfree(spec->bind_cap_vol);
1622 kfree(spec->bind_cap_sw);
1623 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001624}
1625
Takashi Iwai64be2852011-06-17 16:51:39 +02001626/* mute/unmute outputs */
1627static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1628 hda_nid_t *pins, bool mute)
1629{
1630 int i;
Takashi Iwai94994732011-07-11 11:36:44 +02001631 for (i = 0; i < num_pins; i++) {
1632 unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
1633 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1634 if (parm & AC_PINCTL_IN_EN)
1635 continue;
1636 if (mute)
1637 parm &= ~AC_PINCTL_OUT_EN;
1638 else
1639 parm |= AC_PINCTL_OUT_EN;
Takashi Iwai64be2852011-06-17 16:51:39 +02001640 snd_hda_codec_write(codec, pins[i], 0,
Takashi Iwai94994732011-07-11 11:36:44 +02001641 AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
1642 }
Takashi Iwai64be2852011-06-17 16:51:39 +02001643}
1644
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001645/* mute internal speaker if line-out is plugged */
1646static void via_line_automute(struct hda_codec *codec, int present)
1647{
1648 struct via_spec *spec = codec->spec;
1649
1650 if (!spec->autocfg.speaker_outs)
1651 return;
1652 if (!present)
1653 present = snd_hda_jack_detect(codec,
1654 spec->autocfg.line_out_pins[0]);
1655 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1656 spec->autocfg.speaker_pins,
1657 present);
1658}
1659
Harald Welte69e52a82008-09-09 15:57:32 +08001660/* mute internal speaker if HP is plugged */
1661static void via_hp_automute(struct hda_codec *codec)
1662{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001663 int present = 0;
Takashi Iwai6e969d92011-07-11 11:28:13 +02001664 int nums;
Harald Welte69e52a82008-09-09 15:57:32 +08001665 struct via_spec *spec = codec->spec;
1666
Takashi Iwai187d3332011-11-24 16:33:09 +01001667 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0] &&
1668 (spec->codec_type != VT1708 || spec->vt1708_jack_detect))
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001669 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai6e969d92011-07-11 11:28:13 +02001670
1671 if (spec->smart51_enabled)
1672 nums = spec->autocfg.line_outs + spec->smart51_nums;
1673 else
1674 nums = spec->autocfg.line_outs;
1675 toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
1676
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001677 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001678}
1679
Harald Welte69e52a82008-09-09 15:57:32 +08001680static void via_gpio_control(struct hda_codec *codec)
1681{
1682 unsigned int gpio_data;
1683 unsigned int vol_counter;
1684 unsigned int vol;
1685 unsigned int master_vol;
1686
1687 struct via_spec *spec = codec->spec;
1688
1689 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1690 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1691
1692 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1693 0xF84, 0) & 0x3F0000) >> 16;
1694
1695 vol = vol_counter & 0x1F;
1696 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1697 AC_VERB_GET_AMP_GAIN_MUTE,
1698 AC_AMP_GET_INPUT);
1699
1700 if (gpio_data == 0x02) {
1701 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001702 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1703 AC_VERB_SET_PIN_WIDGET_CONTROL,
1704 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001705 if (vol_counter & 0x20) {
1706 /* decrease volume */
1707 if (vol > master_vol)
1708 vol = master_vol;
1709 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1710 0, HDA_AMP_VOLMASK,
1711 master_vol-vol);
1712 } else {
1713 /* increase volume */
1714 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1715 HDA_AMP_VOLMASK,
1716 ((master_vol+vol) > 0x2A) ? 0x2A :
1717 (master_vol+vol));
1718 }
1719 } else if (!(gpio_data & 0x02)) {
1720 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001721 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1722 AC_VERB_SET_PIN_WIDGET_CONTROL,
1723 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001724 }
1725}
1726
1727/* unsolicited event for jack sensing */
1728static void via_unsol_event(struct hda_codec *codec,
1729 unsigned int res)
1730{
1731 res >>= 26;
Takashi Iwai3a938972011-10-28 01:16:55 +02001732 res = snd_hda_jack_get_action(codec, res);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001733
Lydia Wanga34df192009-10-10 19:08:01 +08001734 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001735 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001736
1737 res &= ~VIA_JACK_EVENT;
1738
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001739 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001740 via_hp_automute(codec);
1741 else if (res == VIA_GPIO_EVENT)
1742 via_gpio_control(codec);
Takashi Iwai01a61e12011-10-28 00:03:22 +02001743 snd_hda_jack_report_sync(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001744}
1745
Takashi Iwai2a439522011-07-26 09:52:50 +02001746#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001747static int via_suspend(struct hda_codec *codec, pm_message_t state)
1748{
1749 struct via_spec *spec = codec->spec;
1750 vt1708_stop_hp_work(spec);
1751 return 0;
1752}
1753#endif
1754
Takashi Iwaicb53c622007-08-10 17:21:45 +02001755#ifdef CONFIG_SND_HDA_POWER_SAVE
1756static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1757{
1758 struct via_spec *spec = codec->spec;
1759 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1760}
1761#endif
1762
Joseph Chanc577b8a2006-11-29 15:29:40 +01001763/*
1764 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001765
1766static int via_init(struct hda_codec *codec);
1767
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001768static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001769 .build_controls = via_build_controls,
1770 .build_pcms = via_build_pcms,
1771 .init = via_init,
1772 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001773 .unsol_event = via_unsol_event,
Takashi Iwai2a439522011-07-26 09:52:50 +02001774#ifdef CONFIG_PM
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001775 .suspend = via_suspend,
1776#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001777#ifdef CONFIG_SND_HDA_POWER_SAVE
1778 .check_power_status = via_check_power_status,
1779#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001780};
1781
Takashi Iwai4a796162011-06-17 17:53:38 +02001782static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001783{
Takashi Iwai4a796162011-06-17 17:53:38 +02001784 struct via_spec *spec = codec->spec;
1785 int i;
1786
1787 for (i = 0; i < spec->multiout.num_dacs; i++) {
1788 if (spec->multiout.dac_nids[i] == dac)
1789 return false;
1790 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001791 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001792 return false;
1793 return true;
1794}
1795
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001796static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001797 hda_nid_t target_dac, int with_aa_mix,
1798 struct nid_path *path, int depth)
Takashi Iwai4a796162011-06-17 17:53:38 +02001799{
Takashi Iwai3214b962011-07-18 12:49:25 +02001800 struct via_spec *spec = codec->spec;
Takashi Iwai4a796162011-06-17 17:53:38 +02001801 hda_nid_t conn[8];
1802 int i, nums;
1803
Takashi Iwai3214b962011-07-18 12:49:25 +02001804 if (nid == spec->aa_mix_nid) {
1805 if (!with_aa_mix)
1806 return false;
1807 with_aa_mix = 2; /* mark aa-mix is included */
1808 }
1809
Takashi Iwai4a796162011-06-17 17:53:38 +02001810 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1811 for (i = 0; i < nums; i++) {
1812 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1813 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001814 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1815 /* aa-mix is requested but not included? */
1816 if (!(spec->aa_mix_nid && with_aa_mix == 1))
1817 goto found;
1818 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001819 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001820 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001821 return false;
1822 for (i = 0; i < nums; i++) {
1823 unsigned int type;
1824 type = get_wcaps_type(get_wcaps(codec, conn[i]));
Takashi Iwai3214b962011-07-18 12:49:25 +02001825 if (type == AC_WID_AUD_OUT)
Takashi Iwai4a796162011-06-17 17:53:38 +02001826 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001827 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai3214b962011-07-18 12:49:25 +02001828 with_aa_mix, path, depth + 1))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001829 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001830 }
1831 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001832
1833 found:
1834 path->path[path->depth] = conn[i];
1835 path->idx[path->depth] = i;
1836 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1837 path->multi[path->depth] = 1;
1838 path->depth++;
1839 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001840}
1841
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001842static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001843 hda_nid_t target_dac, int with_aa_mix,
1844 struct nid_path *path)
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001845{
Takashi Iwai3214b962011-07-18 12:49:25 +02001846 if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001847 path->path[path->depth] = nid;
1848 path->depth++;
Takashi Iwai3214b962011-07-18 12:49:25 +02001849 snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
1850 path->depth, path->path[0], path->path[1],
1851 path->path[2], path->path[3], path->path[4]);
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001852 return true;
1853 }
1854 return false;
1855}
1856
Takashi Iwai4a796162011-06-17 17:53:38 +02001857static int via_auto_fill_dac_nids(struct hda_codec *codec)
1858{
1859 struct via_spec *spec = codec->spec;
1860 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001861 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001862 hda_nid_t nid;
1863
Joseph Chanc577b8a2006-11-29 15:29:40 +01001864 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001865 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001866 for (i = 0; i < cfg->line_outs; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001867 hda_nid_t dac = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001868 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001869 if (!nid)
1870 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001871 if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
1872 dac = spec->out_path[i].path[0];
1873 if (!i && parse_output_path(codec, nid, dac, 1,
1874 &spec->out_mix_path))
1875 dac = spec->out_mix_path.path[0];
1876 if (dac) {
1877 spec->private_dac_nids[i] = dac;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001878 dac_num++;
1879 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001880 }
Takashi Iwai3214b962011-07-18 12:49:25 +02001881 if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
1882 spec->out_path[0] = spec->out_mix_path;
1883 spec->out_mix_path.depth = 0;
1884 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001885 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001886 return 0;
1887}
1888
Takashi Iwai4a796162011-06-17 17:53:38 +02001889static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001890 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001891{
Takashi Iwai4a796162011-06-17 17:53:38 +02001892 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001893 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001894 hda_nid_t dac, pin, sel, nid;
1895 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001896
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001897 dac = check_dac ? path->path[0] : 0;
1898 pin = path->path[path->depth - 1];
1899 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001900
Takashi Iwai8df2a312011-06-21 11:48:29 +02001901 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001902 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001903 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001904 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001905 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1906 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001907 else
1908 nid = 0;
1909 if (nid) {
1910 sprintf(name, "%s Playback Volume", pfx);
1911 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001912 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001913 if (err < 0)
1914 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001915 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001916 }
1917
Takashi Iwai8df2a312011-06-21 11:48:29 +02001918 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001919 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001920 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001921 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001922 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1923 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001924 else
1925 nid = 0;
1926 if (nid) {
1927 sprintf(name, "%s Playback Switch", pfx);
1928 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1929 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1930 if (err < 0)
1931 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001932 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001933 }
1934 return 0;
1935}
1936
Takashi Iwaif4a78282011-06-17 18:46:48 +02001937static void mangle_smart51(struct hda_codec *codec)
1938{
1939 struct via_spec *spec = codec->spec;
1940 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001941 struct auto_pin_cfg_item *ins = cfg->inputs;
1942 int i, j, nums, attr;
1943 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001944
Takashi Iwai0f98c242011-06-21 12:51:33 +02001945 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1946 nums = 0;
1947 for (i = 0; i < cfg->num_inputs; i++) {
1948 unsigned int def;
1949 if (ins[i].type > AUTO_PIN_LINE_IN)
1950 continue;
1951 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1952 if (snd_hda_get_input_pin_attr(def) != attr)
1953 continue;
1954 for (j = 0; j < nums; j++)
1955 if (ins[pins[j]].type < ins[i].type) {
1956 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001957 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001958 break;
1959 }
1960 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001961 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001962 }
1963 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001964 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001965 for (i = 0; i < nums; i++) {
1966 hda_nid_t pin = ins[pins[i]].pin;
1967 spec->smart51_pins[spec->smart51_nums++] = pin;
1968 cfg->line_out_pins[cfg->line_outs++] = pin;
1969 if (cfg->line_outs == 3)
1970 break;
1971 }
1972 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001973 }
1974}
1975
Takashi Iwai020066d2011-07-21 13:45:56 +02001976static void copy_path_mixer_ctls(struct nid_path *dst, struct nid_path *src)
1977{
1978 dst->vol_ctl = src->vol_ctl;
1979 dst->mute_ctl = src->mute_ctl;
1980}
1981
Takashi Iwai4a796162011-06-17 17:53:38 +02001982/* add playback controls from the parsed DAC table */
1983static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1984{
1985 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001986 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai3214b962011-07-18 12:49:25 +02001987 struct nid_path *path;
Takashi Iwaiea734962011-01-17 11:29:34 +01001988 static const char * const chname[4] = {
1989 "Front", "Surround", "C/LFE", "Side"
1990 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001991 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001992 int old_line_outs;
1993
1994 /* check smart51 */
1995 old_line_outs = cfg->line_outs;
1996 if (cfg->line_outs == 1)
1997 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001998
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001999 err = via_auto_fill_dac_nids(codec);
2000 if (err < 0)
2001 return err;
2002
Lydia Wang5c9a5612011-07-08 14:03:43 +08002003 if (spec->multiout.num_dacs < 3) {
2004 spec->smart51_nums = 0;
2005 cfg->line_outs = old_line_outs;
2006 }
Takashi Iwai4a796162011-06-17 17:53:38 +02002007 for (i = 0; i < cfg->line_outs; i++) {
2008 hda_nid_t pin, dac;
2009 pin = cfg->line_out_pins[i];
2010 dac = spec->multiout.dac_nids[i];
2011 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002012 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02002013 path = spec->out_path + i;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02002014 if (i == HDA_CLFE) {
Takashi Iwai3214b962011-07-18 12:49:25 +02002015 err = create_ch_ctls(codec, "Center", 1, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002016 if (err < 0)
2017 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002018 err = create_ch_ctls(codec, "LFE", 2, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002019 if (err < 0)
2020 return err;
2021 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02002022 const char *pfx = chname[i];
2023 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
2024 cfg->line_outs == 1)
2025 pfx = "Speaker";
Takashi Iwai3214b962011-07-18 12:49:25 +02002026 err = create_ch_ctls(codec, pfx, 3, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002027 if (err < 0)
2028 return err;
2029 }
Takashi Iwai020066d2011-07-21 13:45:56 +02002030 if (path != spec->out_path + i)
2031 copy_path_mixer_ctls(&spec->out_path[i], path);
2032 if (path == spec->out_path && spec->out_mix_path.depth)
2033 copy_path_mixer_ctls(&spec->out_mix_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002034 }
2035
Takashi Iwai4a796162011-06-17 17:53:38 +02002036 idx = get_connection_index(codec, spec->aa_mix_nid,
2037 spec->multiout.dac_nids[0]);
2038 if (idx >= 0) {
2039 /* add control to mixer */
Takashi Iwai3214b962011-07-18 12:49:25 +02002040 const char *name;
2041 name = spec->out_mix_path.depth ?
2042 "PCM Loopback Playback Volume" : "PCM Playback Volume";
2043 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002044 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2045 idx, HDA_INPUT));
2046 if (err < 0)
2047 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002048 name = spec->out_mix_path.depth ?
2049 "PCM Loopback Playback Switch" : "PCM Playback Switch";
2050 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02002051 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
2052 idx, HDA_INPUT));
2053 if (err < 0)
2054 return err;
2055 }
2056
Takashi Iwaif4a78282011-06-17 18:46:48 +02002057 cfg->line_outs = old_line_outs;
2058
Joseph Chanc577b8a2006-11-29 15:29:40 +01002059 return 0;
2060}
2061
Takashi Iwai4a796162011-06-17 17:53:38 +02002062static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002063{
Takashi Iwai4a796162011-06-17 17:53:38 +02002064 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002065 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002066 bool check_dac;
Takashi Iwai3214b962011-07-18 12:49:25 +02002067 int i, err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002068
2069 if (!pin)
2070 return 0;
2071
Takashi Iwai3214b962011-07-18 12:49:25 +02002072 if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
2073 for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
2074 if (i < spec->multiout.num_dacs &&
2075 parse_output_path(codec, pin,
2076 spec->multiout.dac_nids[i], 0,
2077 &spec->hp_indep_path)) {
2078 spec->hp_indep_shared = i;
2079 break;
2080 }
2081 }
Takashi Iwai25250502011-06-30 17:24:47 +02002082 }
Takashi Iwai3214b962011-07-18 12:49:25 +02002083 if (spec->hp_indep_path.depth) {
2084 spec->hp_dac_nid = spec->hp_indep_path.path[0];
2085 if (!spec->hp_indep_shared)
2086 spec->hp_path = spec->hp_indep_path;
2087 }
2088 /* optionally check front-path w/o AA-mix */
2089 if (!spec->hp_path.depth)
2090 parse_output_path(codec, pin,
2091 spec->multiout.dac_nids[HDA_FRONT], 0,
2092 &spec->hp_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002093
Takashi Iwaiece8d042011-06-19 16:24:21 +02002094 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai3214b962011-07-18 12:49:25 +02002095 1, &spec->hp_mix_path) && !spec->hp_path.depth)
Takashi Iwaiece8d042011-06-19 16:24:21 +02002096 return 0;
2097
Takashi Iwai3214b962011-07-18 12:49:25 +02002098 if (spec->hp_path.depth) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02002099 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002100 check_dac = true;
2101 } else {
Takashi Iwai3214b962011-07-18 12:49:25 +02002102 path = &spec->hp_mix_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02002103 check_dac = false;
2104 }
2105 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002106 if (err < 0)
2107 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002108 if (check_dac)
2109 copy_path_mixer_ctls(&spec->hp_mix_path, path);
2110 else
2111 copy_path_mixer_ctls(&spec->hp_path, path);
2112 if (spec->hp_indep_path.depth)
2113 copy_path_mixer_ctls(&spec->hp_indep_path, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002114 return 0;
2115}
2116
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002117static int via_auto_create_speaker_ctls(struct hda_codec *codec)
2118{
2119 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +02002120 struct nid_path *path;
2121 bool check_dac;
Wang Shaoyan81c0a782011-08-05 18:51:29 +08002122 hda_nid_t pin, dac = 0;
Takashi Iwai3214b962011-07-18 12:49:25 +02002123 int err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002124
2125 pin = spec->autocfg.speaker_pins[0];
2126 if (!spec->autocfg.speaker_outs || !pin)
2127 return 0;
2128
Takashi Iwai3214b962011-07-18 12:49:25 +02002129 if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02002130 dac = spec->speaker_path.path[0];
Takashi Iwai3214b962011-07-18 12:49:25 +02002131 if (!dac)
2132 parse_output_path(codec, pin,
2133 spec->multiout.dac_nids[HDA_FRONT], 0,
2134 &spec->speaker_path);
2135 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
2136 1, &spec->speaker_mix_path) && !dac)
2137 return 0;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002138
Takashi Iwai3214b962011-07-18 12:49:25 +02002139 /* no AA-path for front? */
2140 if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
2141 dac = 0;
2142
2143 spec->speaker_dac_nid = dac;
2144 spec->multiout.extra_out_nid[0] = dac;
2145 if (dac) {
2146 path = &spec->speaker_path;
2147 check_dac = true;
2148 } else {
2149 path = &spec->speaker_mix_path;
2150 check_dac = false;
2151 }
2152 err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
2153 if (err < 0)
2154 return err;
Takashi Iwai020066d2011-07-21 13:45:56 +02002155 if (check_dac)
2156 copy_path_mixer_ctls(&spec->speaker_mix_path, path);
2157 else
2158 copy_path_mixer_ctls(&spec->speaker_path, path);
Takashi Iwai3214b962011-07-18 12:49:25 +02002159 return 0;
2160}
2161
2162#define via_aamix_ctl_info via_pin_power_ctl_info
2163
2164static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol,
2165 struct snd_ctl_elem_value *ucontrol)
2166{
2167 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2168 struct via_spec *spec = codec->spec;
2169 ucontrol->value.enumerated.item[0] = spec->aamix_mode;
2170 return 0;
2171}
2172
2173static void update_aamix_paths(struct hda_codec *codec, int do_mix,
2174 struct nid_path *nomix, struct nid_path *mix)
2175{
2176 if (do_mix) {
2177 activate_output_path(codec, nomix, false, false);
2178 activate_output_path(codec, mix, true, false);
2179 } else {
2180 activate_output_path(codec, mix, false, false);
2181 activate_output_path(codec, nomix, true, false);
2182 }
2183}
2184
2185static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol,
2186 struct snd_ctl_elem_value *ucontrol)
2187{
2188 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2189 struct via_spec *spec = codec->spec;
2190 unsigned int val = ucontrol->value.enumerated.item[0];
2191
2192 if (val == spec->aamix_mode)
2193 return 0;
2194 spec->aamix_mode = val;
2195 /* update front path */
2196 update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
2197 /* update HP path */
2198 if (!spec->hp_independent_mode) {
2199 update_aamix_paths(codec, val, &spec->hp_path,
2200 &spec->hp_mix_path);
2201 }
2202 /* update speaker path */
2203 update_aamix_paths(codec, val, &spec->speaker_path,
2204 &spec->speaker_mix_path);
2205 return 1;
2206}
2207
2208static const struct snd_kcontrol_new via_aamix_ctl_enum = {
2209 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2210 .name = "Loopback Mixing",
2211 .info = via_aamix_ctl_info,
2212 .get = via_aamix_ctl_get,
2213 .put = via_aamix_ctl_put,
2214};
2215
2216static int via_auto_create_loopback_switch(struct hda_codec *codec)
2217{
2218 struct via_spec *spec = codec->spec;
2219
Takashi Iwai4808d122012-01-10 15:16:02 +01002220 if (!spec->aa_mix_nid)
2221 return 0; /* no loopback switching available */
2222 if (!(spec->out_mix_path.depth || spec->hp_mix_path.depth ||
2223 spec->speaker_path.depth))
Takashi Iwai3214b962011-07-18 12:49:25 +02002224 return 0; /* no loopback switching available */
2225 if (!via_clone_control(spec, &via_aamix_ctl_enum))
2226 return -ENOMEM;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002227 return 0;
2228}
2229
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002230/* look for ADCs */
2231static int via_fill_adcs(struct hda_codec *codec)
2232{
2233 struct via_spec *spec = codec->spec;
2234 hda_nid_t nid = codec->start_nid;
2235 int i;
2236
2237 for (i = 0; i < codec->num_nodes; i++, nid++) {
2238 unsigned int wcaps = get_wcaps(codec, nid);
2239 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2240 continue;
2241 if (wcaps & AC_WCAP_DIGITAL)
2242 continue;
2243 if (!(wcaps & AC_WCAP_CONN_LIST))
2244 continue;
2245 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2246 return -ENOMEM;
2247 spec->adc_nids[spec->num_adc_nids++] = nid;
2248 }
2249 return 0;
2250}
2251
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002252/* input-src control */
2253static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2254 struct snd_ctl_elem_info *uinfo)
2255{
2256 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2257 struct via_spec *spec = codec->spec;
2258
2259 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2260 uinfo->count = 1;
2261 uinfo->value.enumerated.items = spec->num_inputs;
2262 if (uinfo->value.enumerated.item >= spec->num_inputs)
2263 uinfo->value.enumerated.item = spec->num_inputs - 1;
2264 strcpy(uinfo->value.enumerated.name,
2265 spec->inputs[uinfo->value.enumerated.item].label);
2266 return 0;
2267}
2268
2269static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2270 struct snd_ctl_elem_value *ucontrol)
2271{
2272 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2273 struct via_spec *spec = codec->spec;
2274 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2275
2276 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2277 return 0;
2278}
2279
2280static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2281 struct snd_ctl_elem_value *ucontrol)
2282{
2283 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2284 struct via_spec *spec = codec->spec;
2285 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2286 hda_nid_t mux;
2287 int cur;
2288
2289 cur = ucontrol->value.enumerated.item[0];
2290 if (cur < 0 || cur >= spec->num_inputs)
2291 return -EINVAL;
2292 if (spec->cur_mux[idx] == cur)
2293 return 0;
2294 spec->cur_mux[idx] = cur;
2295 if (spec->dyn_adc_switch) {
2296 int adc_idx = spec->inputs[cur].adc_idx;
2297 mux = spec->mux_nids[adc_idx];
2298 via_dyn_adc_pcm_resetup(codec, cur);
2299 } else {
2300 mux = spec->mux_nids[idx];
2301 if (snd_BUG_ON(!mux))
2302 return -EINVAL;
2303 }
2304
2305 if (mux) {
2306 /* switch to D0 beofre change index */
Takashi Iwai054d8672012-01-24 12:25:50 +01002307 update_power_state(codec, mux, AC_PWRST_D0);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002308 snd_hda_codec_write(codec, mux, 0,
2309 AC_VERB_SET_CONNECT_SEL,
2310 spec->inputs[cur].mux_idx);
2311 }
2312
2313 /* update jack power state */
2314 set_widgets_power_state(codec);
2315 return 0;
2316}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002317
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002318static const struct snd_kcontrol_new via_input_src_ctl = {
2319 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2320 /* The multiple "Capture Source" controls confuse alsamixer
2321 * So call somewhat different..
2322 */
2323 /* .name = "Capture Source", */
2324 .name = "Input Source",
2325 .info = via_mux_enum_info,
2326 .get = via_mux_enum_get,
2327 .put = via_mux_enum_put,
2328};
2329
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002330static int create_input_src_ctls(struct hda_codec *codec, int count)
2331{
2332 struct via_spec *spec = codec->spec;
2333 struct snd_kcontrol_new *knew;
2334
2335 if (spec->num_inputs <= 1 || !count)
2336 return 0; /* no need for single src */
2337
2338 knew = via_clone_control(spec, &via_input_src_ctl);
2339 if (!knew)
2340 return -ENOMEM;
2341 knew->count = count;
2342 return 0;
2343}
2344
2345/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002346static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2347{
2348 struct hda_amp_list *list;
2349
2350 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2351 return;
2352 list = spec->loopback_list + spec->num_loopbacks;
2353 list->nid = mix;
2354 list->dir = HDA_INPUT;
2355 list->idx = idx;
2356 spec->num_loopbacks++;
2357 spec->loopback.amplist = spec->loopback_list;
2358}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002359
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002360static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002361 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002362{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002363 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002364}
2365
2366/* add the input-route to the given pin */
2367static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002368{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002369 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002370 int c, idx;
2371
2372 spec->inputs[spec->num_inputs].adc_idx = -1;
2373 spec->inputs[spec->num_inputs].pin = pin;
2374 for (c = 0; c < spec->num_adc_nids; c++) {
2375 if (spec->mux_nids[c]) {
2376 idx = get_connection_index(codec, spec->mux_nids[c],
2377 pin);
2378 if (idx < 0)
2379 continue;
2380 spec->inputs[spec->num_inputs].mux_idx = idx;
2381 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002382 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002383 continue;
2384 }
2385 spec->inputs[spec->num_inputs].adc_idx = c;
2386 /* Can primary ADC satisfy all inputs? */
2387 if (!spec->dyn_adc_switch &&
2388 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2389 snd_printd(KERN_INFO
2390 "via: dynamic ADC switching enabled\n");
2391 spec->dyn_adc_switch = 1;
2392 }
2393 return true;
2394 }
2395 return false;
2396}
2397
2398static int get_mux_nids(struct hda_codec *codec);
2399
2400/* parse input-routes; fill ADCs, MUXs and input-src entries */
2401static int parse_analog_inputs(struct hda_codec *codec)
2402{
2403 struct via_spec *spec = codec->spec;
2404 const struct auto_pin_cfg *cfg = &spec->autocfg;
2405 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002406
2407 err = via_fill_adcs(codec);
2408 if (err < 0)
2409 return err;
2410 err = get_mux_nids(codec);
2411 if (err < 0)
2412 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002413
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002414 /* fill all input-routes */
2415 for (i = 0; i < cfg->num_inputs; i++) {
2416 if (add_input_route(codec, cfg->inputs[i].pin))
2417 spec->inputs[spec->num_inputs++].label =
2418 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002419 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002420
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002421 /* check for internal loopback recording */
2422 if (spec->aa_mix_nid &&
2423 add_input_route(codec, spec->aa_mix_nid))
2424 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2425
2426 return 0;
2427}
2428
2429/* create analog-loopback volume/switch controls */
2430static int create_loopback_ctls(struct hda_codec *codec)
2431{
2432 struct via_spec *spec = codec->spec;
2433 const struct auto_pin_cfg *cfg = &spec->autocfg;
2434 const char *prev_label = NULL;
2435 int type_idx = 0;
2436 int i, j, err, idx;
2437
2438 if (!spec->aa_mix_nid)
2439 return 0;
2440
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002441 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002442 hda_nid_t pin = cfg->inputs[i].pin;
2443 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2444
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002445 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002446 type_idx++;
2447 else
2448 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002449 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002450 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2451 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002452 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002453 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002454 if (err < 0)
2455 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002456 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002457 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002458
2459 /* remember the label for smart51 control */
2460 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002461 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002462 spec->smart51_idxs[j] = idx;
2463 spec->smart51_labels[j] = label;
2464 break;
2465 }
2466 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002467 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002468 return 0;
2469}
2470
2471/* create mic-boost controls (if present) */
2472static int create_mic_boost_ctls(struct hda_codec *codec)
2473{
2474 struct via_spec *spec = codec->spec;
2475 const struct auto_pin_cfg *cfg = &spec->autocfg;
2476 int i, err;
2477
2478 for (i = 0; i < cfg->num_inputs; i++) {
2479 hda_nid_t pin = cfg->inputs[i].pin;
2480 unsigned int caps;
2481 const char *label;
2482 char name[32];
2483
2484 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2485 continue;
2486 caps = query_amp_caps(codec, pin, HDA_INPUT);
2487 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2488 continue;
2489 label = hda_get_autocfg_input_label(codec, cfg, i);
2490 snprintf(name, sizeof(name), "%s Boost Volume", label);
2491 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2492 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2493 if (err < 0)
2494 return err;
2495 }
2496 return 0;
2497}
2498
2499/* create capture and input-src controls for multiple streams */
2500static int create_multi_adc_ctls(struct hda_codec *codec)
2501{
2502 struct via_spec *spec = codec->spec;
2503 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002504
2505 /* create capture mixer elements */
2506 for (i = 0; i < spec->num_adc_nids; i++) {
2507 hda_nid_t adc = spec->adc_nids[i];
2508 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2509 "Capture Volume", i,
2510 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2511 HDA_INPUT));
2512 if (err < 0)
2513 return err;
2514 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2515 "Capture Switch", i,
2516 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2517 HDA_INPUT));
2518 if (err < 0)
2519 return err;
2520 }
2521
2522 /* input-source control */
2523 for (i = 0; i < spec->num_adc_nids; i++)
2524 if (!spec->mux_nids[i])
2525 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002526 err = create_input_src_ctls(codec, i);
2527 if (err < 0)
2528 return err;
2529 return 0;
2530}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002531
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002532/* bind capture volume/switch */
2533static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2534 HDA_BIND_VOL("Capture Volume", 0);
2535static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2536 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002537
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002538static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2539 struct hda_ctl_ops *ops)
2540{
2541 struct hda_bind_ctls *ctl;
2542 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002543
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002544 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2545 if (!ctl)
2546 return -ENOMEM;
2547 ctl->ops = ops;
2548 for (i = 0; i < spec->num_adc_nids; i++)
2549 ctl->values[i] =
2550 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2551 *ctl_ret = ctl;
2552 return 0;
2553}
2554
2555/* create capture and input-src controls for dynamic ADC-switch case */
2556static int create_dyn_adc_ctls(struct hda_codec *codec)
2557{
2558 struct via_spec *spec = codec->spec;
2559 struct snd_kcontrol_new *knew;
2560 int err;
2561
2562 /* set up the bind capture ctls */
2563 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2564 if (err < 0)
2565 return err;
2566 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2567 if (err < 0)
2568 return err;
2569
2570 /* create capture mixer elements */
2571 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2572 if (!knew)
2573 return -ENOMEM;
2574 knew->private_value = (long)spec->bind_cap_vol;
2575
2576 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2577 if (!knew)
2578 return -ENOMEM;
2579 knew->private_value = (long)spec->bind_cap_sw;
2580
2581 /* input-source control */
2582 err = create_input_src_ctls(codec, 1);
2583 if (err < 0)
2584 return err;
2585 return 0;
2586}
2587
2588/* parse and create capture-related stuff */
2589static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2590{
2591 struct via_spec *spec = codec->spec;
2592 int err;
2593
2594 err = parse_analog_inputs(codec);
2595 if (err < 0)
2596 return err;
2597 if (spec->dyn_adc_switch)
2598 err = create_dyn_adc_ctls(codec);
2599 else
2600 err = create_multi_adc_ctls(codec);
2601 if (err < 0)
2602 return err;
2603 err = create_loopback_ctls(codec);
2604 if (err < 0)
2605 return err;
2606 err = create_mic_boost_ctls(codec);
2607 if (err < 0)
2608 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002609 return 0;
2610}
2611
Harald Welte76d9b0d2008-09-09 15:50:37 +08002612static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2613{
2614 unsigned int def_conf;
2615 unsigned char seqassoc;
2616
Takashi Iwai2f334f92009-02-20 14:37:42 +01002617 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002618 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2619 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002620 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2621 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2622 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2623 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002624 }
2625
2626 return;
2627}
2628
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002629static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002630 struct snd_ctl_elem_value *ucontrol)
2631{
2632 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2633 struct via_spec *spec = codec->spec;
2634
2635 if (spec->codec_type != VT1708)
2636 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002637 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002638 return 0;
2639}
2640
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002641static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002642 struct snd_ctl_elem_value *ucontrol)
2643{
2644 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2645 struct via_spec *spec = codec->spec;
Takashi Iwai187d3332011-11-24 16:33:09 +01002646 int val;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002647
2648 if (spec->codec_type != VT1708)
2649 return 0;
Takashi Iwai187d3332011-11-24 16:33:09 +01002650 val = !!ucontrol->value.integer.value[0];
2651 if (spec->vt1708_jack_detect == val)
2652 return 0;
2653 spec->vt1708_jack_detect = val;
2654 if (spec->vt1708_jack_detect &&
2655 snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") != 1) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002656 mute_aa_path(codec, 1);
2657 notify_aa_path_ctls(codec);
2658 }
Takashi Iwai187d3332011-11-24 16:33:09 +01002659 via_hp_automute(codec);
2660 vt1708_update_hp_work(spec);
2661 return 1;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002662}
2663
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002664static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2665 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2666 .name = "Jack Detect",
2667 .count = 1,
2668 .info = snd_ctl_boolean_mono_info,
2669 .get = vt1708_jack_detect_get,
2670 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002671};
2672
Takashi Iwai12daef62011-06-18 17:45:49 +02002673static void fill_dig_outs(struct hda_codec *codec);
2674static void fill_dig_in(struct hda_codec *codec);
2675
2676static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002677{
2678 struct via_spec *spec = codec->spec;
2679 int err;
2680
2681 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2682 if (err < 0)
2683 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002684 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002685 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002686
Takashi Iwai4a796162011-06-17 17:53:38 +02002687 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002688 if (err < 0)
2689 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002690 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002691 if (err < 0)
2692 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002693 err = via_auto_create_speaker_ctls(codec);
2694 if (err < 0)
2695 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002696 err = via_auto_create_loopback_switch(codec);
2697 if (err < 0)
2698 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002699 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002700 if (err < 0)
2701 return err;
2702
2703 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2704
Takashi Iwai12daef62011-06-18 17:45:49 +02002705 fill_dig_outs(codec);
2706 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002707
Takashi Iwai603c4012008-07-30 15:01:44 +02002708 if (spec->kctls.list)
2709 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002710
Joseph Chanc577b8a2006-11-29 15:29:40 +01002711
Takashi Iwai3214b962011-07-18 12:49:25 +02002712 if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002713 err = via_hp_build(codec);
2714 if (err < 0)
2715 return err;
2716 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002717
Takashi Iwaif4a78282011-06-17 18:46:48 +02002718 err = via_smart51_build(codec);
2719 if (err < 0)
2720 return err;
2721
Takashi Iwai5d417622011-06-20 11:32:27 +02002722 /* assign slave outs */
2723 if (spec->slave_dig_outs[0])
2724 codec->slave_dig_outs = spec->slave_dig_outs;
2725
Joseph Chanc577b8a2006-11-29 15:29:40 +01002726 return 1;
2727}
2728
Takashi Iwai5d417622011-06-20 11:32:27 +02002729static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002730{
Lydia Wang25eaba22009-10-10 19:08:43 +08002731 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002732 if (spec->multiout.dig_out_nid)
2733 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2734 if (spec->slave_dig_outs[0])
2735 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2736}
Lydia Wang25eaba22009-10-10 19:08:43 +08002737
Takashi Iwai5d417622011-06-20 11:32:27 +02002738static void via_auto_init_dig_in(struct hda_codec *codec)
2739{
2740 struct via_spec *spec = codec->spec;
2741 if (!spec->dig_in_nid)
2742 return;
2743 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2744 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2745}
2746
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002747/* initialize the unsolicited events */
2748static void via_auto_init_unsol_event(struct hda_codec *codec)
2749{
2750 struct via_spec *spec = codec->spec;
2751 struct auto_pin_cfg *cfg = &spec->autocfg;
2752 unsigned int ev;
2753 int i;
2754
2755 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002756 snd_hda_jack_detect_enable(codec, cfg->hp_pins[0],
2757 VIA_HP_EVENT | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002758
2759 if (cfg->speaker_pins[0])
2760 ev = VIA_LINE_EVENT;
2761 else
2762 ev = 0;
2763 for (i = 0; i < cfg->line_outs; i++) {
2764 if (cfg->line_out_pins[i] &&
2765 is_jack_detectable(codec, cfg->line_out_pins[i]))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002766 snd_hda_jack_detect_enable(codec, cfg->line_out_pins[i],
2767 ev | VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002768 }
2769
2770 for (i = 0; i < cfg->num_inputs; i++) {
2771 if (is_jack_detectable(codec, cfg->inputs[i].pin))
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002772 snd_hda_jack_detect_enable(codec, cfg->inputs[i].pin,
2773 VIA_JACK_EVENT);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002774 }
2775}
2776
Takashi Iwai5d417622011-06-20 11:32:27 +02002777static int via_init(struct hda_codec *codec)
2778{
2779 struct via_spec *spec = codec->spec;
2780 int i;
2781
2782 for (i = 0; i < spec->num_iverbs; i++)
2783 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2784
Joseph Chanc577b8a2006-11-29 15:29:40 +01002785 via_auto_init_multi_out(codec);
2786 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002787 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002788 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002789 via_auto_init_dig_outs(codec);
2790 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002791
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002792 via_auto_init_unsol_event(codec);
2793
2794 via_hp_automute(codec);
Takashi Iwai187d3332011-11-24 16:33:09 +01002795 vt1708_update_hp_work(spec);
Takashi Iwai01a61e12011-10-28 00:03:22 +02002796 snd_hda_jack_report_sync(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002797
Joseph Chanc577b8a2006-11-29 15:29:40 +01002798 return 0;
2799}
2800
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002801static void vt1708_update_hp_jack_state(struct work_struct *work)
2802{
2803 struct via_spec *spec = container_of(work, struct via_spec,
2804 vt1708_hp_work.work);
2805 if (spec->codec_type != VT1708)
2806 return;
Takashi Iwai1835a0f2011-10-27 22:12:46 +02002807 snd_hda_jack_set_dirty_all(spec->codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002808 /* if jack state toggled */
2809 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002810 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002811 spec->vt1708_hp_present ^= 1;
2812 via_hp_automute(spec->codec);
2813 }
Takashi Iwai187d3332011-11-24 16:33:09 +01002814 if (spec->vt1708_jack_detect)
2815 schedule_delayed_work(&spec->vt1708_hp_work,
2816 msecs_to_jiffies(100));
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002817}
2818
Takashi Iwai337b9d02009-07-07 18:18:59 +02002819static int get_mux_nids(struct hda_codec *codec)
2820{
2821 struct via_spec *spec = codec->spec;
2822 hda_nid_t nid, conn[8];
2823 unsigned int type;
2824 int i, n;
2825
2826 for (i = 0; i < spec->num_adc_nids; i++) {
2827 nid = spec->adc_nids[i];
2828 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002829 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002830 if (type == AC_WID_PIN)
2831 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002832 n = snd_hda_get_connections(codec, nid, conn,
2833 ARRAY_SIZE(conn));
2834 if (n <= 0)
2835 break;
2836 if (n > 1) {
2837 spec->mux_nids[i] = nid;
2838 break;
2839 }
2840 nid = conn[0];
2841 }
2842 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002843 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002844}
2845
Joseph Chanc577b8a2006-11-29 15:29:40 +01002846static int patch_vt1708(struct hda_codec *codec)
2847{
2848 struct via_spec *spec;
2849 int err;
2850
2851 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002852 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002853 if (spec == NULL)
2854 return -ENOMEM;
2855
Takashi Iwai620e2b22011-06-17 17:19:19 +02002856 spec->aa_mix_nid = 0x17;
2857
Takashi Iwai12daef62011-06-18 17:45:49 +02002858 /* Add HP and CD pin config connect bit re-config action */
2859 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2860 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2861
Joseph Chanc577b8a2006-11-29 15:29:40 +01002862 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002863 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002864 if (err < 0) {
2865 via_free(codec);
2866 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002867 }
2868
Takashi Iwai12daef62011-06-18 17:45:49 +02002869 /* add jack detect on/off control */
2870 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2871 return -ENOMEM;
2872
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002873 /* disable 32bit format on VT1708 */
2874 if (codec->vendor_id == 0x11061708)
2875 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002876
Lydia Wange322a362011-06-29 13:52:02 +08002877 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2878
Joseph Chanc577b8a2006-11-29 15:29:40 +01002879 codec->patch_ops = via_patch_ops;
2880
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002881 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002882 return 0;
2883}
2884
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002885static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002886{
2887 struct via_spec *spec;
2888 int err;
2889
2890 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002891 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002892 if (spec == NULL)
2893 return -ENOMEM;
2894
Takashi Iwai620e2b22011-06-17 17:19:19 +02002895 spec->aa_mix_nid = 0x18;
2896
Takashi Iwai12daef62011-06-18 17:45:49 +02002897 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002898 if (err < 0) {
2899 via_free(codec);
2900 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002901 }
2902
Joseph Chanc577b8a2006-11-29 15:29:40 +01002903 codec->patch_ops = via_patch_ops;
2904
Josepch Chanf7278fd2007-12-13 16:40:40 +01002905 return 0;
2906}
2907
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002908static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2909{
2910 struct via_spec *spec = codec->spec;
2911 int imux_is_smixer;
2912 unsigned int parm;
2913 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002914 if ((spec->codec_type != VT1708B_4CH) &&
2915 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002916 is_8ch = 1;
2917
2918 /* SW0 (17h) = stereo mixer */
2919 imux_is_smixer =
2920 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2921 == ((spec->codec_type == VT1708S) ? 5 : 0));
2922 /* inputs */
2923 /* PW 1/2/5 (1ah/1bh/1eh) */
2924 parm = AC_PWRST_D3;
2925 set_pin_power_state(codec, 0x1a, &parm);
2926 set_pin_power_state(codec, 0x1b, &parm);
2927 set_pin_power_state(codec, 0x1e, &parm);
2928 if (imux_is_smixer)
2929 parm = AC_PWRST_D0;
2930 /* SW0 (17h), AIW 0/1 (13h/14h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01002931 update_power_state(codec, 0x17, parm);
2932 update_power_state(codec, 0x13, parm);
2933 update_power_state(codec, 0x14, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002934
2935 /* outputs */
2936 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2937 parm = AC_PWRST_D3;
2938 set_pin_power_state(codec, 0x19, &parm);
2939 if (spec->smart51_enabled)
2940 set_pin_power_state(codec, 0x1b, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01002941 update_power_state(codec, 0x18, parm);
2942 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002943
2944 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2945 if (is_8ch) {
2946 parm = AC_PWRST_D3;
2947 set_pin_power_state(codec, 0x22, &parm);
2948 if (spec->smart51_enabled)
2949 set_pin_power_state(codec, 0x1a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01002950 update_power_state(codec, 0x26, parm);
2951 update_power_state(codec, 0x24, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002952 } else if (codec->vendor_id == 0x11064397) {
2953 /* PW7(23h), SW2(27h), AOW2(25h) */
2954 parm = AC_PWRST_D3;
2955 set_pin_power_state(codec, 0x23, &parm);
2956 if (spec->smart51_enabled)
2957 set_pin_power_state(codec, 0x1a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01002958 update_power_state(codec, 0x27, parm);
2959 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002960 }
2961
2962 /* PW 3/4/7 (1ch/1dh/23h) */
2963 parm = AC_PWRST_D3;
2964 /* force to D0 for internal Speaker */
2965 set_pin_power_state(codec, 0x1c, &parm);
2966 set_pin_power_state(codec, 0x1d, &parm);
2967 if (is_8ch)
2968 set_pin_power_state(codec, 0x23, &parm);
2969
2970 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01002971 update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
2972 update_power_state(codec, 0x10, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002973 if (is_8ch) {
Takashi Iwai054d8672012-01-24 12:25:50 +01002974 update_power_state(codec, 0x25, parm);
2975 update_power_state(codec, 0x27, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002976 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01002977 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002978}
2979
Lydia Wang518bf3b2009-10-10 19:07:29 +08002980static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002981static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002982{
2983 struct via_spec *spec;
2984 int err;
2985
Lydia Wang518bf3b2009-10-10 19:07:29 +08002986 if (get_codec_type(codec) == VT1708BCE)
2987 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002988
Josepch Chanf7278fd2007-12-13 16:40:40 +01002989 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002990 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002991 if (spec == NULL)
2992 return -ENOMEM;
2993
Takashi Iwai620e2b22011-06-17 17:19:19 +02002994 spec->aa_mix_nid = 0x16;
2995
Josepch Chanf7278fd2007-12-13 16:40:40 +01002996 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002997 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002998 if (err < 0) {
2999 via_free(codec);
3000 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01003001 }
3002
Josepch Chanf7278fd2007-12-13 16:40:40 +01003003 codec->patch_ops = via_patch_ops;
3004
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003005 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
3006
Josepch Chanf7278fd2007-12-13 16:40:40 +01003007 return 0;
3008}
3009
Harald Welted949cac2008-09-09 15:56:01 +08003010/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02003011static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08003012 /* Enable Mic Boost Volume backdoor */
3013 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003014 /* don't bybass mixer */
3015 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08003016 { }
3017};
3018
Takashi Iwai9da29272009-05-07 16:31:14 +02003019/* fill out digital output widgets; one for master and one for slave outputs */
3020static void fill_dig_outs(struct hda_codec *codec)
3021{
3022 struct via_spec *spec = codec->spec;
3023 int i;
3024
3025 for (i = 0; i < spec->autocfg.dig_outs; i++) {
3026 hda_nid_t nid;
3027 int conn;
3028
3029 nid = spec->autocfg.dig_out_pins[i];
3030 if (!nid)
3031 continue;
3032 conn = snd_hda_get_connections(codec, nid, &nid, 1);
3033 if (conn < 1)
3034 continue;
3035 if (!spec->multiout.dig_out_nid)
3036 spec->multiout.dig_out_nid = nid;
3037 else {
3038 spec->slave_dig_outs[0] = nid;
3039 break; /* at most two dig outs */
3040 }
3041 }
3042}
3043
Takashi Iwai12daef62011-06-18 17:45:49 +02003044static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08003045{
3046 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02003047 hda_nid_t dig_nid;
3048 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08003049
Takashi Iwai12daef62011-06-18 17:45:49 +02003050 if (!spec->autocfg.dig_in_pin)
3051 return;
Harald Welted949cac2008-09-09 15:56:01 +08003052
Takashi Iwai12daef62011-06-18 17:45:49 +02003053 dig_nid = codec->start_nid;
3054 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
3055 unsigned int wcaps = get_wcaps(codec, dig_nid);
3056 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
3057 continue;
3058 if (!(wcaps & AC_WCAP_DIGITAL))
3059 continue;
3060 if (!(wcaps & AC_WCAP_CONN_LIST))
3061 continue;
3062 err = get_connection_index(codec, dig_nid,
3063 spec->autocfg.dig_in_pin);
3064 if (err >= 0) {
3065 spec->dig_in_nid = dig_nid;
3066 break;
3067 }
3068 }
Harald Welted949cac2008-09-09 15:56:01 +08003069}
3070
Lydia Wang6369bcf2009-10-10 19:08:31 +08003071static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
3072 int offset, int num_steps, int step_size)
3073{
3074 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
3075 (offset << AC_AMPCAP_OFFSET_SHIFT) |
3076 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
3077 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
3078 (0 << AC_AMPCAP_MUTE_SHIFT));
3079}
3080
Harald Welted949cac2008-09-09 15:56:01 +08003081static int patch_vt1708S(struct hda_codec *codec)
3082{
3083 struct via_spec *spec;
3084 int err;
3085
3086 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003087 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003088 if (spec == NULL)
3089 return -ENOMEM;
3090
Takashi Iwai620e2b22011-06-17 17:19:19 +02003091 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003092 override_mic_boost(codec, 0x1a, 0, 3, 40);
3093 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003094
Harald Welted949cac2008-09-09 15:56:01 +08003095 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003096 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003097 if (err < 0) {
3098 via_free(codec);
3099 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003100 }
3101
Takashi Iwai096a8852011-06-20 12:09:02 +02003102 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003103
Harald Welted949cac2008-09-09 15:56:01 +08003104 codec->patch_ops = via_patch_ops;
3105
Lydia Wang518bf3b2009-10-10 19:07:29 +08003106 /* correct names for VT1708BCE */
3107 if (get_codec_type(codec) == VT1708BCE) {
3108 kfree(codec->chip_name);
3109 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
3110 snprintf(codec->bus->card->mixername,
3111 sizeof(codec->bus->card->mixername),
3112 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08003113 }
Lydia Wangbc92df72011-03-23 17:56:05 +08003114 /* correct names for VT1705 */
3115 if (codec->vendor_id == 0x11064397) {
3116 kfree(codec->chip_name);
3117 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
3118 snprintf(codec->bus->card->mixername,
3119 sizeof(codec->bus->card->mixername),
3120 "%s %s", codec->vendor_name, codec->chip_name);
3121 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003122 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08003123 return 0;
3124}
3125
3126/* Patch for VT1702 */
3127
Takashi Iwai096a8852011-06-20 12:09:02 +02003128static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08003129 /* mixer enable */
3130 {0x1, 0xF88, 0x3},
3131 /* GPIO 0~2 */
3132 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003133 { }
3134};
3135
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003136static void set_widgets_power_state_vt1702(struct hda_codec *codec)
3137{
3138 int imux_is_smixer =
3139 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3140 unsigned int parm;
3141 /* inputs */
3142 /* PW 1/2/5 (14h/15h/18h) */
3143 parm = AC_PWRST_D3;
3144 set_pin_power_state(codec, 0x14, &parm);
3145 set_pin_power_state(codec, 0x15, &parm);
3146 set_pin_power_state(codec, 0x18, &parm);
3147 if (imux_is_smixer)
3148 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
3149 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003150 update_power_state(codec, 0x13, parm);
3151 update_power_state(codec, 0x12, parm);
3152 update_power_state(codec, 0x1f, parm);
3153 update_power_state(codec, 0x20, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003154
3155 /* outputs */
3156 /* PW 3/4 (16h/17h) */
3157 parm = AC_PWRST_D3;
3158 set_pin_power_state(codec, 0x17, &parm);
3159 set_pin_power_state(codec, 0x16, &parm);
3160 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003161 update_power_state(codec, 0x1a, imux_is_smixer ? AC_PWRST_D0 : parm);
3162 update_power_state(codec, 0x10, parm);
3163 update_power_state(codec, 0x1d, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003164}
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) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003228 update_power_state(codec, 0x1e, parm);
3229 update_power_state(codec, 0x1f, parm);
3230 update_power_state(codec, 0x10, parm);
3231 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003232
3233 /* outputs */
3234 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
3235 parm = AC_PWRST_D3;
3236 set_pin_power_state(codec, 0x27, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003237 update_power_state(codec, 0x1a, parm);
3238 update_power_state(codec, 0xb, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003239
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);
Takashi Iwai054d8672012-01-24 12:25:50 +01003245 update_power_state(codec, 0xa, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003246
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);
Takashi Iwai054d8672012-01-24 12:25:50 +01003252 update_power_state(codec, 0x8, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003253 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003254 update_power_state(codec, 0x21, imux_is_smixer ? AC_PWRST_D0 : parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003255
3256 /* PW1 (25h), AOW1 (9h) */
3257 parm = AC_PWRST_D3;
3258 set_pin_power_state(codec, 0x25, &parm);
3259 if (spec->smart51_enabled)
3260 set_pin_power_state(codec, 0x2a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003261 update_power_state(codec, 0x9, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003262
3263 if (spec->hp_independent_mode) {
3264 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3265 parm = AC_PWRST_D3;
3266 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003267 update_power_state(codec, 0x1b, parm);
3268 update_power_state(codec, 0x34, parm);
3269 update_power_state(codec, 0xc, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003270 }
3271}
3272
Takashi Iwai30b45032011-07-11 17:05:04 +02003273/* Add a connection to the primary DAC from AA-mixer for some codecs
3274 * This isn't listed from the raw info, but the chip has a secret connection.
3275 */
3276static int add_secret_dac_path(struct hda_codec *codec)
3277{
3278 struct via_spec *spec = codec->spec;
3279 int i, nums;
3280 hda_nid_t conn[8];
3281 hda_nid_t nid;
3282
3283 if (!spec->aa_mix_nid)
3284 return 0;
3285 nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
3286 ARRAY_SIZE(conn) - 1);
3287 for (i = 0; i < nums; i++) {
3288 if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
3289 return 0;
3290 }
3291
3292 /* find the primary DAC and add to the connection list */
3293 nid = codec->start_nid;
3294 for (i = 0; i < codec->num_nodes; i++, nid++) {
3295 unsigned int caps = get_wcaps(codec, nid);
3296 if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
3297 !(caps & AC_WCAP_DIGITAL)) {
3298 conn[nums++] = nid;
3299 return snd_hda_override_conn_list(codec,
3300 spec->aa_mix_nid,
3301 nums, conn);
3302 }
3303 }
3304 return 0;
3305}
3306
3307
Lydia Wangeb7188c2009-10-10 19:08:34 +08003308static int patch_vt1718S(struct hda_codec *codec)
3309{
3310 struct via_spec *spec;
3311 int err;
3312
3313 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003314 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003315 if (spec == NULL)
3316 return -ENOMEM;
3317
Takashi Iwai620e2b22011-06-17 17:19:19 +02003318 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003319 override_mic_boost(codec, 0x2b, 0, 3, 40);
3320 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003321 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003322
Lydia Wangeb7188c2009-10-10 19:08:34 +08003323 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003324 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003325 if (err < 0) {
3326 via_free(codec);
3327 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003328 }
3329
Takashi Iwai096a8852011-06-20 12:09:02 +02003330 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003331
Lydia Wangeb7188c2009-10-10 19:08:34 +08003332 codec->patch_ops = via_patch_ops;
3333
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003334 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3335
Lydia Wangeb7188c2009-10-10 19:08:34 +08003336 return 0;
3337}
Lydia Wangf3db4232009-10-10 19:08:41 +08003338
3339/* Patch for VT1716S */
3340
3341static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3342 struct snd_ctl_elem_info *uinfo)
3343{
3344 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3345 uinfo->count = 1;
3346 uinfo->value.integer.min = 0;
3347 uinfo->value.integer.max = 1;
3348 return 0;
3349}
3350
3351static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3352 struct snd_ctl_elem_value *ucontrol)
3353{
3354 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3355 int index = 0;
3356
3357 index = snd_hda_codec_read(codec, 0x26, 0,
3358 AC_VERB_GET_CONNECT_SEL, 0);
3359 if (index != -1)
3360 *ucontrol->value.integer.value = index;
3361
3362 return 0;
3363}
3364
3365static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3366 struct snd_ctl_elem_value *ucontrol)
3367{
3368 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3369 struct via_spec *spec = codec->spec;
3370 int index = *ucontrol->value.integer.value;
3371
3372 snd_hda_codec_write(codec, 0x26, 0,
3373 AC_VERB_SET_CONNECT_SEL, index);
3374 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003375 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003376 return 1;
3377}
3378
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003379static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003380 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3381 {
3382 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3383 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003384 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003385 .count = 1,
3386 .info = vt1716s_dmic_info,
3387 .get = vt1716s_dmic_get,
3388 .put = vt1716s_dmic_put,
3389 },
3390 {} /* end */
3391};
3392
3393
3394/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003395static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003396 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3397 { } /* end */
3398};
3399
Takashi Iwai096a8852011-06-20 12:09:02 +02003400static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003401 /* Enable Boost Volume backdoor */
3402 {0x1, 0xf8a, 0x80},
3403 /* don't bybass mixer */
3404 {0x1, 0xf88, 0xc0},
3405 /* Enable mono output */
3406 {0x1, 0xf90, 0x08},
3407 { }
3408};
3409
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003410static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3411{
3412 struct via_spec *spec = codec->spec;
3413 int imux_is_smixer;
3414 unsigned int parm;
3415 unsigned int mono_out, present;
3416 /* SW0 (17h) = stereo mixer */
3417 imux_is_smixer =
3418 (snd_hda_codec_read(codec, 0x17, 0,
3419 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3420 /* inputs */
3421 /* PW 1/2/5 (1ah/1bh/1eh) */
3422 parm = AC_PWRST_D3;
3423 set_pin_power_state(codec, 0x1a, &parm);
3424 set_pin_power_state(codec, 0x1b, &parm);
3425 set_pin_power_state(codec, 0x1e, &parm);
3426 if (imux_is_smixer)
3427 parm = AC_PWRST_D0;
3428 /* SW0 (17h), AIW0(13h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003429 update_power_state(codec, 0x17, parm);
3430 update_power_state(codec, 0x13, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003431
3432 parm = AC_PWRST_D3;
3433 set_pin_power_state(codec, 0x1e, &parm);
3434 /* PW11 (22h) */
3435 if (spec->dmic_enabled)
3436 set_pin_power_state(codec, 0x22, &parm);
3437 else
Takashi Iwai054d8672012-01-24 12:25:50 +01003438 update_power_state(codec, 0x22, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003439
3440 /* SW2(26h), AIW1(14h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003441 update_power_state(codec, 0x26, parm);
3442 update_power_state(codec, 0x14, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003443
3444 /* outputs */
3445 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3446 parm = AC_PWRST_D3;
3447 set_pin_power_state(codec, 0x19, &parm);
3448 /* Smart 5.1 PW2(1bh) */
3449 if (spec->smart51_enabled)
3450 set_pin_power_state(codec, 0x1b, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003451 update_power_state(codec, 0x18, parm);
3452 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003453
3454 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3455 parm = AC_PWRST_D3;
3456 set_pin_power_state(codec, 0x23, &parm);
3457 /* Smart 5.1 PW1(1ah) */
3458 if (spec->smart51_enabled)
3459 set_pin_power_state(codec, 0x1a, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003460 update_power_state(codec, 0x27, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003461
3462 /* Smart 5.1 PW5(1eh) */
3463 if (spec->smart51_enabled)
3464 set_pin_power_state(codec, 0x1e, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003465 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003466
3467 /* Mono out */
3468 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3469 present = snd_hda_jack_detect(codec, 0x1c);
3470
3471 if (present)
3472 mono_out = 0;
3473 else {
3474 present = snd_hda_jack_detect(codec, 0x1d);
3475 if (!spec->hp_independent_mode && present)
3476 mono_out = 0;
3477 else
3478 mono_out = 1;
3479 }
3480 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
Takashi Iwai054d8672012-01-24 12:25:50 +01003481 update_power_state(codec, 0x28, parm);
3482 update_power_state(codec, 0x29, parm);
3483 update_power_state(codec, 0x2a, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003484
3485 /* PW 3/4 (1ch/1dh) */
3486 parm = AC_PWRST_D3;
3487 set_pin_power_state(codec, 0x1c, &parm);
3488 set_pin_power_state(codec, 0x1d, &parm);
3489 /* HP Independent Mode, power on AOW3 */
3490 if (spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01003491 update_power_state(codec, 0x25, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003492
3493 /* force to D0 for internal Speaker */
3494 /* MW0 (16h), AOW0 (10h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003495 update_power_state(codec, 0x16, imux_is_smixer ? AC_PWRST_D0 : parm);
3496 update_power_state(codec, 0x10, mono_out ? AC_PWRST_D0 : parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003497}
3498
Lydia Wangf3db4232009-10-10 19:08:41 +08003499static int patch_vt1716S(struct hda_codec *codec)
3500{
3501 struct via_spec *spec;
3502 int err;
3503
3504 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003505 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003506 if (spec == NULL)
3507 return -ENOMEM;
3508
Takashi Iwai620e2b22011-06-17 17:19:19 +02003509 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003510 override_mic_boost(codec, 0x1a, 0, 3, 40);
3511 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003512
Lydia Wangf3db4232009-10-10 19:08:41 +08003513 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003514 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003515 if (err < 0) {
3516 via_free(codec);
3517 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003518 }
3519
Takashi Iwai096a8852011-06-20 12:09:02 +02003520 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003521
Lydia Wangf3db4232009-10-10 19:08:41 +08003522 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3523 spec->num_mixers++;
3524
3525 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3526
3527 codec->patch_ops = via_patch_ops;
3528
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003529 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003530 return 0;
3531}
Lydia Wang25eaba22009-10-10 19:08:43 +08003532
3533/* for vt2002P */
3534
Takashi Iwai096a8852011-06-20 12:09:02 +02003535static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003536 /* Class-D speaker related verbs */
3537 {0x1, 0xfe0, 0x4},
3538 {0x1, 0xfe9, 0x80},
3539 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003540 /* Enable Boost Volume backdoor */
3541 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003542 /* Enable AOW0 to MW9 */
3543 {0x1, 0xfb8, 0x88},
3544 { }
3545};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003546
Takashi Iwai096a8852011-06-20 12:09:02 +02003547static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003548 /* Enable Boost Volume backdoor */
3549 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003550 /* Enable AOW0 to MW9 */
3551 {0x1, 0xfb8, 0x88},
3552 { }
3553};
Lydia Wang25eaba22009-10-10 19:08:43 +08003554
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003555static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3556{
3557 struct via_spec *spec = codec->spec;
3558 int imux_is_smixer;
3559 unsigned int parm;
3560 unsigned int present;
3561 /* MUX9 (1eh) = stereo mixer */
3562 imux_is_smixer =
3563 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3564 /* inputs */
3565 /* PW 5/6/7 (29h/2ah/2bh) */
3566 parm = AC_PWRST_D3;
3567 set_pin_power_state(codec, 0x29, &parm);
3568 set_pin_power_state(codec, 0x2a, &parm);
3569 set_pin_power_state(codec, 0x2b, &parm);
3570 parm = AC_PWRST_D0;
3571 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003572 update_power_state(codec, 0x1e, parm);
3573 update_power_state(codec, 0x1f, parm);
3574 update_power_state(codec, 0x10, parm);
3575 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003576
3577 /* outputs */
3578 /* AOW0 (8h)*/
Takashi Iwai054d8672012-01-24 12:25:50 +01003579 update_power_state(codec, 0x8, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003580
Lydia Wang118909562011-03-23 17:57:34 +08003581 if (spec->codec_type == VT1802) {
3582 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3583 parm = AC_PWRST_D3;
3584 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003585 update_power_state(codec, 0x18, parm);
3586 update_power_state(codec, 0x38, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003587 } else {
3588 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3589 parm = AC_PWRST_D3;
3590 set_pin_power_state(codec, 0x26, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003591 update_power_state(codec, 0x1c, parm);
3592 update_power_state(codec, 0x37, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003593 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003594
Lydia Wang118909562011-03-23 17:57:34 +08003595 if (spec->codec_type == VT1802) {
3596 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3597 parm = AC_PWRST_D3;
3598 set_pin_power_state(codec, 0x25, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003599 update_power_state(codec, 0x15, parm);
3600 update_power_state(codec, 0x35, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003601 } else {
3602 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3603 parm = AC_PWRST_D3;
3604 set_pin_power_state(codec, 0x25, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003605 update_power_state(codec, 0x19, parm);
3606 update_power_state(codec, 0x35, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003607 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003608
3609 if (spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01003610 update_power_state(codec, 0x9, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003611
3612 /* Class-D */
3613 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3614 present = snd_hda_jack_detect(codec, 0x25);
3615
3616 parm = AC_PWRST_D3;
3617 set_pin_power_state(codec, 0x24, &parm);
3618 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003619 if (spec->codec_type == VT1802)
Takashi Iwai054d8672012-01-24 12:25:50 +01003620 update_power_state(codec, 0x14, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003621 else
Takashi Iwai054d8672012-01-24 12:25:50 +01003622 update_power_state(codec, 0x18, parm);
3623 update_power_state(codec, 0x34, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003624
3625 /* Mono Out */
3626 present = snd_hda_jack_detect(codec, 0x26);
3627
3628 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003629 if (spec->codec_type == VT1802) {
3630 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003631 update_power_state(codec, 0x33, parm);
3632 update_power_state(codec, 0x1c, parm);
3633 update_power_state(codec, 0x3c, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003634 } else {
3635 /* PW15 (31h), MW8(17h), MUX8(3bh) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003636 update_power_state(codec, 0x31, parm);
3637 update_power_state(codec, 0x17, parm);
3638 update_power_state(codec, 0x3b, parm);
Lydia Wang118909562011-03-23 17:57:34 +08003639 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003640 /* MW9 (21h) */
3641 if (imux_is_smixer || !is_aa_path_mute(codec))
Takashi Iwai054d8672012-01-24 12:25:50 +01003642 update_power_state(codec, 0x21, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003643 else
Takashi Iwai054d8672012-01-24 12:25:50 +01003644 update_power_state(codec, 0x21, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003645}
Lydia Wang25eaba22009-10-10 19:08:43 +08003646
3647/* patch for vt2002P */
3648static int patch_vt2002P(struct hda_codec *codec)
3649{
3650 struct via_spec *spec;
3651 int err;
3652
3653 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003654 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003655 if (spec == NULL)
3656 return -ENOMEM;
3657
Takashi Iwai620e2b22011-06-17 17:19:19 +02003658 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003659 override_mic_boost(codec, 0x2b, 0, 3, 40);
3660 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003661 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003662
Lydia Wang25eaba22009-10-10 19:08:43 +08003663 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003664 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003665 if (err < 0) {
3666 via_free(codec);
3667 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003668 }
3669
Lydia Wang118909562011-03-23 17:57:34 +08003670 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003671 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003672 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003673 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003674
Lydia Wang25eaba22009-10-10 19:08:43 +08003675 codec->patch_ops = via_patch_ops;
3676
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003677 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003678 return 0;
3679}
Lydia Wangab6734e2009-10-10 19:08:46 +08003680
3681/* for vt1812 */
3682
Takashi Iwai096a8852011-06-20 12:09:02 +02003683static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003684 /* Enable Boost Volume backdoor */
3685 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003686 /* Enable AOW0 to MW9 */
3687 {0x1, 0xfb8, 0xa8},
3688 { }
3689};
3690
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003691static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3692{
3693 struct via_spec *spec = codec->spec;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003694 unsigned int parm;
3695 unsigned int present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003696 /* inputs */
3697 /* PW 5/6/7 (29h/2ah/2bh) */
3698 parm = AC_PWRST_D3;
3699 set_pin_power_state(codec, 0x29, &parm);
3700 set_pin_power_state(codec, 0x2a, &parm);
3701 set_pin_power_state(codec, 0x2b, &parm);
3702 parm = AC_PWRST_D0;
3703 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
Takashi Iwai054d8672012-01-24 12:25:50 +01003704 update_power_state(codec, 0x1e, parm);
3705 update_power_state(codec, 0x1f, parm);
3706 update_power_state(codec, 0x10, parm);
3707 update_power_state(codec, 0x11, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003708
3709 /* outputs */
3710 /* AOW0 (8h)*/
Takashi Iwai054d8672012-01-24 12:25:50 +01003711 update_power_state(codec, 0x8, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003712
3713 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3714 parm = AC_PWRST_D3;
3715 set_pin_power_state(codec, 0x28, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003716 update_power_state(codec, 0x18, parm);
3717 update_power_state(codec, 0x38, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003718
3719 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3720 parm = AC_PWRST_D3;
3721 set_pin_power_state(codec, 0x25, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003722 update_power_state(codec, 0x15, parm);
3723 update_power_state(codec, 0x35, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003724 if (spec->hp_independent_mode)
Takashi Iwai054d8672012-01-24 12:25:50 +01003725 update_power_state(codec, 0x9, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003726
3727 /* Internal Speaker */
3728 /* PW0 (24h), MW0(14h), MUX0(34h) */
3729 present = snd_hda_jack_detect(codec, 0x25);
3730
3731 parm = AC_PWRST_D3;
3732 set_pin_power_state(codec, 0x24, &parm);
3733 if (present) {
Takashi Iwai054d8672012-01-24 12:25:50 +01003734 update_power_state(codec, 0x14, AC_PWRST_D3);
3735 update_power_state(codec, 0x34, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003736 } else {
Takashi Iwai054d8672012-01-24 12:25:50 +01003737 update_power_state(codec, 0x14, AC_PWRST_D0);
3738 update_power_state(codec, 0x34, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003739 }
3740
3741
3742 /* Mono Out */
3743 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3744 present = snd_hda_jack_detect(codec, 0x28);
3745
3746 parm = AC_PWRST_D3;
3747 set_pin_power_state(codec, 0x31, &parm);
3748 if (present) {
Takashi Iwai054d8672012-01-24 12:25:50 +01003749 update_power_state(codec, 0x1c, AC_PWRST_D3);
3750 update_power_state(codec, 0x3c, AC_PWRST_D3);
3751 update_power_state(codec, 0x3e, AC_PWRST_D3);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003752 } else {
Takashi Iwai054d8672012-01-24 12:25:50 +01003753 update_power_state(codec, 0x1c, AC_PWRST_D0);
3754 update_power_state(codec, 0x3c, AC_PWRST_D0);
3755 update_power_state(codec, 0x3e, AC_PWRST_D0);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003756 }
3757
3758 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3759 parm = AC_PWRST_D3;
3760 set_pin_power_state(codec, 0x33, &parm);
Takashi Iwai054d8672012-01-24 12:25:50 +01003761 update_power_state(codec, 0x1d, parm);
3762 update_power_state(codec, 0x3d, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003763
3764}
Lydia Wangab6734e2009-10-10 19:08:46 +08003765
3766/* patch for vt1812 */
3767static int patch_vt1812(struct hda_codec *codec)
3768{
3769 struct via_spec *spec;
3770 int err;
3771
3772 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003773 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003774 if (spec == NULL)
3775 return -ENOMEM;
3776
Takashi Iwai620e2b22011-06-17 17:19:19 +02003777 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003778 override_mic_boost(codec, 0x2b, 0, 3, 40);
3779 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003780 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003781
Lydia Wangab6734e2009-10-10 19:08:46 +08003782 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003783 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003784 if (err < 0) {
3785 via_free(codec);
3786 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003787 }
3788
Takashi Iwai096a8852011-06-20 12:09:02 +02003789 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003790
Lydia Wangab6734e2009-10-10 19:08:46 +08003791 codec->patch_ops = via_patch_ops;
3792
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003793 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003794 return 0;
3795}
3796
Joseph Chanc577b8a2006-11-29 15:29:40 +01003797/*
3798 * patch entries
3799 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003800static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003801 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3802 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3803 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3804 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3805 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003806 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003807 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003808 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003809 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003810 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003811 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003812 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003813 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003814 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003815 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003816 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003817 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003818 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003819 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003820 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003821 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003822 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003823 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003824 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003825 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003826 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003827 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003828 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003829 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003830 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003831 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003832 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003833 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003834 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003835 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003836 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003837 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003838 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003839 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003840 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003841 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003842 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003843 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003844 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003845 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003846 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003847 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003848 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003849 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003850 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003851 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003852 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003853 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003854 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003855 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003856 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003857 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003858 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003859 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003860 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003861 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003862 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003863 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003864 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003865 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003866 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003867 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003868 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003869 { .id = 0x11060428, .name = "VT1718S",
3870 .patch = patch_vt1718S},
3871 { .id = 0x11064428, .name = "VT1718S",
3872 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003873 { .id = 0x11060441, .name = "VT2020",
3874 .patch = patch_vt1718S},
3875 { .id = 0x11064441, .name = "VT1828S",
3876 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003877 { .id = 0x11060433, .name = "VT1716S",
3878 .patch = patch_vt1716S},
3879 { .id = 0x1106a721, .name = "VT1716S",
3880 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003881 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3882 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003883 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003884 { .id = 0x11060440, .name = "VT1818S",
3885 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003886 { .id = 0x11060446, .name = "VT1802",
3887 .patch = patch_vt2002P},
3888 { .id = 0x11068446, .name = "VT1802",
3889 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003890 {} /* terminator */
3891};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003892
3893MODULE_ALIAS("snd-hda-codec-id:1106*");
3894
3895static struct hda_codec_preset_list via_list = {
3896 .preset = snd_hda_preset_via,
3897 .owner = THIS_MODULE,
3898};
3899
3900MODULE_LICENSE("GPL");
3901MODULE_DESCRIPTION("VIA HD-audio codec");
3902
3903static int __init patch_via_init(void)
3904{
3905 return snd_hda_add_codec_preset(&via_list);
3906}
3907
3908static void __exit patch_via_exit(void)
3909{
3910 snd_hda_delete_codec_preset(&via_list);
3911}
3912
3913module_init(patch_via_init)
3914module_exit(patch_via_exit)