blob: 8f59e0b5d477b3514a99d79d88f33da36fc2afa5 [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>
Joseph Chanc577b8a2006-11-29 15:29:40 +010052#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080053#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010054#include "hda_codec.h"
55#include "hda_local.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010056
Joseph Chanc577b8a2006-11-29 15:29:40 +010057/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080058#define VT1708_HP_PIN_NID 0x20
59#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010060
Harald Welted7426322008-09-15 22:43:23 +080061enum VIA_HDA_CODEC {
62 UNKNOWN = -1,
63 VT1708,
64 VT1709_10CH,
65 VT1709_6CH,
66 VT1708B_8CH,
67 VT1708B_4CH,
68 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080069 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080070 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080071 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080072 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080073 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080074 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080075 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080076 CODEC_TYPES,
77};
78
Lydia Wang118909562011-03-23 17:57:34 +080079#define VT2002P_COMPATIBLE(spec) \
80 ((spec)->codec_type == VT2002P ||\
81 (spec)->codec_type == VT1812 ||\
82 (spec)->codec_type == VT1802)
83
Takashi Iwai8e3679d2011-06-21 09:01:36 +020084#define MAX_NID_PATH_DEPTH 5
85
Takashi Iwai09a9ad692011-06-21 15:57:44 +020086/* output-path: DAC -> ... -> pin
87 * idx[] contains the source index number of the next widget;
88 * e.g. idx[0] is the index of the DAC selected by path[1] widget
89 * multi[] indicates whether it's a selector widget with multi-connectors
90 * (i.e. the connection selection is mandatory)
91 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
92 */
Takashi Iwai4a796162011-06-17 17:53:38 +020093struct nid_path {
94 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020095 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad692011-06-21 15:57:44 +020096 unsigned char idx[MAX_NID_PATH_DEPTH];
97 unsigned char multi[MAX_NID_PATH_DEPTH];
98 unsigned int vol_ctl;
99 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200100};
101
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200102/* input-path */
103struct via_input {
104 hda_nid_t pin; /* input-pin or aa-mix */
105 int adc_idx; /* ADC index to be used */
106 int mux_idx; /* MUX index (if any) */
107 const char *label; /* input-source label */
108};
109
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200110#define VIA_MAX_ADCS 3
111
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800112struct via_spec {
113 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200114 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800115 unsigned int num_mixers;
116
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200117 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800118 unsigned int num_iverbs;
119
Takashi Iwai82673bc2011-06-17 16:24:21 +0200120 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200121 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200122 const struct hda_pcm_stream *stream_analog_playback;
123 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800124
Takashi Iwai82673bc2011-06-17 16:24:21 +0200125 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200126 const struct hda_pcm_stream *stream_digital_playback;
127 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800128
129 /* playback */
130 struct hda_multi_out multiout;
131 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200132 hda_nid_t hp_dac_nid;
Takashi Iwai25250502011-06-30 17:24:47 +0200133 bool hp_indep_shared; /* indep HP-DAC is shared with side ch */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200134 int num_active_streams;
Lydia Wangc4394f52011-07-04 16:54:15 +0800135 int dac_mixer_idx;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800136
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200137 struct nid_path out_path[HDA_SIDE + 1];
Takashi Iwai4a796162011-06-17 17:53:38 +0200138 struct nid_path hp_path;
139 struct nid_path hp_dep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200140 struct nid_path speaker_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200141
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800142 /* capture */
143 unsigned int num_adc_nids;
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200144 hda_nid_t adc_nids[VIA_MAX_ADCS];
145 hda_nid_t mux_nids[VIA_MAX_ADCS];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200146 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800147 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800148
149 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200150 bool dyn_adc_switch;
151 int num_inputs;
152 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200153 unsigned int cur_mux[VIA_MAX_ADCS];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800154
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200155 /* dynamic ADC switching */
156 hda_nid_t cur_adc;
157 unsigned int cur_adc_stream_tag;
158 unsigned int cur_adc_format;
159
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800160 /* PCM information */
161 struct hda_pcm pcm_rec[3];
162
163 /* dynamic controls, init_verbs and input_mux */
164 struct auto_pin_cfg autocfg;
165 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800166 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
167
168 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800169 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800170 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200171 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800172 enum VIA_HDA_CODEC codec_type;
173
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200174 /* smart51 setup */
175 unsigned int smart51_nums;
176 hda_nid_t smart51_pins[2];
177 int smart51_idxs[2];
178 const char *smart51_labels[2];
179 unsigned int smart51_enabled;
180
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800181 /* work to check hp jack state */
182 struct hda_codec *codec;
183 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200184 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800185 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800186
187 void (*set_widgets_power_state)(struct hda_codec *codec);
188
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800189 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200190 int num_loopbacks;
191 struct hda_amp_list loopback_list[8];
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200192
193 /* bind capture-volume */
194 struct hda_bind_ctls *bind_cap_vol;
195 struct hda_bind_ctls *bind_cap_sw;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800196};
197
Lydia Wang0341ccd2011-03-22 16:25:03 +0800198static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100199static struct via_spec * via_new_spec(struct hda_codec *codec)
200{
201 struct via_spec *spec;
202
203 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
204 if (spec == NULL)
205 return NULL;
206
207 codec->spec = spec;
208 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800209 spec->codec_type = get_codec_type(codec);
210 /* VT1708BCE & VT1708S are almost same */
211 if (spec->codec_type == VT1708BCE)
212 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100213 return spec;
214}
215
Lydia Wang744ff5f2009-10-10 19:07:26 +0800216static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800217{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800218 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800219 u16 ven_id = vendor_id >> 16;
220 u16 dev_id = vendor_id & 0xffff;
221 enum VIA_HDA_CODEC codec_type;
222
223 /* get codec type */
224 if (ven_id != 0x1106)
225 codec_type = UNKNOWN;
226 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
227 codec_type = VT1708;
228 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
229 codec_type = VT1709_10CH;
230 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
231 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800232 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800233 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800234 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
235 codec_type = VT1708BCE;
236 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800237 codec_type = VT1708B_4CH;
238 else if ((dev_id & 0xfff) == 0x397
239 && (dev_id >> 12) < 8)
240 codec_type = VT1708S;
241 else if ((dev_id & 0xfff) == 0x398
242 && (dev_id >> 12) < 8)
243 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800244 else if ((dev_id & 0xfff) == 0x428
245 && (dev_id >> 12) < 8)
246 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800247 else if (dev_id == 0x0433 || dev_id == 0xa721)
248 codec_type = VT1716S;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +0800249 else if (dev_id == 0x0441 || dev_id == 0x4441)
250 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800251 else if (dev_id == 0x0438 || dev_id == 0x4438)
252 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800253 else if (dev_id == 0x0448)
254 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800255 else if (dev_id == 0x0440)
256 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800257 else if ((dev_id & 0xfff) == 0x446)
258 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800259 else
260 codec_type = UNKNOWN;
261 return codec_type;
262};
263
Lydia Wangec7e7e42011-03-24 12:43:44 +0800264#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800265#define VIA_HP_EVENT 0x01
266#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200267#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800268
Joseph Chanc577b8a2006-11-29 15:29:40 +0100269enum {
270 VIA_CTL_WIDGET_VOL,
271 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800272 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100273};
274
Takashi Iwaiada509e2011-06-20 15:40:19 +0200275static void analog_low_current_mode(struct hda_codec *codec);
276static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800277
278static void vt1708_start_hp_work(struct via_spec *spec)
279{
280 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
281 return;
282 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200283 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800284 if (!delayed_work_pending(&spec->vt1708_hp_work))
285 schedule_delayed_work(&spec->vt1708_hp_work,
286 msecs_to_jiffies(100));
287}
288
289static void vt1708_stop_hp_work(struct via_spec *spec)
290{
291 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
292 return;
293 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
294 && !is_aa_path_mute(spec->codec))
295 return;
296 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200297 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100298 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800299}
Lydia Wangf5271102009-10-10 19:07:35 +0800300
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800301static void set_widgets_power_state(struct hda_codec *codec)
302{
303 struct via_spec *spec = codec->spec;
304 if (spec->set_widgets_power_state)
305 spec->set_widgets_power_state(codec);
306}
Lydia Wang25eaba22009-10-10 19:08:43 +0800307
Lydia Wangf5271102009-10-10 19:07:35 +0800308static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
309 struct snd_ctl_elem_value *ucontrol)
310{
311 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
312 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
313
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800314 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200315 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800316 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
317 if (is_aa_path_mute(codec))
318 vt1708_start_hp_work(codec->spec);
319 else
320 vt1708_stop_hp_work(codec->spec);
321 }
Lydia Wangf5271102009-10-10 19:07:35 +0800322 return change;
323}
324
325/* modify .put = snd_hda_mixer_amp_switch_put */
326#define ANALOG_INPUT_MUTE \
327 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
328 .name = NULL, \
329 .index = 0, \
330 .info = snd_hda_mixer_amp_switch_info, \
331 .get = snd_hda_mixer_amp_switch_get, \
332 .put = analog_input_switch_put, \
333 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
334
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200335static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100336 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
337 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800338 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100339};
340
Lydia Wangab6734e2009-10-10 19:08:46 +0800341
Joseph Chanc577b8a2006-11-29 15:29:40 +0100342/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200343static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
344 const struct snd_kcontrol_new *tmpl,
345 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100346{
347 struct snd_kcontrol_new *knew;
348
Takashi Iwai603c4012008-07-30 15:01:44 +0200349 snd_array_init(&spec->kctls, sizeof(*knew), 32);
350 knew = snd_array_new(&spec->kctls);
351 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200352 return NULL;
353 *knew = *tmpl;
354 if (!name)
355 name = tmpl->name;
356 if (name) {
357 knew->name = kstrdup(name, GFP_KERNEL);
358 if (!knew->name)
359 return NULL;
360 }
361 return knew;
362}
363
364static int __via_add_control(struct via_spec *spec, int type, const char *name,
365 int idx, unsigned long val)
366{
367 struct snd_kcontrol_new *knew;
368
369 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
370 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100371 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200372 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100373 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100374 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100375 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100376 return 0;
377}
378
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200379#define via_add_control(spec, type, name, val) \
380 __via_add_control(spec, type, name, 0, val)
381
Takashi Iwai291c9e32011-06-17 16:15:26 +0200382#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100383
Takashi Iwai603c4012008-07-30 15:01:44 +0200384static void via_free_kctls(struct hda_codec *codec)
385{
386 struct via_spec *spec = codec->spec;
387
388 if (spec->kctls.list) {
389 struct snd_kcontrol_new *kctl = spec->kctls.list;
390 int i;
391 for (i = 0; i < spec->kctls.used; i++)
392 kfree(kctl[i].name);
393 }
394 snd_array_free(&spec->kctls);
395}
396
Joseph Chanc577b8a2006-11-29 15:29:40 +0100397/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800398static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200399 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100400{
401 char name[32];
402 int err;
403
404 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200405 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100406 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
407 if (err < 0)
408 return err;
409 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200410 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100411 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
412 if (err < 0)
413 return err;
414 return 0;
415}
416
Takashi Iwai5d417622011-06-20 11:32:27 +0200417#define get_connection_index(codec, mux, nid) \
Takashi Iwai8d087c72011-06-28 12:45:47 +0200418 snd_hda_get_conn_index(codec, mux, nid, 0)
Takashi Iwai5d417622011-06-20 11:32:27 +0200419
Takashi Iwai8df2a312011-06-21 11:48:29 +0200420static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
421 unsigned int mask)
422{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200423 unsigned int caps;
424 if (!nid)
425 return false;
426 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200427 if (dir == HDA_INPUT)
428 caps &= AC_WCAP_IN_AMP;
429 else
430 caps &= AC_WCAP_OUT_AMP;
431 if (!caps)
432 return false;
433 if (query_amp_caps(codec, nid, dir) & mask)
434 return true;
435 return false;
436}
437
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200438#define have_mute(codec, nid, dir) \
439 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200440
Lydia Wangd69607b2011-07-08 14:02:52 +0800441static bool is_node_in_path(struct nid_path *path, hda_nid_t nid)
442{
443 int i;
444 if (!nid)
445 return false;
446 for (i = 0; i < path->depth; i++) {
447 if (path->path[i] == nid)
448 return true;
449 }
450 return false;
451}
452
453/* enable/disable the output-route mixers */
454static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
455 hda_nid_t mix_nid, int aa_mix_idx, bool enable)
456{
457 int i, num, val;
458 bool hp_path, front_path;
459 struct via_spec *spec = codec->spec;
460
461 if (!path)
462 return;
463 num = snd_hda_get_conn_list(codec, mix_nid, NULL);
464 hp_path = is_node_in_path(path, spec->hp_dac_nid);
465 front_path = is_node_in_path(path, spec->multiout.dac_nids[0]);
466
467 for (i = 0; i < num; i++) {
468 if (i == aa_mix_idx) {
469 if (hp_path)
470 val = enable ? AMP_IN_MUTE(i) :
471 AMP_IN_UNMUTE(i);
472 else if (front_path)
473 val = AMP_IN_UNMUTE(i);
474 else
475 val = AMP_IN_MUTE(i);
476 } else {
477 if (hp_path)
478 val = enable ? AMP_IN_UNMUTE(i) :
479 AMP_IN_MUTE(i);
480 else if (front_path)
481 val = AMP_IN_MUTE(i);
482 else
483 val = AMP_IN_UNMUTE(i);
484 }
485 snd_hda_codec_write(codec, mix_nid, 0,
486 AC_VERB_SET_AMP_GAIN_MUTE, val);
487 }
488}
489
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200490/* enable/disable the output-route */
491static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
492 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200493{
Lydia Wangd69607b2011-07-08 14:02:52 +0800494 int i, val;
495 struct via_spec *spec = codec->spec;
496 hda_nid_t aa_mix_nid = spec->aa_mix_nid;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200497 for (i = 0; i < path->depth; i++) {
498 hda_nid_t src, dst;
499 int idx = path->idx[i];
500 src = path->path[i];
501 if (i < path->depth - 1)
502 dst = path->path[i + 1];
503 else
504 dst = 0;
505 if (enable && path->multi[i])
506 snd_hda_codec_write(codec, dst, 0,
507 AC_VERB_SET_CONNECT_SEL, idx);
Lydia Wangb89596a2011-07-04 17:01:33 +0800508 if (!force
509 && get_wcaps_type(get_wcaps(codec, src)) == AC_WID_AUD_OUT
510 && get_wcaps_type(get_wcaps(codec, dst)) == AC_WID_AUD_MIX)
Lydia Wange5e14682011-07-01 10:55:07 +0800511 continue;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200512 if (have_mute(codec, dst, HDA_INPUT)) {
Lydia Wangd69607b2011-07-08 14:02:52 +0800513 if (dst == aa_mix_nid) {
514 val = enable ? AMP_IN_UNMUTE(idx) :
515 AMP_IN_MUTE(idx);
516 snd_hda_codec_write(codec, dst, 0,
517 AC_VERB_SET_AMP_GAIN_MUTE, val);
518 } else {
519 idx = get_connection_index(codec, dst,
520 aa_mix_nid);
521 if (idx >= 0) {
522 activate_output_mix(codec, path,
523 dst, idx, enable);
524 }
525 }
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200526 }
527 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
528 continue;
529 if (have_mute(codec, src, HDA_OUTPUT)) {
530 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
531 snd_hda_codec_write(codec, src, 0,
532 AC_VERB_SET_AMP_GAIN_MUTE, val);
533 }
534 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200535}
536
537/* set the given pin as output */
538static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
539 int pin_type)
540{
541 if (!pin)
542 return;
543 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
544 pin_type);
545 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
546 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200547 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100548}
549
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200550static void via_auto_init_output(struct hda_codec *codec,
551 struct nid_path *path, int pin_type,
Takashi Iwaibac4b922011-07-04 17:35:51 +0200552 bool with_aa_mix, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200553{
554 struct via_spec *spec = codec->spec;
555 unsigned int caps;
Lydia Wangd69607b2011-07-08 14:02:52 +0800556 hda_nid_t pin;
Takashi Iwai5d417622011-06-20 11:32:27 +0200557
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200558 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200559 return;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200560 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200561
562 init_output_pin(codec, pin, pin_type);
563 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
564 if (caps & AC_AMPCAP_MUTE) {
565 unsigned int val;
566 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
567 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
568 AMP_OUT_MUTE | val);
569 }
570
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200571 /* initialize the AA-path */
572 if (!spec->aa_mix_nid)
573 return;
Lydia Wangd69607b2011-07-08 14:02:52 +0800574 activate_output_path(codec, path, true, force);
Takashi Iwai5d417622011-06-20 11:32:27 +0200575}
576
Joseph Chanc577b8a2006-11-29 15:29:40 +0100577static void via_auto_init_multi_out(struct hda_codec *codec)
578{
579 struct via_spec *spec = codec->spec;
580 int i;
581
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200582 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwaibac4b922011-07-04 17:35:51 +0200583 /* enable aa-mute only for the front channel */
584 via_auto_init_output(codec, &spec->out_path[i], PIN_OUT,
585 i == 0, true);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100586}
587
588static void via_auto_init_hp_out(struct hda_codec *codec)
589{
590 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100591
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200592 if (!spec->hp_dac_nid) {
Takashi Iwaibac4b922011-07-04 17:35:51 +0200593 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP,
594 true, true);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200595 return;
596 }
597 if (spec->hp_independent_mode) {
598 activate_output_path(codec, &spec->hp_dep_path, false, false);
Takashi Iwaibac4b922011-07-04 17:35:51 +0200599 via_auto_init_output(codec, &spec->hp_path, PIN_HP,
600 true, true);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200601 } else {
602 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwaibac4b922011-07-04 17:35:51 +0200603 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP,
604 true, true);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200605 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100606}
607
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200608static void via_auto_init_speaker_out(struct hda_codec *codec)
609{
610 struct via_spec *spec = codec->spec;
611
612 if (spec->autocfg.speaker_outs)
Takashi Iwaibac4b922011-07-04 17:35:51 +0200613 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT,
614 true, true);
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200615}
616
Takashi Iwaif4a78282011-06-17 18:46:48 +0200617static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200618
Joseph Chanc577b8a2006-11-29 15:29:40 +0100619static void via_auto_init_analog_input(struct hda_codec *codec)
620{
621 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200622 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200623 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200624 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200625 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100626
Takashi Iwai096a8852011-06-20 12:09:02 +0200627 /* init ADCs */
628 for (i = 0; i < spec->num_adc_nids; i++) {
629 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
630 AC_VERB_SET_AMP_GAIN_MUTE,
631 AMP_IN_UNMUTE(0));
632 }
633
634 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200635 for (i = 0; i < cfg->num_inputs; i++) {
636 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200637 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200638 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100639 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200640 ctl = PIN_VREF50;
641 else
642 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100643 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200644 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100645 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200646
647 /* init input-src */
648 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200649 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
650 if (spec->mux_nids[adc_idx]) {
651 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
652 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
653 AC_VERB_SET_CONNECT_SEL,
654 mux_idx);
655 }
656 if (spec->dyn_adc_switch)
657 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200658 }
659
660 /* init aa-mixer */
661 if (!spec->aa_mix_nid)
662 return;
663 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
664 ARRAY_SIZE(conn));
665 for (i = 0; i < num_conns; i++) {
666 unsigned int caps = get_wcaps(codec, conn[i]);
667 if (get_wcaps_type(caps) == AC_WID_PIN)
668 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
669 AC_VERB_SET_AMP_GAIN_MUTE,
670 AMP_IN_MUTE(i));
671 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100672}
Lydia Wangf5271102009-10-10 19:07:35 +0800673
674static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
675 unsigned int *affected_parm)
676{
677 unsigned parm;
678 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
679 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
680 >> AC_DEFCFG_MISC_SHIFT
681 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800682 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200683 unsigned present = 0;
684
685 no_presence |= spec->no_pin_power_ctl;
686 if (!no_presence)
687 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200688 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800689 || ((no_presence || present)
690 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800691 *affected_parm = AC_PWRST_D0; /* if it's connected */
692 parm = AC_PWRST_D0;
693 } else
694 parm = AC_PWRST_D3;
695
696 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
697}
698
Takashi Iwai24088a52011-06-17 16:59:21 +0200699static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
700 struct snd_ctl_elem_info *uinfo)
701{
702 static const char * const texts[] = {
703 "Disabled", "Enabled"
704 };
705
706 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
707 uinfo->count = 1;
708 uinfo->value.enumerated.items = 2;
709 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
710 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
711 strcpy(uinfo->value.enumerated.name,
712 texts[uinfo->value.enumerated.item]);
713 return 0;
714}
715
716static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
717 struct snd_ctl_elem_value *ucontrol)
718{
719 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
720 struct via_spec *spec = codec->spec;
721 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
722 return 0;
723}
724
725static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
726 struct snd_ctl_elem_value *ucontrol)
727{
728 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
729 struct via_spec *spec = codec->spec;
730 unsigned int val = !ucontrol->value.enumerated.item[0];
731
732 if (val == spec->no_pin_power_ctl)
733 return 0;
734 spec->no_pin_power_ctl = val;
735 set_widgets_power_state(codec);
736 return 1;
737}
738
739static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
740 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
741 .name = "Dynamic Power-Control",
742 .info = via_pin_power_ctl_info,
743 .get = via_pin_power_ctl_get,
744 .put = via_pin_power_ctl_put,
745};
746
747
Harald Welte0aa62ae2008-09-09 15:58:27 +0800748static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
749 struct snd_ctl_elem_info *uinfo)
750{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200751 static const char * const texts[] = { "OFF", "ON" };
752
753 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
754 uinfo->count = 1;
755 uinfo->value.enumerated.items = 2;
756 if (uinfo->value.enumerated.item >= 2)
757 uinfo->value.enumerated.item = 1;
758 strcpy(uinfo->value.enumerated.name,
759 texts[uinfo->value.enumerated.item]);
760 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800761}
762
763static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
764 struct snd_ctl_elem_value *ucontrol)
765{
766 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800767 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800768
Takashi Iwaiece8d042011-06-19 16:24:21 +0200769 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800770 return 0;
771}
772
Harald Welte0aa62ae2008-09-09 15:58:27 +0800773static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
774 struct snd_ctl_elem_value *ucontrol)
775{
776 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
777 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +0200778 int cur;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200779
Takashi Iwai25250502011-06-30 17:24:47 +0200780 /* no independent-hp status change during PCM playback is running */
781 if (spec->num_active_streams)
782 return -EBUSY;
783
784 cur = !!ucontrol->value.enumerated.item[0];
785 if (spec->hp_independent_mode == cur)
786 return 0;
787 spec->hp_independent_mode = cur;
788 if (cur) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200789 activate_output_path(codec, &spec->hp_dep_path, false, false);
790 activate_output_path(codec, &spec->hp_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200791 if (spec->hp_indep_shared)
792 activate_output_path(codec, &spec->out_path[HDA_SIDE],
793 false, false);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200794 } else {
795 activate_output_path(codec, &spec->hp_path, false, false);
796 activate_output_path(codec, &spec->hp_dep_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200797 if (spec->hp_indep_shared)
798 activate_output_path(codec, &spec->out_path[HDA_SIDE],
799 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200800 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800801
Lydia Wangce0e5a92011-03-22 16:22:37 +0800802 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800803 set_widgets_power_state(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200804 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800805}
806
Takashi Iwaiece8d042011-06-19 16:24:21 +0200807static const struct snd_kcontrol_new via_hp_mixer = {
808 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
809 .name = "Independent HP",
810 .info = via_independent_hp_info,
811 .get = via_independent_hp_get,
812 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800813};
814
Takashi Iwai3d83e572010-04-14 14:36:23 +0200815static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100816{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200817 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100818 struct snd_kcontrol_new *knew;
819 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100820
Takashi Iwaiece8d042011-06-19 16:24:21 +0200821 nid = spec->autocfg.hp_pins[0];
822 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200823 if (knew == NULL)
824 return -ENOMEM;
825
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100826 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100827
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100828 return 0;
829}
830
Lydia Wang1564b282009-10-10 19:07:52 +0800831static void notify_aa_path_ctls(struct hda_codec *codec)
832{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200833 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800834 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800835
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200836 for (i = 0; i < spec->smart51_nums; i++) {
837 struct snd_kcontrol *ctl;
838 struct snd_ctl_elem_id id;
839 memset(&id, 0, sizeof(id));
840 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
841 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800842 ctl = snd_hda_find_mixer_ctl(codec, id.name);
843 if (ctl)
844 snd_ctl_notify(codec->bus->card,
845 SNDRV_CTL_EVENT_MASK_VALUE,
846 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800847 }
848}
849
850static void mute_aa_path(struct hda_codec *codec, int mute)
851{
852 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200853 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800854 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200855
Lydia Wang1564b282009-10-10 19:07:52 +0800856 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200857 for (i = 0; i < spec->smart51_nums; i++) {
858 if (spec->smart51_idxs[i] < 0)
859 continue;
860 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
861 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800862 HDA_AMP_MUTE, val);
863 }
864}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200865
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200866static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
867{
868 struct via_spec *spec = codec->spec;
869 int i;
870
871 for (i = 0; i < spec->smart51_nums; i++)
872 if (spec->smart51_pins[i] == pin)
873 return true;
874 return false;
875}
876
Lydia Wang1564b282009-10-10 19:07:52 +0800877static int via_smart51_get(struct snd_kcontrol *kcontrol,
878 struct snd_ctl_elem_value *ucontrol)
879{
880 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
881 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800882
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200883 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800884 return 0;
885}
886
887static int via_smart51_put(struct snd_kcontrol *kcontrol,
888 struct snd_ctl_elem_value *ucontrol)
889{
890 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
891 struct via_spec *spec = codec->spec;
892 int out_in = *ucontrol->value.integer.value
893 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800894 int i;
895
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200896 for (i = 0; i < spec->smart51_nums; i++) {
897 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200898 unsigned int parm;
899
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200900 parm = snd_hda_codec_read(codec, nid, 0,
901 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
902 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
903 parm |= out_in;
904 snd_hda_codec_write(codec, nid, 0,
905 AC_VERB_SET_PIN_WIDGET_CONTROL,
906 parm);
907 if (out_in == AC_PINCTL_OUT_EN) {
908 mute_aa_path(codec, 1);
909 notify_aa_path_ctls(codec);
910 }
Lydia Wang1564b282009-10-10 19:07:52 +0800911 }
912 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800913 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800914 return 1;
915}
916
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200917static const struct snd_kcontrol_new via_smart51_mixer = {
918 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
919 .name = "Smart 5.1",
920 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200921 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200922 .get = via_smart51_get,
923 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800924};
925
Takashi Iwaif4a78282011-06-17 18:46:48 +0200926static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100927{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200928 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100929
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200930 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800931 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200932 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100933 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100934 return 0;
935}
936
Takashi Iwaiada509e2011-06-20 15:40:19 +0200937/* check AA path's mute status */
938static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800939{
Lydia Wangf5271102009-10-10 19:07:35 +0800940 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200941 const struct hda_amp_list *p;
942 int i, ch, v;
943
944 for (i = 0; i < spec->num_loopbacks; i++) {
945 p = &spec->loopback_list[i];
946 for (ch = 0; ch < 2; ch++) {
947 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
948 p->idx);
949 if (!(v & HDA_AMP_MUTE) && v > 0)
950 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800951 }
952 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200953 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800954}
955
956/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200957static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800958{
959 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200960 bool enable;
961 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800962
Takashi Iwaiada509e2011-06-20 15:40:19 +0200963 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800964
965 /* decide low current mode's verb & parameter */
966 switch (spec->codec_type) {
967 case VT1708B_8CH:
968 case VT1708B_4CH:
969 verb = 0xf70;
970 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
971 break;
972 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800973 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800974 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800975 verb = 0xf73;
976 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
977 break;
978 case VT1702:
979 verb = 0xf73;
980 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
981 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800982 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800983 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800984 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800985 verb = 0xf93;
986 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
987 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800988 default:
989 return; /* other codecs are not supported */
990 }
991 /* send verb */
992 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
993}
994
Joseph Chanc577b8a2006-11-29 15:29:40 +0100995/*
996 * generic initialization of ADC, input mixers and output mixers
997 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200998static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800999 /* power down jack detect function */
1000 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001001 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001002};
1003
Takashi Iwaiada509e2011-06-20 15:40:19 +02001004static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001005{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001006 struct via_spec *spec = codec->spec;
1007
1008 if (active)
1009 spec->num_active_streams++;
1010 else
1011 spec->num_active_streams--;
1012 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001013}
1014
Takashi Iwaiece8d042011-06-19 16:24:21 +02001015static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001016 struct hda_codec *codec,
1017 struct snd_pcm_substream *substream)
1018{
1019 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001020 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001021 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001022
Takashi Iwai25250502011-06-30 17:24:47 +02001023 spec->multiout.hp_nid = 0;
1024 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
1025 if (!spec->hp_independent_mode) {
1026 if (!spec->hp_indep_shared)
1027 spec->multiout.hp_nid = spec->hp_dac_nid;
1028 } else {
1029 if (spec->hp_indep_shared)
1030 spec->multiout.num_dacs = cfg->line_outs - 1;
1031 }
1032 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001033 set_stream_active(codec, true);
1034 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1035 hinfo);
1036 if (err < 0) {
1037 spec->multiout.hp_nid = 0;
1038 set_stream_active(codec, false);
1039 return err;
1040 }
1041 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001042}
1043
Takashi Iwaiece8d042011-06-19 16:24:21 +02001044static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001045 struct hda_codec *codec,
1046 struct snd_pcm_substream *substream)
1047{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001048 struct via_spec *spec = codec->spec;
1049
1050 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001051 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001052 return 0;
1053}
1054
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001055static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1056 struct hda_codec *codec,
1057 struct snd_pcm_substream *substream)
1058{
1059 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001060
Takashi Iwaiece8d042011-06-19 16:24:21 +02001061 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001062 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001063 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1064 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001065 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001066 return 0;
1067}
1068
1069static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1070 struct hda_codec *codec,
1071 struct snd_pcm_substream *substream)
1072{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001073 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001074 return 0;
1075}
1076
1077static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1078 struct hda_codec *codec,
1079 unsigned int stream_tag,
1080 unsigned int format,
1081 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001082{
1083 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001084
Takashi Iwaiece8d042011-06-19 16:24:21 +02001085 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1086 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001087 vt1708_start_hp_work(spec);
1088 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001089}
1090
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001091static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1092 struct hda_codec *codec,
1093 unsigned int stream_tag,
1094 unsigned int format,
1095 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001096{
1097 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001098
Takashi Iwaiece8d042011-06-19 16:24:21 +02001099 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1100 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001101 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001102 return 0;
1103}
1104
1105static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1106 struct hda_codec *codec,
1107 struct snd_pcm_substream *substream)
1108{
1109 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001110
Takashi Iwaiece8d042011-06-19 16:24:21 +02001111 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001112 vt1708_stop_hp_work(spec);
1113 return 0;
1114}
1115
1116static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1117 struct hda_codec *codec,
1118 struct snd_pcm_substream *substream)
1119{
1120 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001121
Takashi Iwaiece8d042011-06-19 16:24:21 +02001122 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001123 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001124 return 0;
1125}
1126
Joseph Chanc577b8a2006-11-29 15:29:40 +01001127/*
1128 * Digital out
1129 */
1130static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1131 struct hda_codec *codec,
1132 struct snd_pcm_substream *substream)
1133{
1134 struct via_spec *spec = codec->spec;
1135 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1136}
1137
1138static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1139 struct hda_codec *codec,
1140 struct snd_pcm_substream *substream)
1141{
1142 struct via_spec *spec = codec->spec;
1143 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1144}
1145
Harald Welte5691ec72008-09-15 22:42:26 +08001146static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001147 struct hda_codec *codec,
1148 unsigned int stream_tag,
1149 unsigned int format,
1150 struct snd_pcm_substream *substream)
1151{
1152 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001153 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1154 stream_tag, format, substream);
1155}
Harald Welte5691ec72008-09-15 22:42:26 +08001156
Takashi Iwai9da29272009-05-07 16:31:14 +02001157static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1158 struct hda_codec *codec,
1159 struct snd_pcm_substream *substream)
1160{
1161 struct via_spec *spec = codec->spec;
1162 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001163 return 0;
1164}
1165
Joseph Chanc577b8a2006-11-29 15:29:40 +01001166/*
1167 * Analog capture
1168 */
1169static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1170 struct hda_codec *codec,
1171 unsigned int stream_tag,
1172 unsigned int format,
1173 struct snd_pcm_substream *substream)
1174{
1175 struct via_spec *spec = codec->spec;
1176
1177 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1178 stream_tag, 0, format);
1179 return 0;
1180}
1181
1182static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1183 struct hda_codec *codec,
1184 struct snd_pcm_substream *substream)
1185{
1186 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001187 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001188 return 0;
1189}
1190
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001191/* analog capture with dynamic ADC switching */
1192static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1193 struct hda_codec *codec,
1194 unsigned int stream_tag,
1195 unsigned int format,
1196 struct snd_pcm_substream *substream)
1197{
1198 struct via_spec *spec = codec->spec;
1199 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1200
1201 spec->cur_adc = spec->adc_nids[adc_idx];
1202 spec->cur_adc_stream_tag = stream_tag;
1203 spec->cur_adc_format = format;
1204 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
1205 return 0;
1206}
1207
1208static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1209 struct hda_codec *codec,
1210 struct snd_pcm_substream *substream)
1211{
1212 struct via_spec *spec = codec->spec;
1213
1214 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1215 spec->cur_adc = 0;
1216 return 0;
1217}
1218
1219/* re-setup the stream if running; called from input-src put */
1220static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1221{
1222 struct via_spec *spec = codec->spec;
1223 int adc_idx = spec->inputs[cur].adc_idx;
1224 hda_nid_t adc = spec->adc_nids[adc_idx];
1225
1226 if (spec->cur_adc && spec->cur_adc != adc) {
1227 /* stream is running, let's swap the current ADC */
1228 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1229 spec->cur_adc = adc;
1230 snd_hda_codec_setup_stream(codec, adc,
1231 spec->cur_adc_stream_tag, 0,
1232 spec->cur_adc_format);
1233 return true;
1234 }
1235 return false;
1236}
1237
Takashi Iwai9af74212011-06-18 16:17:45 +02001238static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001239 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001240 .channels_min = 2,
1241 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001242 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001243 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001244 .open = via_playback_multi_pcm_open,
1245 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001246 .prepare = via_playback_multi_pcm_prepare,
1247 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001248 },
1249};
1250
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001251static const struct hda_pcm_stream via_pcm_hp_playback = {
1252 .substreams = 1,
1253 .channels_min = 2,
1254 .channels_max = 2,
1255 /* NID is set in via_build_pcms */
1256 .ops = {
1257 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001258 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001259 .prepare = via_playback_hp_pcm_prepare,
1260 .cleanup = via_playback_hp_pcm_cleanup
1261 },
1262};
1263
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001264static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001265 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001266 .channels_min = 2,
1267 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001268 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001269 /* We got noisy outputs on the right channel on VT1708 when
1270 * 24bit samples are used. Until any workaround is found,
1271 * disable the 24bit format, so far.
1272 */
1273 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1274 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001275 .open = via_playback_multi_pcm_open,
1276 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001277 .prepare = via_playback_multi_pcm_prepare,
1278 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001279 },
1280};
1281
Takashi Iwai9af74212011-06-18 16:17:45 +02001282static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001283 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001284 .channels_min = 2,
1285 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001286 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001287 .ops = {
1288 .prepare = via_capture_pcm_prepare,
1289 .cleanup = via_capture_pcm_cleanup
1290 },
1291};
1292
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001293static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1294 .substreams = 1,
1295 .channels_min = 2,
1296 .channels_max = 2,
1297 /* NID is set in via_build_pcms */
1298 .ops = {
1299 .prepare = via_dyn_adc_capture_pcm_prepare,
1300 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1301 },
1302};
1303
Takashi Iwai9af74212011-06-18 16:17:45 +02001304static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001305 .substreams = 1,
1306 .channels_min = 2,
1307 .channels_max = 2,
1308 /* NID is set in via_build_pcms */
1309 .ops = {
1310 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001311 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001312 .prepare = via_dig_playback_pcm_prepare,
1313 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001314 },
1315};
1316
Takashi Iwai9af74212011-06-18 16:17:45 +02001317static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001318 .substreams = 1,
1319 .channels_min = 2,
1320 .channels_max = 2,
1321};
1322
Takashi Iwai370bafb2011-06-20 12:47:45 +02001323/*
1324 * slave controls for virtual master
1325 */
1326static const char * const via_slave_vols[] = {
1327 "Front Playback Volume",
1328 "Surround Playback Volume",
1329 "Center Playback Volume",
1330 "LFE Playback Volume",
1331 "Side Playback Volume",
1332 "Headphone Playback Volume",
1333 "Speaker Playback Volume",
1334 NULL,
1335};
1336
1337static const char * const via_slave_sws[] = {
1338 "Front Playback Switch",
1339 "Surround Playback Switch",
1340 "Center Playback Switch",
1341 "LFE Playback Switch",
1342 "Side Playback Switch",
1343 "Headphone Playback Switch",
1344 "Speaker Playback Switch",
1345 NULL,
1346};
1347
Joseph Chanc577b8a2006-11-29 15:29:40 +01001348static int via_build_controls(struct hda_codec *codec)
1349{
1350 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001351 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001352 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001353
Takashi Iwai24088a52011-06-17 16:59:21 +02001354 if (spec->set_widgets_power_state)
1355 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1356 return -ENOMEM;
1357
Joseph Chanc577b8a2006-11-29 15:29:40 +01001358 for (i = 0; i < spec->num_mixers; i++) {
1359 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1360 if (err < 0)
1361 return err;
1362 }
1363
1364 if (spec->multiout.dig_out_nid) {
1365 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001366 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001367 spec->multiout.dig_out_nid);
1368 if (err < 0)
1369 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001370 err = snd_hda_create_spdif_share_sw(codec,
1371 &spec->multiout);
1372 if (err < 0)
1373 return err;
1374 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001375 }
1376 if (spec->dig_in_nid) {
1377 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1378 if (err < 0)
1379 return err;
1380 }
Lydia Wang17314372009-10-10 19:07:37 +08001381
Takashi Iwai370bafb2011-06-20 12:47:45 +02001382 /* if we have no master control, let's create it */
1383 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1384 unsigned int vmaster_tlv[4];
1385 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1386 HDA_OUTPUT, vmaster_tlv);
1387 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1388 vmaster_tlv, via_slave_vols);
1389 if (err < 0)
1390 return err;
1391 }
1392 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1393 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1394 NULL, via_slave_sws);
1395 if (err < 0)
1396 return err;
1397 }
1398
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001399 /* assign Capture Source enums to NID */
1400 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1401 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001402 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001403 if (err < 0)
1404 return err;
1405 }
1406
Lydia Wang17314372009-10-10 19:07:37 +08001407 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001408 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001409 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001410
Takashi Iwai603c4012008-07-30 15:01:44 +02001411 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001412 return 0;
1413}
1414
1415static int via_build_pcms(struct hda_codec *codec)
1416{
1417 struct via_spec *spec = codec->spec;
1418 struct hda_pcm *info = spec->pcm_rec;
1419
1420 codec->num_pcms = 1;
1421 codec->pcm_info = info;
1422
Takashi Iwai82673bc2011-06-17 16:24:21 +02001423 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1424 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001425 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001426
1427 if (!spec->stream_analog_playback)
1428 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001429 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001430 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001431 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1432 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001433 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1434 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001435
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001436 if (!spec->stream_analog_capture) {
1437 if (spec->dyn_adc_switch)
1438 spec->stream_analog_capture =
1439 &via_pcm_dyn_adc_analog_capture;
1440 else
1441 spec->stream_analog_capture = &via_pcm_analog_capture;
1442 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001443 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1444 *spec->stream_analog_capture;
1445 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001446 if (!spec->dyn_adc_switch)
1447 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1448 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001449
1450 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1451 codec->num_pcms++;
1452 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001453 snprintf(spec->stream_name_digital,
1454 sizeof(spec->stream_name_digital),
1455 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001456 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001457 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001458 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001459 if (!spec->stream_digital_playback)
1460 spec->stream_digital_playback =
1461 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001462 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001463 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001464 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1465 spec->multiout.dig_out_nid;
1466 }
1467 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001468 if (!spec->stream_digital_capture)
1469 spec->stream_digital_capture =
1470 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001471 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001472 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001473 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1474 spec->dig_in_nid;
1475 }
1476 }
1477
Takashi Iwaiece8d042011-06-19 16:24:21 +02001478 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001479 codec->num_pcms++;
1480 info++;
1481 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1482 "%s HP", codec->chip_name);
1483 info->name = spec->stream_name_hp;
1484 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1485 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001486 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001487 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001488 return 0;
1489}
1490
1491static void via_free(struct hda_codec *codec)
1492{
1493 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001494
1495 if (!spec)
1496 return;
1497
Takashi Iwai603c4012008-07-30 15:01:44 +02001498 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001499 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001500 kfree(spec->bind_cap_vol);
1501 kfree(spec->bind_cap_sw);
1502 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001503}
1504
Takashi Iwai64be2852011-06-17 16:51:39 +02001505/* mute/unmute outputs */
1506static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1507 hda_nid_t *pins, bool mute)
1508{
1509 int i;
1510 for (i = 0; i < num_pins; i++)
1511 snd_hda_codec_write(codec, pins[i], 0,
1512 AC_VERB_SET_PIN_WIDGET_CONTROL,
1513 mute ? 0 : PIN_OUT);
1514}
1515
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001516/* mute internal speaker if line-out is plugged */
1517static void via_line_automute(struct hda_codec *codec, int present)
1518{
1519 struct via_spec *spec = codec->spec;
1520
1521 if (!spec->autocfg.speaker_outs)
1522 return;
1523 if (!present)
1524 present = snd_hda_jack_detect(codec,
1525 spec->autocfg.line_out_pins[0]);
1526 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1527 spec->autocfg.speaker_pins,
1528 present);
1529}
1530
Harald Welte69e52a82008-09-09 15:57:32 +08001531/* mute internal speaker if HP is plugged */
1532static void via_hp_automute(struct hda_codec *codec)
1533{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001534 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001535 struct via_spec *spec = codec->spec;
1536
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001537 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001538 int nums;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001539 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001540 if (spec->smart51_enabled)
1541 nums = spec->autocfg.line_outs + spec->smart51_nums;
1542 else
1543 nums = spec->autocfg.line_outs;
1544 toggle_output_mutes(codec, nums,
Takashi Iwai64be2852011-06-17 16:51:39 +02001545 spec->autocfg.line_out_pins,
1546 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001547 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001548 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001549}
1550
Harald Welte69e52a82008-09-09 15:57:32 +08001551static void via_gpio_control(struct hda_codec *codec)
1552{
1553 unsigned int gpio_data;
1554 unsigned int vol_counter;
1555 unsigned int vol;
1556 unsigned int master_vol;
1557
1558 struct via_spec *spec = codec->spec;
1559
1560 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1561 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1562
1563 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1564 0xF84, 0) & 0x3F0000) >> 16;
1565
1566 vol = vol_counter & 0x1F;
1567 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1568 AC_VERB_GET_AMP_GAIN_MUTE,
1569 AC_AMP_GET_INPUT);
1570
1571 if (gpio_data == 0x02) {
1572 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001573 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1574 AC_VERB_SET_PIN_WIDGET_CONTROL,
1575 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001576 if (vol_counter & 0x20) {
1577 /* decrease volume */
1578 if (vol > master_vol)
1579 vol = master_vol;
1580 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1581 0, HDA_AMP_VOLMASK,
1582 master_vol-vol);
1583 } else {
1584 /* increase volume */
1585 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1586 HDA_AMP_VOLMASK,
1587 ((master_vol+vol) > 0x2A) ? 0x2A :
1588 (master_vol+vol));
1589 }
1590 } else if (!(gpio_data & 0x02)) {
1591 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001592 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1593 AC_VERB_SET_PIN_WIDGET_CONTROL,
1594 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001595 }
1596}
1597
1598/* unsolicited event for jack sensing */
1599static void via_unsol_event(struct hda_codec *codec,
1600 unsigned int res)
1601{
1602 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001603
Lydia Wanga34df192009-10-10 19:08:01 +08001604 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001605 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001606
1607 res &= ~VIA_JACK_EVENT;
1608
1609 if (res == VIA_HP_EVENT)
1610 via_hp_automute(codec);
1611 else if (res == VIA_GPIO_EVENT)
1612 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001613 else if (res == VIA_LINE_EVENT)
1614 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001615}
1616
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001617#ifdef SND_HDA_NEEDS_RESUME
1618static int via_suspend(struct hda_codec *codec, pm_message_t state)
1619{
1620 struct via_spec *spec = codec->spec;
1621 vt1708_stop_hp_work(spec);
1622 return 0;
1623}
1624#endif
1625
Takashi Iwaicb53c622007-08-10 17:21:45 +02001626#ifdef CONFIG_SND_HDA_POWER_SAVE
1627static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1628{
1629 struct via_spec *spec = codec->spec;
1630 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1631}
1632#endif
1633
Joseph Chanc577b8a2006-11-29 15:29:40 +01001634/*
1635 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001636
1637static int via_init(struct hda_codec *codec);
1638
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001639static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001640 .build_controls = via_build_controls,
1641 .build_pcms = via_build_pcms,
1642 .init = via_init,
1643 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001644 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001645#ifdef SND_HDA_NEEDS_RESUME
1646 .suspend = via_suspend,
1647#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001648#ifdef CONFIG_SND_HDA_POWER_SAVE
1649 .check_power_status = via_check_power_status,
1650#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001651};
1652
Takashi Iwai4a796162011-06-17 17:53:38 +02001653static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001654{
Takashi Iwai4a796162011-06-17 17:53:38 +02001655 struct via_spec *spec = codec->spec;
1656 int i;
1657
1658 for (i = 0; i < spec->multiout.num_dacs; i++) {
1659 if (spec->multiout.dac_nids[i] == dac)
1660 return false;
1661 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001662 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001663 return false;
1664 return true;
1665}
1666
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001667static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001668 hda_nid_t target_dac, struct nid_path *path,
1669 int depth, int wid_type)
1670{
1671 hda_nid_t conn[8];
1672 int i, nums;
1673
1674 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1675 for (i = 0; i < nums; i++) {
1676 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1677 continue;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001678 if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
1679 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001680 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001681 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001682 return false;
1683 for (i = 0; i < nums; i++) {
1684 unsigned int type;
1685 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1686 if (type == AC_WID_AUD_OUT ||
1687 (wid_type != -1 && type != wid_type))
1688 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001689 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001690 path, depth + 1, AC_WID_AUD_SEL))
1691 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001692 }
1693 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001694
1695 found:
1696 path->path[path->depth] = conn[i];
1697 path->idx[path->depth] = i;
1698 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1699 path->multi[path->depth] = 1;
1700 path->depth++;
1701 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001702}
1703
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001704static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1705 hda_nid_t target_dac, struct nid_path *path)
1706{
1707 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1708 path->path[path->depth] = nid;
1709 path->depth++;
1710 return true;
1711 }
1712 return false;
1713}
1714
Takashi Iwai4a796162011-06-17 17:53:38 +02001715static int via_auto_fill_dac_nids(struct hda_codec *codec)
1716{
1717 struct via_spec *spec = codec->spec;
1718 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001719 int i;
1720 hda_nid_t nid;
1721
Joseph Chanc577b8a2006-11-29 15:29:40 +01001722 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001723 spec->multiout.num_dacs = cfg->line_outs;
1724 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001725 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001726 if (!nid)
1727 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001728 if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
1729 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001730 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001731 return 0;
1732}
1733
Takashi Iwai4a796162011-06-17 17:53:38 +02001734static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001735 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001736{
Takashi Iwai4a796162011-06-17 17:53:38 +02001737 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001738 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001739 hda_nid_t dac, pin, sel, nid;
1740 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001741
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001742 dac = check_dac ? path->path[0] : 0;
1743 pin = path->path[path->depth - 1];
1744 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001745
Takashi Iwai8df2a312011-06-21 11:48:29 +02001746 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001747 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001748 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001749 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001750 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1751 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001752 else
1753 nid = 0;
1754 if (nid) {
1755 sprintf(name, "%s Playback Volume", pfx);
1756 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001757 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001758 if (err < 0)
1759 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001760 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001761 }
1762
Takashi Iwai8df2a312011-06-21 11:48:29 +02001763 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001764 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001765 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001766 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001767 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1768 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001769 else
1770 nid = 0;
1771 if (nid) {
1772 sprintf(name, "%s Playback Switch", pfx);
1773 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1774 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1775 if (err < 0)
1776 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001777 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001778 }
1779 return 0;
1780}
1781
Takashi Iwaif4a78282011-06-17 18:46:48 +02001782static void mangle_smart51(struct hda_codec *codec)
1783{
1784 struct via_spec *spec = codec->spec;
1785 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001786 struct auto_pin_cfg_item *ins = cfg->inputs;
1787 int i, j, nums, attr;
1788 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001789
Takashi Iwai0f98c242011-06-21 12:51:33 +02001790 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1791 nums = 0;
1792 for (i = 0; i < cfg->num_inputs; i++) {
1793 unsigned int def;
1794 if (ins[i].type > AUTO_PIN_LINE_IN)
1795 continue;
1796 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1797 if (snd_hda_get_input_pin_attr(def) != attr)
1798 continue;
1799 for (j = 0; j < nums; j++)
1800 if (ins[pins[j]].type < ins[i].type) {
1801 memmove(pins + j + 1, pins + j,
1802 (nums - j - 1) * sizeof(int));
1803 break;
1804 }
1805 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001806 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001807 }
1808 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001809 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001810 for (i = 0; i < nums; i++) {
1811 hda_nid_t pin = ins[pins[i]].pin;
1812 spec->smart51_pins[spec->smart51_nums++] = pin;
1813 cfg->line_out_pins[cfg->line_outs++] = pin;
1814 if (cfg->line_outs == 3)
1815 break;
1816 }
1817 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001818 }
1819}
1820
Takashi Iwai4a796162011-06-17 17:53:38 +02001821/* add playback controls from the parsed DAC table */
1822static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1823{
1824 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001825 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001826 static const char * const chname[4] = {
1827 "Front", "Surround", "C/LFE", "Side"
1828 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001829 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001830 int old_line_outs;
1831
1832 /* check smart51 */
1833 old_line_outs = cfg->line_outs;
1834 if (cfg->line_outs == 1)
1835 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001836
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001837 err = via_auto_fill_dac_nids(codec);
1838 if (err < 0)
1839 return err;
1840
Takashi Iwai4a796162011-06-17 17:53:38 +02001841 for (i = 0; i < cfg->line_outs; i++) {
1842 hda_nid_t pin, dac;
1843 pin = cfg->line_out_pins[i];
1844 dac = spec->multiout.dac_nids[i];
1845 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001846 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001847 if (i == HDA_CLFE) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001848 err = create_ch_ctls(codec, "Center", 1, true,
1849 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001850 if (err < 0)
1851 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001852 err = create_ch_ctls(codec, "LFE", 2, true,
1853 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001854 if (err < 0)
1855 return err;
1856 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001857 const char *pfx = chname[i];
1858 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1859 cfg->line_outs == 1)
1860 pfx = "Speaker";
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001861 err = create_ch_ctls(codec, pfx, 3, true,
1862 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001863 if (err < 0)
1864 return err;
1865 }
1866 }
1867
Takashi Iwai4a796162011-06-17 17:53:38 +02001868 idx = get_connection_index(codec, spec->aa_mix_nid,
1869 spec->multiout.dac_nids[0]);
Lydia Wangc4394f52011-07-04 16:54:15 +08001870 if (idx < 0 && spec->dac_mixer_idx)
1871 idx = spec->dac_mixer_idx;
Takashi Iwai4a796162011-06-17 17:53:38 +02001872 if (idx >= 0) {
1873 /* add control to mixer */
1874 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1875 "PCM Playback Volume",
1876 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1877 idx, HDA_INPUT));
1878 if (err < 0)
1879 return err;
1880 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1881 "PCM Playback Switch",
1882 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1883 idx, HDA_INPUT));
1884 if (err < 0)
1885 return err;
1886 }
1887
Takashi Iwaif4a78282011-06-17 18:46:48 +02001888 cfg->line_outs = old_line_outs;
1889
Joseph Chanc577b8a2006-11-29 15:29:40 +01001890 return 0;
1891}
1892
Takashi Iwai4a796162011-06-17 17:53:38 +02001893static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001894{
Takashi Iwai4a796162011-06-17 17:53:38 +02001895 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001896 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001897 bool check_dac;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001898 int err;
1899
1900 if (!pin)
1901 return 0;
1902
Takashi Iwai8df2a312011-06-21 11:48:29 +02001903 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001904 spec->hp_dac_nid = spec->hp_path.path[0];
Takashi Iwai25250502011-06-30 17:24:47 +02001905 else if (spec->multiout.dac_nids[HDA_SIDE] &&
1906 parse_output_path(codec, pin,
1907 spec->multiout.dac_nids[HDA_SIDE],
1908 &spec->hp_path)) {
1909 spec->hp_dac_nid = spec->hp_path.path[0];
1910 spec->hp_indep_shared = true;
1911 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001912
Takashi Iwaiece8d042011-06-19 16:24:21 +02001913 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001914 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001915 !spec->hp_dac_nid)
1916 return 0;
1917
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001918 if (spec->hp_dac_nid && !spec->hp_indep_shared) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001919 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001920 check_dac = true;
1921 } else {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001922 path = &spec->hp_dep_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001923 check_dac = false;
1924 }
1925 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001926 if (err < 0)
1927 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001928 if (spec->hp_dac_nid) {
1929 spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
1930 spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
1931 }
Harald Welte0aa62ae2008-09-09 15:58:27 +08001932
Joseph Chanc577b8a2006-11-29 15:29:40 +01001933 return 0;
1934}
1935
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001936static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1937{
1938 struct via_spec *spec = codec->spec;
1939 hda_nid_t pin, dac;
1940
1941 pin = spec->autocfg.speaker_pins[0];
1942 if (!spec->autocfg.speaker_outs || !pin)
1943 return 0;
1944
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001945 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1946 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001947 spec->multiout.extra_out_nid[0] = dac;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001948 return create_ch_ctls(codec, "Speaker", 3, true,
1949 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001950 }
1951 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001952 &spec->speaker_path))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001953 return create_ch_ctls(codec, "Speaker", 3, false,
1954 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001955
1956 return 0;
1957}
1958
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001959/* look for ADCs */
1960static int via_fill_adcs(struct hda_codec *codec)
1961{
1962 struct via_spec *spec = codec->spec;
1963 hda_nid_t nid = codec->start_nid;
1964 int i;
1965
1966 for (i = 0; i < codec->num_nodes; i++, nid++) {
1967 unsigned int wcaps = get_wcaps(codec, nid);
1968 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1969 continue;
1970 if (wcaps & AC_WCAP_DIGITAL)
1971 continue;
1972 if (!(wcaps & AC_WCAP_CONN_LIST))
1973 continue;
1974 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1975 return -ENOMEM;
1976 spec->adc_nids[spec->num_adc_nids++] = nid;
1977 }
1978 return 0;
1979}
1980
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001981/* input-src control */
1982static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
1983 struct snd_ctl_elem_info *uinfo)
1984{
1985 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1986 struct via_spec *spec = codec->spec;
1987
1988 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1989 uinfo->count = 1;
1990 uinfo->value.enumerated.items = spec->num_inputs;
1991 if (uinfo->value.enumerated.item >= spec->num_inputs)
1992 uinfo->value.enumerated.item = spec->num_inputs - 1;
1993 strcpy(uinfo->value.enumerated.name,
1994 spec->inputs[uinfo->value.enumerated.item].label);
1995 return 0;
1996}
1997
1998static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
1999 struct snd_ctl_elem_value *ucontrol)
2000{
2001 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2002 struct via_spec *spec = codec->spec;
2003 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2004
2005 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2006 return 0;
2007}
2008
2009static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2010 struct snd_ctl_elem_value *ucontrol)
2011{
2012 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2013 struct via_spec *spec = codec->spec;
2014 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2015 hda_nid_t mux;
2016 int cur;
2017
2018 cur = ucontrol->value.enumerated.item[0];
2019 if (cur < 0 || cur >= spec->num_inputs)
2020 return -EINVAL;
2021 if (spec->cur_mux[idx] == cur)
2022 return 0;
2023 spec->cur_mux[idx] = cur;
2024 if (spec->dyn_adc_switch) {
2025 int adc_idx = spec->inputs[cur].adc_idx;
2026 mux = spec->mux_nids[adc_idx];
2027 via_dyn_adc_pcm_resetup(codec, cur);
2028 } else {
2029 mux = spec->mux_nids[idx];
2030 if (snd_BUG_ON(!mux))
2031 return -EINVAL;
2032 }
2033
2034 if (mux) {
2035 /* switch to D0 beofre change index */
2036 if (snd_hda_codec_read(codec, mux, 0,
2037 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2038 snd_hda_codec_write(codec, mux, 0,
2039 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2040 snd_hda_codec_write(codec, mux, 0,
2041 AC_VERB_SET_CONNECT_SEL,
2042 spec->inputs[cur].mux_idx);
2043 }
2044
2045 /* update jack power state */
2046 set_widgets_power_state(codec);
2047 return 0;
2048}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002049
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002050static const struct snd_kcontrol_new via_input_src_ctl = {
2051 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2052 /* The multiple "Capture Source" controls confuse alsamixer
2053 * So call somewhat different..
2054 */
2055 /* .name = "Capture Source", */
2056 .name = "Input Source",
2057 .info = via_mux_enum_info,
2058 .get = via_mux_enum_get,
2059 .put = via_mux_enum_put,
2060};
2061
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002062static int create_input_src_ctls(struct hda_codec *codec, int count)
2063{
2064 struct via_spec *spec = codec->spec;
2065 struct snd_kcontrol_new *knew;
2066
2067 if (spec->num_inputs <= 1 || !count)
2068 return 0; /* no need for single src */
2069
2070 knew = via_clone_control(spec, &via_input_src_ctl);
2071 if (!knew)
2072 return -ENOMEM;
2073 knew->count = count;
2074 return 0;
2075}
2076
2077/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002078static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2079{
2080 struct hda_amp_list *list;
2081
2082 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2083 return;
2084 list = spec->loopback_list + spec->num_loopbacks;
2085 list->nid = mix;
2086 list->dir = HDA_INPUT;
2087 list->idx = idx;
2088 spec->num_loopbacks++;
2089 spec->loopback.amplist = spec->loopback_list;
2090}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002091
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002092static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002093 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002094{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002095 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002096}
2097
2098/* add the input-route to the given pin */
2099static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002100{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002101 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002102 int c, idx;
2103
2104 spec->inputs[spec->num_inputs].adc_idx = -1;
2105 spec->inputs[spec->num_inputs].pin = pin;
2106 for (c = 0; c < spec->num_adc_nids; c++) {
2107 if (spec->mux_nids[c]) {
2108 idx = get_connection_index(codec, spec->mux_nids[c],
2109 pin);
2110 if (idx < 0)
2111 continue;
2112 spec->inputs[spec->num_inputs].mux_idx = idx;
2113 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002114 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002115 continue;
2116 }
2117 spec->inputs[spec->num_inputs].adc_idx = c;
2118 /* Can primary ADC satisfy all inputs? */
2119 if (!spec->dyn_adc_switch &&
2120 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2121 snd_printd(KERN_INFO
2122 "via: dynamic ADC switching enabled\n");
2123 spec->dyn_adc_switch = 1;
2124 }
2125 return true;
2126 }
2127 return false;
2128}
2129
2130static int get_mux_nids(struct hda_codec *codec);
2131
2132/* parse input-routes; fill ADCs, MUXs and input-src entries */
2133static int parse_analog_inputs(struct hda_codec *codec)
2134{
2135 struct via_spec *spec = codec->spec;
2136 const struct auto_pin_cfg *cfg = &spec->autocfg;
2137 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002138
2139 err = via_fill_adcs(codec);
2140 if (err < 0)
2141 return err;
2142 err = get_mux_nids(codec);
2143 if (err < 0)
2144 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002145
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002146 /* fill all input-routes */
2147 for (i = 0; i < cfg->num_inputs; i++) {
2148 if (add_input_route(codec, cfg->inputs[i].pin))
2149 spec->inputs[spec->num_inputs++].label =
2150 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002151 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002152
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002153 /* check for internal loopback recording */
2154 if (spec->aa_mix_nid &&
2155 add_input_route(codec, spec->aa_mix_nid))
2156 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2157
2158 return 0;
2159}
2160
2161/* create analog-loopback volume/switch controls */
2162static int create_loopback_ctls(struct hda_codec *codec)
2163{
2164 struct via_spec *spec = codec->spec;
2165 const struct auto_pin_cfg *cfg = &spec->autocfg;
2166 const char *prev_label = NULL;
2167 int type_idx = 0;
2168 int i, j, err, idx;
2169
2170 if (!spec->aa_mix_nid)
2171 return 0;
2172
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002173 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002174 hda_nid_t pin = cfg->inputs[i].pin;
2175 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2176
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002177 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002178 type_idx++;
2179 else
2180 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002181 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002182 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2183 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002184 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002185 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002186 if (err < 0)
2187 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002188 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002189 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002190
2191 /* remember the label for smart51 control */
2192 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002193 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002194 spec->smart51_idxs[j] = idx;
2195 spec->smart51_labels[j] = label;
2196 break;
2197 }
2198 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002199 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002200 return 0;
2201}
2202
2203/* create mic-boost controls (if present) */
2204static int create_mic_boost_ctls(struct hda_codec *codec)
2205{
2206 struct via_spec *spec = codec->spec;
2207 const struct auto_pin_cfg *cfg = &spec->autocfg;
2208 int i, err;
2209
2210 for (i = 0; i < cfg->num_inputs; i++) {
2211 hda_nid_t pin = cfg->inputs[i].pin;
2212 unsigned int caps;
2213 const char *label;
2214 char name[32];
2215
2216 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2217 continue;
2218 caps = query_amp_caps(codec, pin, HDA_INPUT);
2219 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2220 continue;
2221 label = hda_get_autocfg_input_label(codec, cfg, i);
2222 snprintf(name, sizeof(name), "%s Boost Volume", label);
2223 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2224 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2225 if (err < 0)
2226 return err;
2227 }
2228 return 0;
2229}
2230
2231/* create capture and input-src controls for multiple streams */
2232static int create_multi_adc_ctls(struct hda_codec *codec)
2233{
2234 struct via_spec *spec = codec->spec;
2235 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002236
2237 /* create capture mixer elements */
2238 for (i = 0; i < spec->num_adc_nids; i++) {
2239 hda_nid_t adc = spec->adc_nids[i];
2240 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2241 "Capture Volume", i,
2242 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2243 HDA_INPUT));
2244 if (err < 0)
2245 return err;
2246 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2247 "Capture Switch", i,
2248 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2249 HDA_INPUT));
2250 if (err < 0)
2251 return err;
2252 }
2253
2254 /* input-source control */
2255 for (i = 0; i < spec->num_adc_nids; i++)
2256 if (!spec->mux_nids[i])
2257 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002258 err = create_input_src_ctls(codec, i);
2259 if (err < 0)
2260 return err;
2261 return 0;
2262}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002263
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002264/* bind capture volume/switch */
2265static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2266 HDA_BIND_VOL("Capture Volume", 0);
2267static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2268 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002269
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002270static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2271 struct hda_ctl_ops *ops)
2272{
2273 struct hda_bind_ctls *ctl;
2274 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002275
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002276 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2277 if (!ctl)
2278 return -ENOMEM;
2279 ctl->ops = ops;
2280 for (i = 0; i < spec->num_adc_nids; i++)
2281 ctl->values[i] =
2282 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2283 *ctl_ret = ctl;
2284 return 0;
2285}
2286
2287/* create capture and input-src controls for dynamic ADC-switch case */
2288static int create_dyn_adc_ctls(struct hda_codec *codec)
2289{
2290 struct via_spec *spec = codec->spec;
2291 struct snd_kcontrol_new *knew;
2292 int err;
2293
2294 /* set up the bind capture ctls */
2295 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2296 if (err < 0)
2297 return err;
2298 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2299 if (err < 0)
2300 return err;
2301
2302 /* create capture mixer elements */
2303 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2304 if (!knew)
2305 return -ENOMEM;
2306 knew->private_value = (long)spec->bind_cap_vol;
2307
2308 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2309 if (!knew)
2310 return -ENOMEM;
2311 knew->private_value = (long)spec->bind_cap_sw;
2312
2313 /* input-source control */
2314 err = create_input_src_ctls(codec, 1);
2315 if (err < 0)
2316 return err;
2317 return 0;
2318}
2319
2320/* parse and create capture-related stuff */
2321static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2322{
2323 struct via_spec *spec = codec->spec;
2324 int err;
2325
2326 err = parse_analog_inputs(codec);
2327 if (err < 0)
2328 return err;
2329 if (spec->dyn_adc_switch)
2330 err = create_dyn_adc_ctls(codec);
2331 else
2332 err = create_multi_adc_ctls(codec);
2333 if (err < 0)
2334 return err;
2335 err = create_loopback_ctls(codec);
2336 if (err < 0)
2337 return err;
2338 err = create_mic_boost_ctls(codec);
2339 if (err < 0)
2340 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002341 return 0;
2342}
2343
Harald Welte76d9b0d2008-09-09 15:50:37 +08002344static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2345{
2346 unsigned int def_conf;
2347 unsigned char seqassoc;
2348
Takashi Iwai2f334f92009-02-20 14:37:42 +01002349 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002350 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2351 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002352 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2353 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2354 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2355 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002356 }
2357
2358 return;
2359}
2360
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002361static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002362 struct snd_ctl_elem_value *ucontrol)
2363{
2364 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2365 struct via_spec *spec = codec->spec;
2366
2367 if (spec->codec_type != VT1708)
2368 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002369 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002370 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002371 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002372 return 0;
2373}
2374
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002375static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002376 struct snd_ctl_elem_value *ucontrol)
2377{
2378 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2379 struct via_spec *spec = codec->spec;
2380 int change;
2381
2382 if (spec->codec_type != VT1708)
2383 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002384 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002385 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002386 == !spec->vt1708_jack_detect;
2387 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002388 mute_aa_path(codec, 1);
2389 notify_aa_path_ctls(codec);
2390 }
2391 return change;
2392}
2393
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002394static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2395 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2396 .name = "Jack Detect",
2397 .count = 1,
2398 .info = snd_ctl_boolean_mono_info,
2399 .get = vt1708_jack_detect_get,
2400 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002401};
2402
Takashi Iwai12daef62011-06-18 17:45:49 +02002403static void fill_dig_outs(struct hda_codec *codec);
2404static void fill_dig_in(struct hda_codec *codec);
2405
2406static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002407{
2408 struct via_spec *spec = codec->spec;
2409 int err;
2410
2411 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2412 if (err < 0)
2413 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002414 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002415 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002416
Takashi Iwai4a796162011-06-17 17:53:38 +02002417 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002418 if (err < 0)
2419 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002420 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002421 if (err < 0)
2422 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002423 err = via_auto_create_speaker_ctls(codec);
2424 if (err < 0)
2425 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002426 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002427 if (err < 0)
2428 return err;
2429
2430 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2431
Takashi Iwai12daef62011-06-18 17:45:49 +02002432 fill_dig_outs(codec);
2433 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002434
Takashi Iwai603c4012008-07-30 15:01:44 +02002435 if (spec->kctls.list)
2436 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002437
Joseph Chanc577b8a2006-11-29 15:29:40 +01002438
Takashi Iwai8df2a312011-06-21 11:48:29 +02002439 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002440 err = via_hp_build(codec);
2441 if (err < 0)
2442 return err;
2443 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002444
Takashi Iwaif4a78282011-06-17 18:46:48 +02002445 err = via_smart51_build(codec);
2446 if (err < 0)
2447 return err;
2448
Takashi Iwai5d417622011-06-20 11:32:27 +02002449 /* assign slave outs */
2450 if (spec->slave_dig_outs[0])
2451 codec->slave_dig_outs = spec->slave_dig_outs;
2452
Joseph Chanc577b8a2006-11-29 15:29:40 +01002453 return 1;
2454}
2455
Takashi Iwai5d417622011-06-20 11:32:27 +02002456static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002457{
Lydia Wang25eaba22009-10-10 19:08:43 +08002458 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002459 if (spec->multiout.dig_out_nid)
2460 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2461 if (spec->slave_dig_outs[0])
2462 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2463}
Lydia Wang25eaba22009-10-10 19:08:43 +08002464
Takashi Iwai5d417622011-06-20 11:32:27 +02002465static void via_auto_init_dig_in(struct hda_codec *codec)
2466{
2467 struct via_spec *spec = codec->spec;
2468 if (!spec->dig_in_nid)
2469 return;
2470 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2471 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2472}
2473
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002474/* initialize the unsolicited events */
2475static void via_auto_init_unsol_event(struct hda_codec *codec)
2476{
2477 struct via_spec *spec = codec->spec;
2478 struct auto_pin_cfg *cfg = &spec->autocfg;
2479 unsigned int ev;
2480 int i;
2481
2482 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2483 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2484 AC_VERB_SET_UNSOLICITED_ENABLE,
2485 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2486
2487 if (cfg->speaker_pins[0])
2488 ev = VIA_LINE_EVENT;
2489 else
2490 ev = 0;
2491 for (i = 0; i < cfg->line_outs; i++) {
2492 if (cfg->line_out_pins[i] &&
2493 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002494 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002495 AC_VERB_SET_UNSOLICITED_ENABLE,
2496 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2497 }
2498
2499 for (i = 0; i < cfg->num_inputs; i++) {
2500 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2501 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2502 AC_VERB_SET_UNSOLICITED_ENABLE,
2503 AC_USRSP_EN | VIA_JACK_EVENT);
2504 }
2505}
2506
Takashi Iwai5d417622011-06-20 11:32:27 +02002507static int via_init(struct hda_codec *codec)
2508{
2509 struct via_spec *spec = codec->spec;
2510 int i;
2511
2512 for (i = 0; i < spec->num_iverbs; i++)
2513 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2514
Joseph Chanc577b8a2006-11-29 15:29:40 +01002515 via_auto_init_multi_out(codec);
2516 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002517 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002518 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002519 via_auto_init_dig_outs(codec);
2520 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002521
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002522 via_auto_init_unsol_event(codec);
2523
2524 via_hp_automute(codec);
2525 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002526
Joseph Chanc577b8a2006-11-29 15:29:40 +01002527 return 0;
2528}
2529
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002530static void vt1708_update_hp_jack_state(struct work_struct *work)
2531{
2532 struct via_spec *spec = container_of(work, struct via_spec,
2533 vt1708_hp_work.work);
2534 if (spec->codec_type != VT1708)
2535 return;
2536 /* if jack state toggled */
2537 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002538 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002539 spec->vt1708_hp_present ^= 1;
2540 via_hp_automute(spec->codec);
2541 }
2542 vt1708_start_hp_work(spec);
2543}
2544
Takashi Iwai337b9d02009-07-07 18:18:59 +02002545static int get_mux_nids(struct hda_codec *codec)
2546{
2547 struct via_spec *spec = codec->spec;
2548 hda_nid_t nid, conn[8];
2549 unsigned int type;
2550 int i, n;
2551
2552 for (i = 0; i < spec->num_adc_nids; i++) {
2553 nid = spec->adc_nids[i];
2554 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002555 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002556 if (type == AC_WID_PIN)
2557 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002558 n = snd_hda_get_connections(codec, nid, conn,
2559 ARRAY_SIZE(conn));
2560 if (n <= 0)
2561 break;
2562 if (n > 1) {
2563 spec->mux_nids[i] = nid;
2564 break;
2565 }
2566 nid = conn[0];
2567 }
2568 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002569 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002570}
2571
Joseph Chanc577b8a2006-11-29 15:29:40 +01002572static int patch_vt1708(struct hda_codec *codec)
2573{
2574 struct via_spec *spec;
2575 int err;
2576
2577 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002578 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002579 if (spec == NULL)
2580 return -ENOMEM;
2581
Takashi Iwai620e2b22011-06-17 17:19:19 +02002582 spec->aa_mix_nid = 0x17;
2583
Takashi Iwai12daef62011-06-18 17:45:49 +02002584 /* Add HP and CD pin config connect bit re-config action */
2585 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2586 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2587
Joseph Chanc577b8a2006-11-29 15:29:40 +01002588 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002589 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002590 if (err < 0) {
2591 via_free(codec);
2592 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002593 }
2594
Takashi Iwai12daef62011-06-18 17:45:49 +02002595 /* add jack detect on/off control */
2596 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2597 return -ENOMEM;
2598
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002599 /* disable 32bit format on VT1708 */
2600 if (codec->vendor_id == 0x11061708)
2601 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002602
Lydia Wange322a362011-06-29 13:52:02 +08002603 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2604
Joseph Chanc577b8a2006-11-29 15:29:40 +01002605 codec->patch_ops = via_patch_ops;
2606
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002607 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002608 return 0;
2609}
2610
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002611static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002612{
2613 struct via_spec *spec;
2614 int err;
2615
2616 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002617 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002618 if (spec == NULL)
2619 return -ENOMEM;
2620
Takashi Iwai620e2b22011-06-17 17:19:19 +02002621 spec->aa_mix_nid = 0x18;
2622
Takashi Iwai12daef62011-06-18 17:45:49 +02002623 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002624 if (err < 0) {
2625 via_free(codec);
2626 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002627 }
2628
Joseph Chanc577b8a2006-11-29 15:29:40 +01002629 codec->patch_ops = via_patch_ops;
2630
Josepch Chanf7278fd2007-12-13 16:40:40 +01002631 return 0;
2632}
2633
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002634static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2635{
2636 struct via_spec *spec = codec->spec;
2637 int imux_is_smixer;
2638 unsigned int parm;
2639 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002640 if ((spec->codec_type != VT1708B_4CH) &&
2641 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002642 is_8ch = 1;
2643
2644 /* SW0 (17h) = stereo mixer */
2645 imux_is_smixer =
2646 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2647 == ((spec->codec_type == VT1708S) ? 5 : 0));
2648 /* inputs */
2649 /* PW 1/2/5 (1ah/1bh/1eh) */
2650 parm = AC_PWRST_D3;
2651 set_pin_power_state(codec, 0x1a, &parm);
2652 set_pin_power_state(codec, 0x1b, &parm);
2653 set_pin_power_state(codec, 0x1e, &parm);
2654 if (imux_is_smixer)
2655 parm = AC_PWRST_D0;
2656 /* SW0 (17h), AIW 0/1 (13h/14h) */
2657 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2658 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2659 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2660
2661 /* outputs */
2662 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2663 parm = AC_PWRST_D3;
2664 set_pin_power_state(codec, 0x19, &parm);
2665 if (spec->smart51_enabled)
2666 set_pin_power_state(codec, 0x1b, &parm);
2667 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2668 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2669
2670 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2671 if (is_8ch) {
2672 parm = AC_PWRST_D3;
2673 set_pin_power_state(codec, 0x22, &parm);
2674 if (spec->smart51_enabled)
2675 set_pin_power_state(codec, 0x1a, &parm);
2676 snd_hda_codec_write(codec, 0x26, 0,
2677 AC_VERB_SET_POWER_STATE, parm);
2678 snd_hda_codec_write(codec, 0x24, 0,
2679 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002680 } else if (codec->vendor_id == 0x11064397) {
2681 /* PW7(23h), SW2(27h), AOW2(25h) */
2682 parm = AC_PWRST_D3;
2683 set_pin_power_state(codec, 0x23, &parm);
2684 if (spec->smart51_enabled)
2685 set_pin_power_state(codec, 0x1a, &parm);
2686 snd_hda_codec_write(codec, 0x27, 0,
2687 AC_VERB_SET_POWER_STATE, parm);
2688 snd_hda_codec_write(codec, 0x25, 0,
2689 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002690 }
2691
2692 /* PW 3/4/7 (1ch/1dh/23h) */
2693 parm = AC_PWRST_D3;
2694 /* force to D0 for internal Speaker */
2695 set_pin_power_state(codec, 0x1c, &parm);
2696 set_pin_power_state(codec, 0x1d, &parm);
2697 if (is_8ch)
2698 set_pin_power_state(codec, 0x23, &parm);
2699
2700 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2701 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2702 imux_is_smixer ? AC_PWRST_D0 : parm);
2703 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2704 if (is_8ch) {
2705 snd_hda_codec_write(codec, 0x25, 0,
2706 AC_VERB_SET_POWER_STATE, parm);
2707 snd_hda_codec_write(codec, 0x27, 0,
2708 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002709 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2710 snd_hda_codec_write(codec, 0x25, 0,
2711 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002712}
2713
Lydia Wang518bf3b2009-10-10 19:07:29 +08002714static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002715static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002716{
2717 struct via_spec *spec;
2718 int err;
2719
Lydia Wang518bf3b2009-10-10 19:07:29 +08002720 if (get_codec_type(codec) == VT1708BCE)
2721 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002722
Josepch Chanf7278fd2007-12-13 16:40:40 +01002723 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002724 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002725 if (spec == NULL)
2726 return -ENOMEM;
2727
Takashi Iwai620e2b22011-06-17 17:19:19 +02002728 spec->aa_mix_nid = 0x16;
2729
Josepch Chanf7278fd2007-12-13 16:40:40 +01002730 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002731 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002732 if (err < 0) {
2733 via_free(codec);
2734 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002735 }
2736
Josepch Chanf7278fd2007-12-13 16:40:40 +01002737 codec->patch_ops = via_patch_ops;
2738
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002739 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2740
Josepch Chanf7278fd2007-12-13 16:40:40 +01002741 return 0;
2742}
2743
Harald Welted949cac2008-09-09 15:56:01 +08002744/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002745static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002746 /* Enable Mic Boost Volume backdoor */
2747 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002748 /* don't bybass mixer */
2749 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002750 { }
2751};
2752
Takashi Iwai9da29272009-05-07 16:31:14 +02002753/* fill out digital output widgets; one for master and one for slave outputs */
2754static void fill_dig_outs(struct hda_codec *codec)
2755{
2756 struct via_spec *spec = codec->spec;
2757 int i;
2758
2759 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2760 hda_nid_t nid;
2761 int conn;
2762
2763 nid = spec->autocfg.dig_out_pins[i];
2764 if (!nid)
2765 continue;
2766 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2767 if (conn < 1)
2768 continue;
2769 if (!spec->multiout.dig_out_nid)
2770 spec->multiout.dig_out_nid = nid;
2771 else {
2772 spec->slave_dig_outs[0] = nid;
2773 break; /* at most two dig outs */
2774 }
2775 }
2776}
2777
Takashi Iwai12daef62011-06-18 17:45:49 +02002778static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002779{
2780 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002781 hda_nid_t dig_nid;
2782 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002783
Takashi Iwai12daef62011-06-18 17:45:49 +02002784 if (!spec->autocfg.dig_in_pin)
2785 return;
Harald Welted949cac2008-09-09 15:56:01 +08002786
Takashi Iwai12daef62011-06-18 17:45:49 +02002787 dig_nid = codec->start_nid;
2788 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2789 unsigned int wcaps = get_wcaps(codec, dig_nid);
2790 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2791 continue;
2792 if (!(wcaps & AC_WCAP_DIGITAL))
2793 continue;
2794 if (!(wcaps & AC_WCAP_CONN_LIST))
2795 continue;
2796 err = get_connection_index(codec, dig_nid,
2797 spec->autocfg.dig_in_pin);
2798 if (err >= 0) {
2799 spec->dig_in_nid = dig_nid;
2800 break;
2801 }
2802 }
Harald Welted949cac2008-09-09 15:56:01 +08002803}
2804
Lydia Wang6369bcf2009-10-10 19:08:31 +08002805static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2806 int offset, int num_steps, int step_size)
2807{
2808 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2809 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2810 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2811 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2812 (0 << AC_AMPCAP_MUTE_SHIFT));
2813}
2814
Harald Welted949cac2008-09-09 15:56:01 +08002815static int patch_vt1708S(struct hda_codec *codec)
2816{
2817 struct via_spec *spec;
2818 int err;
2819
2820 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002821 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002822 if (spec == NULL)
2823 return -ENOMEM;
2824
Takashi Iwai620e2b22011-06-17 17:19:19 +02002825 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002826 override_mic_boost(codec, 0x1a, 0, 3, 40);
2827 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002828
Harald Welted949cac2008-09-09 15:56:01 +08002829 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002830 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002831 if (err < 0) {
2832 via_free(codec);
2833 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002834 }
2835
Takashi Iwai096a8852011-06-20 12:09:02 +02002836 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002837
Harald Welted949cac2008-09-09 15:56:01 +08002838 codec->patch_ops = via_patch_ops;
2839
Lydia Wang518bf3b2009-10-10 19:07:29 +08002840 /* correct names for VT1708BCE */
2841 if (get_codec_type(codec) == VT1708BCE) {
2842 kfree(codec->chip_name);
2843 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2844 snprintf(codec->bus->card->mixername,
2845 sizeof(codec->bus->card->mixername),
2846 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002847 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002848 /* correct names for VT1705 */
2849 if (codec->vendor_id == 0x11064397) {
2850 kfree(codec->chip_name);
2851 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2852 snprintf(codec->bus->card->mixername,
2853 sizeof(codec->bus->card->mixername),
2854 "%s %s", codec->vendor_name, codec->chip_name);
2855 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002856 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002857 return 0;
2858}
2859
2860/* Patch for VT1702 */
2861
Takashi Iwai096a8852011-06-20 12:09:02 +02002862static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002863 /* mixer enable */
2864 {0x1, 0xF88, 0x3},
2865 /* GPIO 0~2 */
2866 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002867 { }
2868};
2869
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002870static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2871{
2872 int imux_is_smixer =
2873 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2874 unsigned int parm;
2875 /* inputs */
2876 /* PW 1/2/5 (14h/15h/18h) */
2877 parm = AC_PWRST_D3;
2878 set_pin_power_state(codec, 0x14, &parm);
2879 set_pin_power_state(codec, 0x15, &parm);
2880 set_pin_power_state(codec, 0x18, &parm);
2881 if (imux_is_smixer)
2882 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2883 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2884 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2885 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2886 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2887 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2888
2889 /* outputs */
2890 /* PW 3/4 (16h/17h) */
2891 parm = AC_PWRST_D3;
2892 set_pin_power_state(codec, 0x17, &parm);
2893 set_pin_power_state(codec, 0x16, &parm);
2894 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2895 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2896 imux_is_smixer ? AC_PWRST_D0 : parm);
2897 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2898 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2899}
2900
Harald Welted949cac2008-09-09 15:56:01 +08002901static int patch_vt1702(struct hda_codec *codec)
2902{
2903 struct via_spec *spec;
2904 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002905
2906 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002907 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002908 if (spec == NULL)
2909 return -ENOMEM;
2910
Takashi Iwai620e2b22011-06-17 17:19:19 +02002911 spec->aa_mix_nid = 0x1a;
2912
Takashi Iwai12daef62011-06-18 17:45:49 +02002913 /* limit AA path volume to 0 dB */
2914 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2915 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2916 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2917 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2918 (1 << AC_AMPCAP_MUTE_SHIFT));
2919
Harald Welted949cac2008-09-09 15:56:01 +08002920 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002921 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002922 if (err < 0) {
2923 via_free(codec);
2924 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002925 }
2926
Takashi Iwai096a8852011-06-20 12:09:02 +02002927 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002928
Harald Welted949cac2008-09-09 15:56:01 +08002929 codec->patch_ops = via_patch_ops;
2930
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002931 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002932 return 0;
2933}
2934
Lydia Wangeb7188c2009-10-10 19:08:34 +08002935/* Patch for VT1718S */
2936
Takashi Iwai096a8852011-06-20 12:09:02 +02002937static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002938 /* Enable MW0 adjust Gain 5 */
2939 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002940 /* Enable Boost Volume backdoor */
2941 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002942
Lydia Wangeb7188c2009-10-10 19:08:34 +08002943 { }
2944};
2945
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002946static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2947{
2948 struct via_spec *spec = codec->spec;
2949 int imux_is_smixer;
2950 unsigned int parm;
2951 /* MUX6 (1eh) = stereo mixer */
2952 imux_is_smixer =
2953 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2954 /* inputs */
2955 /* PW 5/6/7 (29h/2ah/2bh) */
2956 parm = AC_PWRST_D3;
2957 set_pin_power_state(codec, 0x29, &parm);
2958 set_pin_power_state(codec, 0x2a, &parm);
2959 set_pin_power_state(codec, 0x2b, &parm);
2960 if (imux_is_smixer)
2961 parm = AC_PWRST_D0;
2962 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2963 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2964 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2965 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2966 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2967
2968 /* outputs */
2969 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2970 parm = AC_PWRST_D3;
2971 set_pin_power_state(codec, 0x27, &parm);
2972 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2973 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2974
2975 /* PW2 (26h), AOW2 (ah) */
2976 parm = AC_PWRST_D3;
2977 set_pin_power_state(codec, 0x26, &parm);
2978 if (spec->smart51_enabled)
2979 set_pin_power_state(codec, 0x2b, &parm);
2980 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2981
2982 /* PW0 (24h), AOW0 (8h) */
2983 parm = AC_PWRST_D3;
2984 set_pin_power_state(codec, 0x24, &parm);
2985 if (!spec->hp_independent_mode) /* check for redirected HP */
2986 set_pin_power_state(codec, 0x28, &parm);
2987 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2988 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2989 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2990 imux_is_smixer ? AC_PWRST_D0 : parm);
2991
2992 /* PW1 (25h), AOW1 (9h) */
2993 parm = AC_PWRST_D3;
2994 set_pin_power_state(codec, 0x25, &parm);
2995 if (spec->smart51_enabled)
2996 set_pin_power_state(codec, 0x2a, &parm);
2997 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2998
2999 if (spec->hp_independent_mode) {
3000 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3001 parm = AC_PWRST_D3;
3002 set_pin_power_state(codec, 0x28, &parm);
3003 snd_hda_codec_write(codec, 0x1b, 0,
3004 AC_VERB_SET_POWER_STATE, parm);
3005 snd_hda_codec_write(codec, 0x34, 0,
3006 AC_VERB_SET_POWER_STATE, parm);
3007 snd_hda_codec_write(codec, 0xc, 0,
3008 AC_VERB_SET_POWER_STATE, parm);
3009 }
3010}
3011
Lydia Wangeb7188c2009-10-10 19:08:34 +08003012static int patch_vt1718S(struct hda_codec *codec)
3013{
3014 struct via_spec *spec;
3015 int err;
3016
3017 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003018 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003019 if (spec == NULL)
3020 return -ENOMEM;
3021
Takashi Iwai620e2b22011-06-17 17:19:19 +02003022 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003023 override_mic_boost(codec, 0x2b, 0, 3, 40);
3024 override_mic_boost(codec, 0x29, 0, 3, 40);
Lydia Wangc4394f52011-07-04 16:54:15 +08003025 spec->dac_mixer_idx = 5;
Takashi Iwai620e2b22011-06-17 17:19:19 +02003026
Lydia Wangeb7188c2009-10-10 19:08:34 +08003027 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003028 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003029 if (err < 0) {
3030 via_free(codec);
3031 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003032 }
3033
Takashi Iwai096a8852011-06-20 12:09:02 +02003034 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003035
Lydia Wangeb7188c2009-10-10 19:08:34 +08003036 codec->patch_ops = via_patch_ops;
3037
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003038 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3039
Lydia Wangeb7188c2009-10-10 19:08:34 +08003040 return 0;
3041}
Lydia Wangf3db4232009-10-10 19:08:41 +08003042
3043/* Patch for VT1716S */
3044
3045static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3046 struct snd_ctl_elem_info *uinfo)
3047{
3048 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3049 uinfo->count = 1;
3050 uinfo->value.integer.min = 0;
3051 uinfo->value.integer.max = 1;
3052 return 0;
3053}
3054
3055static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3056 struct snd_ctl_elem_value *ucontrol)
3057{
3058 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3059 int index = 0;
3060
3061 index = snd_hda_codec_read(codec, 0x26, 0,
3062 AC_VERB_GET_CONNECT_SEL, 0);
3063 if (index != -1)
3064 *ucontrol->value.integer.value = index;
3065
3066 return 0;
3067}
3068
3069static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3070 struct snd_ctl_elem_value *ucontrol)
3071{
3072 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3073 struct via_spec *spec = codec->spec;
3074 int index = *ucontrol->value.integer.value;
3075
3076 snd_hda_codec_write(codec, 0x26, 0,
3077 AC_VERB_SET_CONNECT_SEL, index);
3078 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003079 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003080 return 1;
3081}
3082
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003083static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003084 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3085 {
3086 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3087 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003088 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003089 .count = 1,
3090 .info = vt1716s_dmic_info,
3091 .get = vt1716s_dmic_get,
3092 .put = vt1716s_dmic_put,
3093 },
3094 {} /* end */
3095};
3096
3097
3098/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003099static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003100 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3101 { } /* end */
3102};
3103
Takashi Iwai096a8852011-06-20 12:09:02 +02003104static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003105 /* Enable Boost Volume backdoor */
3106 {0x1, 0xf8a, 0x80},
3107 /* don't bybass mixer */
3108 {0x1, 0xf88, 0xc0},
3109 /* Enable mono output */
3110 {0x1, 0xf90, 0x08},
3111 { }
3112};
3113
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003114static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3115{
3116 struct via_spec *spec = codec->spec;
3117 int imux_is_smixer;
3118 unsigned int parm;
3119 unsigned int mono_out, present;
3120 /* SW0 (17h) = stereo mixer */
3121 imux_is_smixer =
3122 (snd_hda_codec_read(codec, 0x17, 0,
3123 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3124 /* inputs */
3125 /* PW 1/2/5 (1ah/1bh/1eh) */
3126 parm = AC_PWRST_D3;
3127 set_pin_power_state(codec, 0x1a, &parm);
3128 set_pin_power_state(codec, 0x1b, &parm);
3129 set_pin_power_state(codec, 0x1e, &parm);
3130 if (imux_is_smixer)
3131 parm = AC_PWRST_D0;
3132 /* SW0 (17h), AIW0(13h) */
3133 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3134 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3135
3136 parm = AC_PWRST_D3;
3137 set_pin_power_state(codec, 0x1e, &parm);
3138 /* PW11 (22h) */
3139 if (spec->dmic_enabled)
3140 set_pin_power_state(codec, 0x22, &parm);
3141 else
3142 snd_hda_codec_write(codec, 0x22, 0,
3143 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3144
3145 /* SW2(26h), AIW1(14h) */
3146 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3147 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3148
3149 /* outputs */
3150 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3151 parm = AC_PWRST_D3;
3152 set_pin_power_state(codec, 0x19, &parm);
3153 /* Smart 5.1 PW2(1bh) */
3154 if (spec->smart51_enabled)
3155 set_pin_power_state(codec, 0x1b, &parm);
3156 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3157 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3158
3159 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3160 parm = AC_PWRST_D3;
3161 set_pin_power_state(codec, 0x23, &parm);
3162 /* Smart 5.1 PW1(1ah) */
3163 if (spec->smart51_enabled)
3164 set_pin_power_state(codec, 0x1a, &parm);
3165 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3166
3167 /* Smart 5.1 PW5(1eh) */
3168 if (spec->smart51_enabled)
3169 set_pin_power_state(codec, 0x1e, &parm);
3170 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3171
3172 /* Mono out */
3173 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3174 present = snd_hda_jack_detect(codec, 0x1c);
3175
3176 if (present)
3177 mono_out = 0;
3178 else {
3179 present = snd_hda_jack_detect(codec, 0x1d);
3180 if (!spec->hp_independent_mode && present)
3181 mono_out = 0;
3182 else
3183 mono_out = 1;
3184 }
3185 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3186 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3187 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3188 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3189
3190 /* PW 3/4 (1ch/1dh) */
3191 parm = AC_PWRST_D3;
3192 set_pin_power_state(codec, 0x1c, &parm);
3193 set_pin_power_state(codec, 0x1d, &parm);
3194 /* HP Independent Mode, power on AOW3 */
3195 if (spec->hp_independent_mode)
3196 snd_hda_codec_write(codec, 0x25, 0,
3197 AC_VERB_SET_POWER_STATE, parm);
3198
3199 /* force to D0 for internal Speaker */
3200 /* MW0 (16h), AOW0 (10h) */
3201 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3202 imux_is_smixer ? AC_PWRST_D0 : parm);
3203 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3204 mono_out ? AC_PWRST_D0 : parm);
3205}
3206
Lydia Wangf3db4232009-10-10 19:08:41 +08003207static int patch_vt1716S(struct hda_codec *codec)
3208{
3209 struct via_spec *spec;
3210 int err;
3211
3212 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003213 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003214 if (spec == NULL)
3215 return -ENOMEM;
3216
Takashi Iwai620e2b22011-06-17 17:19:19 +02003217 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003218 override_mic_boost(codec, 0x1a, 0, 3, 40);
3219 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003220
Lydia Wangf3db4232009-10-10 19:08:41 +08003221 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003222 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003223 if (err < 0) {
3224 via_free(codec);
3225 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003226 }
3227
Takashi Iwai096a8852011-06-20 12:09:02 +02003228 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003229
Lydia Wangf3db4232009-10-10 19:08:41 +08003230 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3231 spec->num_mixers++;
3232
3233 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3234
3235 codec->patch_ops = via_patch_ops;
3236
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003237 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003238 return 0;
3239}
Lydia Wang25eaba22009-10-10 19:08:43 +08003240
3241/* for vt2002P */
3242
Takashi Iwai096a8852011-06-20 12:09:02 +02003243static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003244 /* Class-D speaker related verbs */
3245 {0x1, 0xfe0, 0x4},
3246 {0x1, 0xfe9, 0x80},
3247 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003248 /* Enable Boost Volume backdoor */
3249 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003250 /* Enable AOW0 to MW9 */
3251 {0x1, 0xfb8, 0x88},
3252 { }
3253};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003254
Takashi Iwai096a8852011-06-20 12:09:02 +02003255static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003256 /* Enable Boost Volume backdoor */
3257 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003258 /* Enable AOW0 to MW9 */
3259 {0x1, 0xfb8, 0x88},
3260 { }
3261};
Lydia Wang25eaba22009-10-10 19:08:43 +08003262
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003263static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3264{
3265 struct via_spec *spec = codec->spec;
3266 int imux_is_smixer;
3267 unsigned int parm;
3268 unsigned int present;
3269 /* MUX9 (1eh) = stereo mixer */
3270 imux_is_smixer =
3271 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3272 /* inputs */
3273 /* PW 5/6/7 (29h/2ah/2bh) */
3274 parm = AC_PWRST_D3;
3275 set_pin_power_state(codec, 0x29, &parm);
3276 set_pin_power_state(codec, 0x2a, &parm);
3277 set_pin_power_state(codec, 0x2b, &parm);
3278 parm = AC_PWRST_D0;
3279 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3280 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3281 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3282 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3283 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3284
3285 /* outputs */
3286 /* AOW0 (8h)*/
3287 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3288
Lydia Wang118909562011-03-23 17:57:34 +08003289 if (spec->codec_type == VT1802) {
3290 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3291 parm = AC_PWRST_D3;
3292 set_pin_power_state(codec, 0x28, &parm);
3293 snd_hda_codec_write(codec, 0x18, 0,
3294 AC_VERB_SET_POWER_STATE, parm);
3295 snd_hda_codec_write(codec, 0x38, 0,
3296 AC_VERB_SET_POWER_STATE, parm);
3297 } else {
3298 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3299 parm = AC_PWRST_D3;
3300 set_pin_power_state(codec, 0x26, &parm);
3301 snd_hda_codec_write(codec, 0x1c, 0,
3302 AC_VERB_SET_POWER_STATE, parm);
3303 snd_hda_codec_write(codec, 0x37, 0,
3304 AC_VERB_SET_POWER_STATE, parm);
3305 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003306
Lydia Wang118909562011-03-23 17:57:34 +08003307 if (spec->codec_type == VT1802) {
3308 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3309 parm = AC_PWRST_D3;
3310 set_pin_power_state(codec, 0x25, &parm);
3311 snd_hda_codec_write(codec, 0x15, 0,
3312 AC_VERB_SET_POWER_STATE, parm);
3313 snd_hda_codec_write(codec, 0x35, 0,
3314 AC_VERB_SET_POWER_STATE, parm);
3315 } else {
3316 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3317 parm = AC_PWRST_D3;
3318 set_pin_power_state(codec, 0x25, &parm);
3319 snd_hda_codec_write(codec, 0x19, 0,
3320 AC_VERB_SET_POWER_STATE, parm);
3321 snd_hda_codec_write(codec, 0x35, 0,
3322 AC_VERB_SET_POWER_STATE, parm);
3323 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003324
3325 if (spec->hp_independent_mode)
3326 snd_hda_codec_write(codec, 0x9, 0,
3327 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3328
3329 /* Class-D */
3330 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3331 present = snd_hda_jack_detect(codec, 0x25);
3332
3333 parm = AC_PWRST_D3;
3334 set_pin_power_state(codec, 0x24, &parm);
3335 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003336 if (spec->codec_type == VT1802)
3337 snd_hda_codec_write(codec, 0x14, 0,
3338 AC_VERB_SET_POWER_STATE, parm);
3339 else
3340 snd_hda_codec_write(codec, 0x18, 0,
3341 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003342 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3343
3344 /* Mono Out */
3345 present = snd_hda_jack_detect(codec, 0x26);
3346
3347 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003348 if (spec->codec_type == VT1802) {
3349 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3350 snd_hda_codec_write(codec, 0x33, 0,
3351 AC_VERB_SET_POWER_STATE, parm);
3352 snd_hda_codec_write(codec, 0x1c, 0,
3353 AC_VERB_SET_POWER_STATE, parm);
3354 snd_hda_codec_write(codec, 0x3c, 0,
3355 AC_VERB_SET_POWER_STATE, parm);
3356 } else {
3357 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3358 snd_hda_codec_write(codec, 0x31, 0,
3359 AC_VERB_SET_POWER_STATE, parm);
3360 snd_hda_codec_write(codec, 0x17, 0,
3361 AC_VERB_SET_POWER_STATE, parm);
3362 snd_hda_codec_write(codec, 0x3b, 0,
3363 AC_VERB_SET_POWER_STATE, parm);
3364 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003365 /* MW9 (21h) */
3366 if (imux_is_smixer || !is_aa_path_mute(codec))
3367 snd_hda_codec_write(codec, 0x21, 0,
3368 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3369 else
3370 snd_hda_codec_write(codec, 0x21, 0,
3371 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3372}
Lydia Wang25eaba22009-10-10 19:08:43 +08003373
3374/* patch for vt2002P */
3375static int patch_vt2002P(struct hda_codec *codec)
3376{
3377 struct via_spec *spec;
3378 int err;
3379
3380 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003381 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003382 if (spec == NULL)
3383 return -ENOMEM;
3384
Takashi Iwai620e2b22011-06-17 17:19:19 +02003385 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003386 override_mic_boost(codec, 0x2b, 0, 3, 40);
3387 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003388
Lydia Wang25eaba22009-10-10 19:08:43 +08003389 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003390 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003391 if (err < 0) {
3392 via_free(codec);
3393 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003394 }
3395
Lydia Wang118909562011-03-23 17:57:34 +08003396 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003397 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003398 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003399 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003400
Lydia Wang25eaba22009-10-10 19:08:43 +08003401 codec->patch_ops = via_patch_ops;
3402
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003403 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003404 return 0;
3405}
Lydia Wangab6734e2009-10-10 19:08:46 +08003406
3407/* for vt1812 */
3408
Takashi Iwai096a8852011-06-20 12:09:02 +02003409static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003410 /* Enable Boost Volume backdoor */
3411 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003412 /* Enable AOW0 to MW9 */
3413 {0x1, 0xfb8, 0xa8},
3414 { }
3415};
3416
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003417static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3418{
3419 struct via_spec *spec = codec->spec;
3420 int imux_is_smixer =
3421 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3422 unsigned int parm;
3423 unsigned int present;
3424 /* MUX10 (1eh) = stereo mixer */
3425 imux_is_smixer =
3426 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3427 /* inputs */
3428 /* PW 5/6/7 (29h/2ah/2bh) */
3429 parm = AC_PWRST_D3;
3430 set_pin_power_state(codec, 0x29, &parm);
3431 set_pin_power_state(codec, 0x2a, &parm);
3432 set_pin_power_state(codec, 0x2b, &parm);
3433 parm = AC_PWRST_D0;
3434 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3435 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3436 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3437 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3438 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3439
3440 /* outputs */
3441 /* AOW0 (8h)*/
3442 snd_hda_codec_write(codec, 0x8, 0,
3443 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3444
3445 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3446 parm = AC_PWRST_D3;
3447 set_pin_power_state(codec, 0x28, &parm);
3448 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3449 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3450
3451 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3452 parm = AC_PWRST_D3;
3453 set_pin_power_state(codec, 0x25, &parm);
3454 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3455 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3456 if (spec->hp_independent_mode)
3457 snd_hda_codec_write(codec, 0x9, 0,
3458 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3459
3460 /* Internal Speaker */
3461 /* PW0 (24h), MW0(14h), MUX0(34h) */
3462 present = snd_hda_jack_detect(codec, 0x25);
3463
3464 parm = AC_PWRST_D3;
3465 set_pin_power_state(codec, 0x24, &parm);
3466 if (present) {
3467 snd_hda_codec_write(codec, 0x14, 0,
3468 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3469 snd_hda_codec_write(codec, 0x34, 0,
3470 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3471 } else {
3472 snd_hda_codec_write(codec, 0x14, 0,
3473 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3474 snd_hda_codec_write(codec, 0x34, 0,
3475 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3476 }
3477
3478
3479 /* Mono Out */
3480 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3481 present = snd_hda_jack_detect(codec, 0x28);
3482
3483 parm = AC_PWRST_D3;
3484 set_pin_power_state(codec, 0x31, &parm);
3485 if (present) {
3486 snd_hda_codec_write(codec, 0x1c, 0,
3487 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3488 snd_hda_codec_write(codec, 0x3c, 0,
3489 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3490 snd_hda_codec_write(codec, 0x3e, 0,
3491 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3492 } else {
3493 snd_hda_codec_write(codec, 0x1c, 0,
3494 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3495 snd_hda_codec_write(codec, 0x3c, 0,
3496 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3497 snd_hda_codec_write(codec, 0x3e, 0,
3498 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3499 }
3500
3501 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3502 parm = AC_PWRST_D3;
3503 set_pin_power_state(codec, 0x33, &parm);
3504 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3505 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3506
3507}
Lydia Wangab6734e2009-10-10 19:08:46 +08003508
3509/* patch for vt1812 */
3510static int patch_vt1812(struct hda_codec *codec)
3511{
3512 struct via_spec *spec;
3513 int err;
3514
3515 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003516 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003517 if (spec == NULL)
3518 return -ENOMEM;
3519
Takashi Iwai620e2b22011-06-17 17:19:19 +02003520 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003521 override_mic_boost(codec, 0x2b, 0, 3, 40);
3522 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003523
Lydia Wangab6734e2009-10-10 19:08:46 +08003524 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003525 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003526 if (err < 0) {
3527 via_free(codec);
3528 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003529 }
3530
Takashi Iwai096a8852011-06-20 12:09:02 +02003531 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003532
Lydia Wangab6734e2009-10-10 19:08:46 +08003533 codec->patch_ops = via_patch_ops;
3534
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003535 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003536 return 0;
3537}
3538
Joseph Chanc577b8a2006-11-29 15:29:40 +01003539/*
3540 * patch entries
3541 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003542static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003543 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3544 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3545 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3546 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3547 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003548 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003549 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003550 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003551 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003552 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003553 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003554 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003555 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003556 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003557 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003558 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003559 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003560 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003561 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003562 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003563 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003564 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003565 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003566 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003567 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003568 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003569 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003570 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003571 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003572 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003573 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003574 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003575 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003576 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003577 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003578 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003579 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003580 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003581 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003582 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003583 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003584 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003585 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003586 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003587 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003588 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003589 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003590 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003591 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003592 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003593 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003594 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003595 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003596 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003597 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003598 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003599 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003600 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003601 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003602 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003603 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003604 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003605 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003606 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003607 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003608 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003609 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003610 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003611 { .id = 0x11060428, .name = "VT1718S",
3612 .patch = patch_vt1718S},
3613 { .id = 0x11064428, .name = "VT1718S",
3614 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003615 { .id = 0x11060441, .name = "VT2020",
3616 .patch = patch_vt1718S},
3617 { .id = 0x11064441, .name = "VT1828S",
3618 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003619 { .id = 0x11060433, .name = "VT1716S",
3620 .patch = patch_vt1716S},
3621 { .id = 0x1106a721, .name = "VT1716S",
3622 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003623 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3624 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003625 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003626 { .id = 0x11060440, .name = "VT1818S",
3627 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003628 { .id = 0x11060446, .name = "VT1802",
3629 .patch = patch_vt2002P},
3630 { .id = 0x11068446, .name = "VT1802",
3631 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003632 {} /* terminator */
3633};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003634
3635MODULE_ALIAS("snd-hda-codec-id:1106*");
3636
3637static struct hda_codec_preset_list via_list = {
3638 .preset = snd_hda_preset_via,
3639 .owner = THIS_MODULE,
3640};
3641
3642MODULE_LICENSE("GPL");
3643MODULE_DESCRIPTION("VIA HD-audio codec");
3644
3645static int __init patch_via_init(void)
3646{
3647 return snd_hda_add_codec_preset(&via_list);
3648}
3649
3650static void __exit patch_via_exit(void)
3651{
3652 snd_hda_delete_codec_preset(&via_list);
3653}
3654
3655module_init(patch_via_init)
3656module_exit(patch_via_exit)