blob: 0a5a02ac2b22806d080bcbad3fef6d0a60a369ca [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
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200441/* enable/disable the output-route */
442static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
443 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200444{
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200445 int i;
446 for (i = 0; i < path->depth; i++) {
447 hda_nid_t src, dst;
448 int idx = path->idx[i];
449 src = path->path[i];
450 if (i < path->depth - 1)
451 dst = path->path[i + 1];
452 else
453 dst = 0;
454 if (enable && path->multi[i])
455 snd_hda_codec_write(codec, dst, 0,
456 AC_VERB_SET_CONNECT_SEL, idx);
Lydia Wangb89596a2011-07-04 17:01:33 +0800457 if (!force
458 && get_wcaps_type(get_wcaps(codec, src)) == AC_WID_AUD_OUT
459 && get_wcaps_type(get_wcaps(codec, dst)) == AC_WID_AUD_MIX)
Lydia Wange5e14682011-07-01 10:55:07 +0800460 continue;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200461 if (have_mute(codec, dst, HDA_INPUT)) {
462 int val = enable ? AMP_IN_UNMUTE(idx) :
463 AMP_IN_MUTE(idx);
464 snd_hda_codec_write(codec, dst, 0,
465 AC_VERB_SET_AMP_GAIN_MUTE, val);
466 }
467 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
468 continue;
469 if (have_mute(codec, src, HDA_OUTPUT)) {
470 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
471 snd_hda_codec_write(codec, src, 0,
472 AC_VERB_SET_AMP_GAIN_MUTE, val);
473 }
474 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200475}
476
477/* set the given pin as output */
478static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
479 int pin_type)
480{
481 if (!pin)
482 return;
483 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
484 pin_type);
485 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
486 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200487 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100488}
489
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200490static void via_auto_init_output(struct hda_codec *codec,
491 struct nid_path *path, int pin_type,
492 bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200493{
494 struct via_spec *spec = codec->spec;
495 unsigned int caps;
Lydia Wangb89596a2011-07-04 17:01:33 +0800496 hda_nid_t pin, nid, pre_nid;
497 int i, idx, j, num;
Takashi Iwai5d417622011-06-20 11:32:27 +0200498
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200499 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200500 return;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200501 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200502
503 init_output_pin(codec, pin, pin_type);
504 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
505 if (caps & AC_AMPCAP_MUTE) {
506 unsigned int val;
507 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
508 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
509 AMP_OUT_MUTE | val);
510 }
511
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200512 activate_output_path(codec, path, true, force);
513
514 /* initialize the AA-path */
515 if (!spec->aa_mix_nid)
516 return;
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200517 for (i = path->depth - 1; i > 0; i--) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200518 nid = path->path[i];
Lydia Wangb89596a2011-07-04 17:01:33 +0800519 pre_nid = path->path[i - 1];
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200520 idx = get_connection_index(codec, nid, spec->aa_mix_nid);
521 if (idx >= 0) {
Lydia Wangb89596a2011-07-04 17:01:33 +0800522 if (have_mute(codec, nid, HDA_INPUT)) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200523 snd_hda_codec_write(codec, nid, 0,
524 AC_VERB_SET_AMP_GAIN_MUTE,
525 AMP_IN_UNMUTE(idx));
Lydia Wangb89596a2011-07-04 17:01:33 +0800526 if (pre_nid == spec->multiout.dac_nids[0]) {
527 num = snd_hda_get_conn_list(codec, nid,
528 NULL);
529 for (j = 0; j < num; j++) {
530 if (j == idx)
531 continue;
532 snd_hda_codec_write(codec,
533 nid, 0,
534 AC_VERB_SET_AMP_GAIN_MUTE,
535 AMP_IN_MUTE(j));
536 }
537 }
538 }
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200539 break;
540 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200541 }
542}
543
Joseph Chanc577b8a2006-11-29 15:29:40 +0100544static void via_auto_init_multi_out(struct hda_codec *codec)
545{
546 struct via_spec *spec = codec->spec;
547 int i;
548
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200549 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200550 via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, true);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100551}
552
553static void via_auto_init_hp_out(struct hda_codec *codec)
554{
555 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100556
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200557 if (!spec->hp_dac_nid) {
558 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
559 return;
560 }
561 if (spec->hp_independent_mode) {
562 activate_output_path(codec, &spec->hp_dep_path, false, false);
563 via_auto_init_output(codec, &spec->hp_path, PIN_HP, true);
564 } else {
565 activate_output_path(codec, &spec->hp_path, false, false);
566 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
567 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100568}
569
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200570static void via_auto_init_speaker_out(struct hda_codec *codec)
571{
572 struct via_spec *spec = codec->spec;
573
574 if (spec->autocfg.speaker_outs)
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200575 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, true);
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200576}
577
Takashi Iwaif4a78282011-06-17 18:46:48 +0200578static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200579
Joseph Chanc577b8a2006-11-29 15:29:40 +0100580static void via_auto_init_analog_input(struct hda_codec *codec)
581{
582 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200583 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200584 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200585 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200586 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100587
Takashi Iwai096a8852011-06-20 12:09:02 +0200588 /* init ADCs */
589 for (i = 0; i < spec->num_adc_nids; i++) {
590 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
591 AC_VERB_SET_AMP_GAIN_MUTE,
592 AMP_IN_UNMUTE(0));
593 }
594
595 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200596 for (i = 0; i < cfg->num_inputs; i++) {
597 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200598 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200599 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100600 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200601 ctl = PIN_VREF50;
602 else
603 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100604 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200605 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100606 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200607
608 /* init input-src */
609 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200610 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
611 if (spec->mux_nids[adc_idx]) {
612 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
613 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
614 AC_VERB_SET_CONNECT_SEL,
615 mux_idx);
616 }
617 if (spec->dyn_adc_switch)
618 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200619 }
620
621 /* init aa-mixer */
622 if (!spec->aa_mix_nid)
623 return;
624 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
625 ARRAY_SIZE(conn));
626 for (i = 0; i < num_conns; i++) {
627 unsigned int caps = get_wcaps(codec, conn[i]);
628 if (get_wcaps_type(caps) == AC_WID_PIN)
629 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
630 AC_VERB_SET_AMP_GAIN_MUTE,
631 AMP_IN_MUTE(i));
632 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100633}
Lydia Wangf5271102009-10-10 19:07:35 +0800634
635static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
636 unsigned int *affected_parm)
637{
638 unsigned parm;
639 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
640 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
641 >> AC_DEFCFG_MISC_SHIFT
642 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800643 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200644 unsigned present = 0;
645
646 no_presence |= spec->no_pin_power_ctl;
647 if (!no_presence)
648 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200649 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800650 || ((no_presence || present)
651 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800652 *affected_parm = AC_PWRST_D0; /* if it's connected */
653 parm = AC_PWRST_D0;
654 } else
655 parm = AC_PWRST_D3;
656
657 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
658}
659
Takashi Iwai24088a52011-06-17 16:59:21 +0200660static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
661 struct snd_ctl_elem_info *uinfo)
662{
663 static const char * const texts[] = {
664 "Disabled", "Enabled"
665 };
666
667 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
668 uinfo->count = 1;
669 uinfo->value.enumerated.items = 2;
670 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
671 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
672 strcpy(uinfo->value.enumerated.name,
673 texts[uinfo->value.enumerated.item]);
674 return 0;
675}
676
677static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
678 struct snd_ctl_elem_value *ucontrol)
679{
680 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
681 struct via_spec *spec = codec->spec;
682 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
683 return 0;
684}
685
686static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
687 struct snd_ctl_elem_value *ucontrol)
688{
689 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
690 struct via_spec *spec = codec->spec;
691 unsigned int val = !ucontrol->value.enumerated.item[0];
692
693 if (val == spec->no_pin_power_ctl)
694 return 0;
695 spec->no_pin_power_ctl = val;
696 set_widgets_power_state(codec);
697 return 1;
698}
699
700static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
701 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
702 .name = "Dynamic Power-Control",
703 .info = via_pin_power_ctl_info,
704 .get = via_pin_power_ctl_get,
705 .put = via_pin_power_ctl_put,
706};
707
708
Harald Welte0aa62ae2008-09-09 15:58:27 +0800709static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
710 struct snd_ctl_elem_info *uinfo)
711{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200712 static const char * const texts[] = { "OFF", "ON" };
713
714 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
715 uinfo->count = 1;
716 uinfo->value.enumerated.items = 2;
717 if (uinfo->value.enumerated.item >= 2)
718 uinfo->value.enumerated.item = 1;
719 strcpy(uinfo->value.enumerated.name,
720 texts[uinfo->value.enumerated.item]);
721 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800722}
723
724static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
725 struct snd_ctl_elem_value *ucontrol)
726{
727 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800728 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800729
Takashi Iwaiece8d042011-06-19 16:24:21 +0200730 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800731 return 0;
732}
733
Harald Welte0aa62ae2008-09-09 15:58:27 +0800734static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
735 struct snd_ctl_elem_value *ucontrol)
736{
737 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
738 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +0200739 int cur;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200740
Takashi Iwai25250502011-06-30 17:24:47 +0200741 /* no independent-hp status change during PCM playback is running */
742 if (spec->num_active_streams)
743 return -EBUSY;
744
745 cur = !!ucontrol->value.enumerated.item[0];
746 if (spec->hp_independent_mode == cur)
747 return 0;
748 spec->hp_independent_mode = cur;
749 if (cur) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200750 activate_output_path(codec, &spec->hp_dep_path, false, false);
751 activate_output_path(codec, &spec->hp_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200752 if (spec->hp_indep_shared)
753 activate_output_path(codec, &spec->out_path[HDA_SIDE],
754 false, false);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200755 } else {
756 activate_output_path(codec, &spec->hp_path, false, false);
757 activate_output_path(codec, &spec->hp_dep_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200758 if (spec->hp_indep_shared)
759 activate_output_path(codec, &spec->out_path[HDA_SIDE],
760 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200761 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800762
Lydia Wangce0e5a92011-03-22 16:22:37 +0800763 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800764 set_widgets_power_state(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200765 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800766}
767
Takashi Iwaiece8d042011-06-19 16:24:21 +0200768static const struct snd_kcontrol_new via_hp_mixer = {
769 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
770 .name = "Independent HP",
771 .info = via_independent_hp_info,
772 .get = via_independent_hp_get,
773 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800774};
775
Takashi Iwai3d83e572010-04-14 14:36:23 +0200776static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100777{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200778 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100779 struct snd_kcontrol_new *knew;
780 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100781
Takashi Iwaiece8d042011-06-19 16:24:21 +0200782 nid = spec->autocfg.hp_pins[0];
783 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200784 if (knew == NULL)
785 return -ENOMEM;
786
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100787 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100788
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100789 return 0;
790}
791
Lydia Wang1564b282009-10-10 19:07:52 +0800792static void notify_aa_path_ctls(struct hda_codec *codec)
793{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200794 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800795 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800796
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200797 for (i = 0; i < spec->smart51_nums; i++) {
798 struct snd_kcontrol *ctl;
799 struct snd_ctl_elem_id id;
800 memset(&id, 0, sizeof(id));
801 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
802 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800803 ctl = snd_hda_find_mixer_ctl(codec, id.name);
804 if (ctl)
805 snd_ctl_notify(codec->bus->card,
806 SNDRV_CTL_EVENT_MASK_VALUE,
807 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800808 }
809}
810
811static void mute_aa_path(struct hda_codec *codec, int mute)
812{
813 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200814 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800815 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200816
Lydia Wang1564b282009-10-10 19:07:52 +0800817 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200818 for (i = 0; i < spec->smart51_nums; i++) {
819 if (spec->smart51_idxs[i] < 0)
820 continue;
821 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
822 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800823 HDA_AMP_MUTE, val);
824 }
825}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200826
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200827static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
828{
829 struct via_spec *spec = codec->spec;
830 int i;
831
832 for (i = 0; i < spec->smart51_nums; i++)
833 if (spec->smart51_pins[i] == pin)
834 return true;
835 return false;
836}
837
Lydia Wang1564b282009-10-10 19:07:52 +0800838static int via_smart51_get(struct snd_kcontrol *kcontrol,
839 struct snd_ctl_elem_value *ucontrol)
840{
841 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
842 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800843
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200844 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800845 return 0;
846}
847
848static int via_smart51_put(struct snd_kcontrol *kcontrol,
849 struct snd_ctl_elem_value *ucontrol)
850{
851 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
852 struct via_spec *spec = codec->spec;
853 int out_in = *ucontrol->value.integer.value
854 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800855 int i;
856
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200857 for (i = 0; i < spec->smart51_nums; i++) {
858 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200859 unsigned int parm;
860
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200861 parm = snd_hda_codec_read(codec, nid, 0,
862 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
863 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
864 parm |= out_in;
865 snd_hda_codec_write(codec, nid, 0,
866 AC_VERB_SET_PIN_WIDGET_CONTROL,
867 parm);
868 if (out_in == AC_PINCTL_OUT_EN) {
869 mute_aa_path(codec, 1);
870 notify_aa_path_ctls(codec);
871 }
Lydia Wang1564b282009-10-10 19:07:52 +0800872 }
873 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800874 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800875 return 1;
876}
877
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200878static const struct snd_kcontrol_new via_smart51_mixer = {
879 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
880 .name = "Smart 5.1",
881 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200882 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200883 .get = via_smart51_get,
884 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800885};
886
Takashi Iwaif4a78282011-06-17 18:46:48 +0200887static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100888{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200889 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100890
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200891 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800892 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200893 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100894 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100895 return 0;
896}
897
Takashi Iwaiada509e2011-06-20 15:40:19 +0200898/* check AA path's mute status */
899static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800900{
Lydia Wangf5271102009-10-10 19:07:35 +0800901 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200902 const struct hda_amp_list *p;
903 int i, ch, v;
904
905 for (i = 0; i < spec->num_loopbacks; i++) {
906 p = &spec->loopback_list[i];
907 for (ch = 0; ch < 2; ch++) {
908 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
909 p->idx);
910 if (!(v & HDA_AMP_MUTE) && v > 0)
911 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800912 }
913 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200914 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800915}
916
917/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200918static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800919{
920 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200921 bool enable;
922 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800923
Takashi Iwaiada509e2011-06-20 15:40:19 +0200924 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800925
926 /* decide low current mode's verb & parameter */
927 switch (spec->codec_type) {
928 case VT1708B_8CH:
929 case VT1708B_4CH:
930 verb = 0xf70;
931 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
932 break;
933 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800934 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800935 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800936 verb = 0xf73;
937 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
938 break;
939 case VT1702:
940 verb = 0xf73;
941 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
942 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800943 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800944 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800945 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800946 verb = 0xf93;
947 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
948 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800949 default:
950 return; /* other codecs are not supported */
951 }
952 /* send verb */
953 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
954}
955
Joseph Chanc577b8a2006-11-29 15:29:40 +0100956/*
957 * generic initialization of ADC, input mixers and output mixers
958 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200959static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800960 /* power down jack detect function */
961 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +0100962 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100963};
964
Takashi Iwaiada509e2011-06-20 15:40:19 +0200965static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200966{
Takashi Iwaiada509e2011-06-20 15:40:19 +0200967 struct via_spec *spec = codec->spec;
968
969 if (active)
970 spec->num_active_streams++;
971 else
972 spec->num_active_streams--;
973 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200974}
975
Takashi Iwaiece8d042011-06-19 16:24:21 +0200976static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100977 struct hda_codec *codec,
978 struct snd_pcm_substream *substream)
979{
980 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +0200981 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200982 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +0200983
Takashi Iwai25250502011-06-30 17:24:47 +0200984 spec->multiout.hp_nid = 0;
985 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
986 if (!spec->hp_independent_mode) {
987 if (!spec->hp_indep_shared)
988 spec->multiout.hp_nid = spec->hp_dac_nid;
989 } else {
990 if (spec->hp_indep_shared)
991 spec->multiout.num_dacs = cfg->line_outs - 1;
992 }
993 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200994 set_stream_active(codec, true);
995 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
996 hinfo);
997 if (err < 0) {
998 spec->multiout.hp_nid = 0;
999 set_stream_active(codec, false);
1000 return err;
1001 }
1002 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001003}
1004
Takashi Iwaiece8d042011-06-19 16:24:21 +02001005static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001006 struct hda_codec *codec,
1007 struct snd_pcm_substream *substream)
1008{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001009 struct via_spec *spec = codec->spec;
1010
1011 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001012 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001013 return 0;
1014}
1015
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001016static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1017 struct hda_codec *codec,
1018 struct snd_pcm_substream *substream)
1019{
1020 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001021
Takashi Iwaiece8d042011-06-19 16:24:21 +02001022 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001023 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001024 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1025 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001026 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001027 return 0;
1028}
1029
1030static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1031 struct hda_codec *codec,
1032 struct snd_pcm_substream *substream)
1033{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001034 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001035 return 0;
1036}
1037
1038static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1039 struct hda_codec *codec,
1040 unsigned int stream_tag,
1041 unsigned int format,
1042 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001043{
1044 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001045
Takashi Iwaiece8d042011-06-19 16:24:21 +02001046 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1047 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001048 vt1708_start_hp_work(spec);
1049 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001050}
1051
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001052static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1053 struct hda_codec *codec,
1054 unsigned int stream_tag,
1055 unsigned int format,
1056 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001057{
1058 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001059
Takashi Iwaiece8d042011-06-19 16:24:21 +02001060 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1061 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001062 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001063 return 0;
1064}
1065
1066static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1067 struct hda_codec *codec,
1068 struct snd_pcm_substream *substream)
1069{
1070 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001071
Takashi Iwaiece8d042011-06-19 16:24:21 +02001072 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001073 vt1708_stop_hp_work(spec);
1074 return 0;
1075}
1076
1077static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1078 struct hda_codec *codec,
1079 struct snd_pcm_substream *substream)
1080{
1081 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001082
Takashi Iwaiece8d042011-06-19 16:24:21 +02001083 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001084 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001085 return 0;
1086}
1087
Joseph Chanc577b8a2006-11-29 15:29:40 +01001088/*
1089 * Digital out
1090 */
1091static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1092 struct hda_codec *codec,
1093 struct snd_pcm_substream *substream)
1094{
1095 struct via_spec *spec = codec->spec;
1096 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1097}
1098
1099static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1100 struct hda_codec *codec,
1101 struct snd_pcm_substream *substream)
1102{
1103 struct via_spec *spec = codec->spec;
1104 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1105}
1106
Harald Welte5691ec72008-09-15 22:42:26 +08001107static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001108 struct hda_codec *codec,
1109 unsigned int stream_tag,
1110 unsigned int format,
1111 struct snd_pcm_substream *substream)
1112{
1113 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001114 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1115 stream_tag, format, substream);
1116}
Harald Welte5691ec72008-09-15 22:42:26 +08001117
Takashi Iwai9da29272009-05-07 16:31:14 +02001118static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1119 struct hda_codec *codec,
1120 struct snd_pcm_substream *substream)
1121{
1122 struct via_spec *spec = codec->spec;
1123 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001124 return 0;
1125}
1126
Joseph Chanc577b8a2006-11-29 15:29:40 +01001127/*
1128 * Analog capture
1129 */
1130static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1131 struct hda_codec *codec,
1132 unsigned int stream_tag,
1133 unsigned int format,
1134 struct snd_pcm_substream *substream)
1135{
1136 struct via_spec *spec = codec->spec;
1137
1138 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1139 stream_tag, 0, format);
1140 return 0;
1141}
1142
1143static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1144 struct hda_codec *codec,
1145 struct snd_pcm_substream *substream)
1146{
1147 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001148 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001149 return 0;
1150}
1151
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001152/* analog capture with dynamic ADC switching */
1153static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1154 struct hda_codec *codec,
1155 unsigned int stream_tag,
1156 unsigned int format,
1157 struct snd_pcm_substream *substream)
1158{
1159 struct via_spec *spec = codec->spec;
1160 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1161
1162 spec->cur_adc = spec->adc_nids[adc_idx];
1163 spec->cur_adc_stream_tag = stream_tag;
1164 spec->cur_adc_format = format;
1165 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
1166 return 0;
1167}
1168
1169static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1170 struct hda_codec *codec,
1171 struct snd_pcm_substream *substream)
1172{
1173 struct via_spec *spec = codec->spec;
1174
1175 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1176 spec->cur_adc = 0;
1177 return 0;
1178}
1179
1180/* re-setup the stream if running; called from input-src put */
1181static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1182{
1183 struct via_spec *spec = codec->spec;
1184 int adc_idx = spec->inputs[cur].adc_idx;
1185 hda_nid_t adc = spec->adc_nids[adc_idx];
1186
1187 if (spec->cur_adc && spec->cur_adc != adc) {
1188 /* stream is running, let's swap the current ADC */
1189 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1190 spec->cur_adc = adc;
1191 snd_hda_codec_setup_stream(codec, adc,
1192 spec->cur_adc_stream_tag, 0,
1193 spec->cur_adc_format);
1194 return true;
1195 }
1196 return false;
1197}
1198
Takashi Iwai9af74212011-06-18 16:17:45 +02001199static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001200 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001201 .channels_min = 2,
1202 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001203 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001204 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001205 .open = via_playback_multi_pcm_open,
1206 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001207 .prepare = via_playback_multi_pcm_prepare,
1208 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001209 },
1210};
1211
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001212static const struct hda_pcm_stream via_pcm_hp_playback = {
1213 .substreams = 1,
1214 .channels_min = 2,
1215 .channels_max = 2,
1216 /* NID is set in via_build_pcms */
1217 .ops = {
1218 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001219 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001220 .prepare = via_playback_hp_pcm_prepare,
1221 .cleanup = via_playback_hp_pcm_cleanup
1222 },
1223};
1224
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001225static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001226 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001227 .channels_min = 2,
1228 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001229 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001230 /* We got noisy outputs on the right channel on VT1708 when
1231 * 24bit samples are used. Until any workaround is found,
1232 * disable the 24bit format, so far.
1233 */
1234 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1235 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001236 .open = via_playback_multi_pcm_open,
1237 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001238 .prepare = via_playback_multi_pcm_prepare,
1239 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001240 },
1241};
1242
Takashi Iwai9af74212011-06-18 16:17:45 +02001243static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001244 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001245 .channels_min = 2,
1246 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001247 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001248 .ops = {
1249 .prepare = via_capture_pcm_prepare,
1250 .cleanup = via_capture_pcm_cleanup
1251 },
1252};
1253
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001254static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1255 .substreams = 1,
1256 .channels_min = 2,
1257 .channels_max = 2,
1258 /* NID is set in via_build_pcms */
1259 .ops = {
1260 .prepare = via_dyn_adc_capture_pcm_prepare,
1261 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1262 },
1263};
1264
Takashi Iwai9af74212011-06-18 16:17:45 +02001265static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001266 .substreams = 1,
1267 .channels_min = 2,
1268 .channels_max = 2,
1269 /* NID is set in via_build_pcms */
1270 .ops = {
1271 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001272 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001273 .prepare = via_dig_playback_pcm_prepare,
1274 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001275 },
1276};
1277
Takashi Iwai9af74212011-06-18 16:17:45 +02001278static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001279 .substreams = 1,
1280 .channels_min = 2,
1281 .channels_max = 2,
1282};
1283
Takashi Iwai370bafb2011-06-20 12:47:45 +02001284/*
1285 * slave controls for virtual master
1286 */
1287static const char * const via_slave_vols[] = {
1288 "Front Playback Volume",
1289 "Surround Playback Volume",
1290 "Center Playback Volume",
1291 "LFE Playback Volume",
1292 "Side Playback Volume",
1293 "Headphone Playback Volume",
1294 "Speaker Playback Volume",
1295 NULL,
1296};
1297
1298static const char * const via_slave_sws[] = {
1299 "Front Playback Switch",
1300 "Surround Playback Switch",
1301 "Center Playback Switch",
1302 "LFE Playback Switch",
1303 "Side Playback Switch",
1304 "Headphone Playback Switch",
1305 "Speaker Playback Switch",
1306 NULL,
1307};
1308
Joseph Chanc577b8a2006-11-29 15:29:40 +01001309static int via_build_controls(struct hda_codec *codec)
1310{
1311 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001312 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001313 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001314
Takashi Iwai24088a52011-06-17 16:59:21 +02001315 if (spec->set_widgets_power_state)
1316 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1317 return -ENOMEM;
1318
Joseph Chanc577b8a2006-11-29 15:29:40 +01001319 for (i = 0; i < spec->num_mixers; i++) {
1320 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1321 if (err < 0)
1322 return err;
1323 }
1324
1325 if (spec->multiout.dig_out_nid) {
1326 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001327 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001328 spec->multiout.dig_out_nid);
1329 if (err < 0)
1330 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001331 err = snd_hda_create_spdif_share_sw(codec,
1332 &spec->multiout);
1333 if (err < 0)
1334 return err;
1335 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001336 }
1337 if (spec->dig_in_nid) {
1338 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1339 if (err < 0)
1340 return err;
1341 }
Lydia Wang17314372009-10-10 19:07:37 +08001342
Takashi Iwai370bafb2011-06-20 12:47:45 +02001343 /* if we have no master control, let's create it */
1344 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1345 unsigned int vmaster_tlv[4];
1346 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1347 HDA_OUTPUT, vmaster_tlv);
1348 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1349 vmaster_tlv, via_slave_vols);
1350 if (err < 0)
1351 return err;
1352 }
1353 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1354 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1355 NULL, via_slave_sws);
1356 if (err < 0)
1357 return err;
1358 }
1359
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001360 /* assign Capture Source enums to NID */
1361 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1362 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001363 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001364 if (err < 0)
1365 return err;
1366 }
1367
Lydia Wang17314372009-10-10 19:07:37 +08001368 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001369 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001370 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001371
Takashi Iwai603c4012008-07-30 15:01:44 +02001372 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001373 return 0;
1374}
1375
1376static int via_build_pcms(struct hda_codec *codec)
1377{
1378 struct via_spec *spec = codec->spec;
1379 struct hda_pcm *info = spec->pcm_rec;
1380
1381 codec->num_pcms = 1;
1382 codec->pcm_info = info;
1383
Takashi Iwai82673bc2011-06-17 16:24:21 +02001384 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1385 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001386 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001387
1388 if (!spec->stream_analog_playback)
1389 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001390 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001391 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001392 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1393 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001394 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1395 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001396
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001397 if (!spec->stream_analog_capture) {
1398 if (spec->dyn_adc_switch)
1399 spec->stream_analog_capture =
1400 &via_pcm_dyn_adc_analog_capture;
1401 else
1402 spec->stream_analog_capture = &via_pcm_analog_capture;
1403 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001404 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1405 *spec->stream_analog_capture;
1406 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001407 if (!spec->dyn_adc_switch)
1408 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1409 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001410
1411 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1412 codec->num_pcms++;
1413 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001414 snprintf(spec->stream_name_digital,
1415 sizeof(spec->stream_name_digital),
1416 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001417 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001418 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001419 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001420 if (!spec->stream_digital_playback)
1421 spec->stream_digital_playback =
1422 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001423 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001424 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001425 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1426 spec->multiout.dig_out_nid;
1427 }
1428 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001429 if (!spec->stream_digital_capture)
1430 spec->stream_digital_capture =
1431 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001432 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001433 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001434 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1435 spec->dig_in_nid;
1436 }
1437 }
1438
Takashi Iwaiece8d042011-06-19 16:24:21 +02001439 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001440 codec->num_pcms++;
1441 info++;
1442 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1443 "%s HP", codec->chip_name);
1444 info->name = spec->stream_name_hp;
1445 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1446 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001447 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001448 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001449 return 0;
1450}
1451
1452static void via_free(struct hda_codec *codec)
1453{
1454 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001455
1456 if (!spec)
1457 return;
1458
Takashi Iwai603c4012008-07-30 15:01:44 +02001459 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001460 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001461 kfree(spec->bind_cap_vol);
1462 kfree(spec->bind_cap_sw);
1463 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001464}
1465
Takashi Iwai64be2852011-06-17 16:51:39 +02001466/* mute/unmute outputs */
1467static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1468 hda_nid_t *pins, bool mute)
1469{
1470 int i;
1471 for (i = 0; i < num_pins; i++)
1472 snd_hda_codec_write(codec, pins[i], 0,
1473 AC_VERB_SET_PIN_WIDGET_CONTROL,
1474 mute ? 0 : PIN_OUT);
1475}
1476
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001477/* mute internal speaker if line-out is plugged */
1478static void via_line_automute(struct hda_codec *codec, int present)
1479{
1480 struct via_spec *spec = codec->spec;
1481
1482 if (!spec->autocfg.speaker_outs)
1483 return;
1484 if (!present)
1485 present = snd_hda_jack_detect(codec,
1486 spec->autocfg.line_out_pins[0]);
1487 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1488 spec->autocfg.speaker_pins,
1489 present);
1490}
1491
Harald Welte69e52a82008-09-09 15:57:32 +08001492/* mute internal speaker if HP is plugged */
1493static void via_hp_automute(struct hda_codec *codec)
1494{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001495 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001496 struct via_spec *spec = codec->spec;
1497
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001498 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001499 int nums;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001500 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001501 if (spec->smart51_enabled)
1502 nums = spec->autocfg.line_outs + spec->smart51_nums;
1503 else
1504 nums = spec->autocfg.line_outs;
1505 toggle_output_mutes(codec, nums,
Takashi Iwai64be2852011-06-17 16:51:39 +02001506 spec->autocfg.line_out_pins,
1507 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001508 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001509 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001510}
1511
Harald Welte69e52a82008-09-09 15:57:32 +08001512static void via_gpio_control(struct hda_codec *codec)
1513{
1514 unsigned int gpio_data;
1515 unsigned int vol_counter;
1516 unsigned int vol;
1517 unsigned int master_vol;
1518
1519 struct via_spec *spec = codec->spec;
1520
1521 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1522 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1523
1524 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1525 0xF84, 0) & 0x3F0000) >> 16;
1526
1527 vol = vol_counter & 0x1F;
1528 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1529 AC_VERB_GET_AMP_GAIN_MUTE,
1530 AC_AMP_GET_INPUT);
1531
1532 if (gpio_data == 0x02) {
1533 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001534 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1535 AC_VERB_SET_PIN_WIDGET_CONTROL,
1536 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001537 if (vol_counter & 0x20) {
1538 /* decrease volume */
1539 if (vol > master_vol)
1540 vol = master_vol;
1541 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1542 0, HDA_AMP_VOLMASK,
1543 master_vol-vol);
1544 } else {
1545 /* increase volume */
1546 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1547 HDA_AMP_VOLMASK,
1548 ((master_vol+vol) > 0x2A) ? 0x2A :
1549 (master_vol+vol));
1550 }
1551 } else if (!(gpio_data & 0x02)) {
1552 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001553 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1554 AC_VERB_SET_PIN_WIDGET_CONTROL,
1555 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001556 }
1557}
1558
1559/* unsolicited event for jack sensing */
1560static void via_unsol_event(struct hda_codec *codec,
1561 unsigned int res)
1562{
1563 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001564
Lydia Wanga34df192009-10-10 19:08:01 +08001565 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001566 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001567
1568 res &= ~VIA_JACK_EVENT;
1569
1570 if (res == VIA_HP_EVENT)
1571 via_hp_automute(codec);
1572 else if (res == VIA_GPIO_EVENT)
1573 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001574 else if (res == VIA_LINE_EVENT)
1575 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001576}
1577
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001578#ifdef SND_HDA_NEEDS_RESUME
1579static int via_suspend(struct hda_codec *codec, pm_message_t state)
1580{
1581 struct via_spec *spec = codec->spec;
1582 vt1708_stop_hp_work(spec);
1583 return 0;
1584}
1585#endif
1586
Takashi Iwaicb53c622007-08-10 17:21:45 +02001587#ifdef CONFIG_SND_HDA_POWER_SAVE
1588static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1589{
1590 struct via_spec *spec = codec->spec;
1591 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1592}
1593#endif
1594
Joseph Chanc577b8a2006-11-29 15:29:40 +01001595/*
1596 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001597
1598static int via_init(struct hda_codec *codec);
1599
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001600static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001601 .build_controls = via_build_controls,
1602 .build_pcms = via_build_pcms,
1603 .init = via_init,
1604 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001605 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001606#ifdef SND_HDA_NEEDS_RESUME
1607 .suspend = via_suspend,
1608#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001609#ifdef CONFIG_SND_HDA_POWER_SAVE
1610 .check_power_status = via_check_power_status,
1611#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001612};
1613
Takashi Iwai4a796162011-06-17 17:53:38 +02001614static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001615{
Takashi Iwai4a796162011-06-17 17:53:38 +02001616 struct via_spec *spec = codec->spec;
1617 int i;
1618
1619 for (i = 0; i < spec->multiout.num_dacs; i++) {
1620 if (spec->multiout.dac_nids[i] == dac)
1621 return false;
1622 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001623 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001624 return false;
1625 return true;
1626}
1627
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001628static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001629 hda_nid_t target_dac, struct nid_path *path,
1630 int depth, int wid_type)
1631{
1632 hda_nid_t conn[8];
1633 int i, nums;
1634
1635 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1636 for (i = 0; i < nums; i++) {
1637 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1638 continue;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001639 if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
1640 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001641 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001642 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001643 return false;
1644 for (i = 0; i < nums; i++) {
1645 unsigned int type;
1646 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1647 if (type == AC_WID_AUD_OUT ||
1648 (wid_type != -1 && type != wid_type))
1649 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001650 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001651 path, depth + 1, AC_WID_AUD_SEL))
1652 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001653 }
1654 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001655
1656 found:
1657 path->path[path->depth] = conn[i];
1658 path->idx[path->depth] = i;
1659 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1660 path->multi[path->depth] = 1;
1661 path->depth++;
1662 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001663}
1664
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001665static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1666 hda_nid_t target_dac, struct nid_path *path)
1667{
1668 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1669 path->path[path->depth] = nid;
1670 path->depth++;
1671 return true;
1672 }
1673 return false;
1674}
1675
Takashi Iwai4a796162011-06-17 17:53:38 +02001676static int via_auto_fill_dac_nids(struct hda_codec *codec)
1677{
1678 struct via_spec *spec = codec->spec;
1679 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001680 int i;
1681 hda_nid_t nid;
1682
Joseph Chanc577b8a2006-11-29 15:29:40 +01001683 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001684 spec->multiout.num_dacs = cfg->line_outs;
1685 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001686 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001687 if (!nid)
1688 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001689 if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
1690 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001691 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001692 return 0;
1693}
1694
Takashi Iwai4a796162011-06-17 17:53:38 +02001695static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001696 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001697{
Takashi Iwai4a796162011-06-17 17:53:38 +02001698 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001699 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001700 hda_nid_t dac, pin, sel, nid;
1701 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001702
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001703 dac = check_dac ? path->path[0] : 0;
1704 pin = path->path[path->depth - 1];
1705 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001706
Takashi Iwai8df2a312011-06-21 11:48:29 +02001707 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001708 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001709 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001710 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001711 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1712 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001713 else
1714 nid = 0;
1715 if (nid) {
1716 sprintf(name, "%s Playback Volume", pfx);
1717 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001718 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001719 if (err < 0)
1720 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001721 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001722 }
1723
Takashi Iwai8df2a312011-06-21 11:48:29 +02001724 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001725 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001726 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001727 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001728 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1729 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001730 else
1731 nid = 0;
1732 if (nid) {
1733 sprintf(name, "%s Playback Switch", pfx);
1734 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1735 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1736 if (err < 0)
1737 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001738 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001739 }
1740 return 0;
1741}
1742
Takashi Iwaif4a78282011-06-17 18:46:48 +02001743static void mangle_smart51(struct hda_codec *codec)
1744{
1745 struct via_spec *spec = codec->spec;
1746 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001747 struct auto_pin_cfg_item *ins = cfg->inputs;
1748 int i, j, nums, attr;
1749 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001750
Takashi Iwai0f98c242011-06-21 12:51:33 +02001751 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1752 nums = 0;
1753 for (i = 0; i < cfg->num_inputs; i++) {
1754 unsigned int def;
1755 if (ins[i].type > AUTO_PIN_LINE_IN)
1756 continue;
1757 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1758 if (snd_hda_get_input_pin_attr(def) != attr)
1759 continue;
1760 for (j = 0; j < nums; j++)
1761 if (ins[pins[j]].type < ins[i].type) {
1762 memmove(pins + j + 1, pins + j,
1763 (nums - j - 1) * sizeof(int));
1764 break;
1765 }
1766 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001767 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001768 }
1769 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001770 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001771 for (i = 0; i < nums; i++) {
1772 hda_nid_t pin = ins[pins[i]].pin;
1773 spec->smart51_pins[spec->smart51_nums++] = pin;
1774 cfg->line_out_pins[cfg->line_outs++] = pin;
1775 if (cfg->line_outs == 3)
1776 break;
1777 }
1778 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001779 }
1780}
1781
Takashi Iwai4a796162011-06-17 17:53:38 +02001782/* add playback controls from the parsed DAC table */
1783static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1784{
1785 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001786 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001787 static const char * const chname[4] = {
1788 "Front", "Surround", "C/LFE", "Side"
1789 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001790 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001791 int old_line_outs;
1792
1793 /* check smart51 */
1794 old_line_outs = cfg->line_outs;
1795 if (cfg->line_outs == 1)
1796 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001797
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001798 err = via_auto_fill_dac_nids(codec);
1799 if (err < 0)
1800 return err;
1801
Takashi Iwai4a796162011-06-17 17:53:38 +02001802 for (i = 0; i < cfg->line_outs; i++) {
1803 hda_nid_t pin, dac;
1804 pin = cfg->line_out_pins[i];
1805 dac = spec->multiout.dac_nids[i];
1806 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001807 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001808 if (i == HDA_CLFE) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001809 err = create_ch_ctls(codec, "Center", 1, true,
1810 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001811 if (err < 0)
1812 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001813 err = create_ch_ctls(codec, "LFE", 2, true,
1814 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001815 if (err < 0)
1816 return err;
1817 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001818 const char *pfx = chname[i];
1819 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1820 cfg->line_outs == 1)
1821 pfx = "Speaker";
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001822 err = create_ch_ctls(codec, pfx, 3, true,
1823 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001824 if (err < 0)
1825 return err;
1826 }
1827 }
1828
Takashi Iwai4a796162011-06-17 17:53:38 +02001829 idx = get_connection_index(codec, spec->aa_mix_nid,
1830 spec->multiout.dac_nids[0]);
Lydia Wangc4394f52011-07-04 16:54:15 +08001831 if (idx < 0 && spec->dac_mixer_idx)
1832 idx = spec->dac_mixer_idx;
Takashi Iwai4a796162011-06-17 17:53:38 +02001833 if (idx >= 0) {
1834 /* add control to mixer */
1835 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1836 "PCM Playback Volume",
1837 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1838 idx, HDA_INPUT));
1839 if (err < 0)
1840 return err;
1841 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1842 "PCM Playback Switch",
1843 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1844 idx, HDA_INPUT));
1845 if (err < 0)
1846 return err;
1847 }
1848
Takashi Iwaif4a78282011-06-17 18:46:48 +02001849 cfg->line_outs = old_line_outs;
1850
Joseph Chanc577b8a2006-11-29 15:29:40 +01001851 return 0;
1852}
1853
Takashi Iwai4a796162011-06-17 17:53:38 +02001854static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001855{
Takashi Iwai4a796162011-06-17 17:53:38 +02001856 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001857 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001858 int err;
1859
1860 if (!pin)
1861 return 0;
1862
Takashi Iwai8df2a312011-06-21 11:48:29 +02001863 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001864 spec->hp_dac_nid = spec->hp_path.path[0];
Takashi Iwai25250502011-06-30 17:24:47 +02001865 else if (spec->multiout.dac_nids[HDA_SIDE] &&
1866 parse_output_path(codec, pin,
1867 spec->multiout.dac_nids[HDA_SIDE],
1868 &spec->hp_path)) {
1869 spec->hp_dac_nid = spec->hp_path.path[0];
1870 spec->hp_indep_shared = true;
1871 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001872
Takashi Iwaiece8d042011-06-19 16:24:21 +02001873 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001874 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001875 !spec->hp_dac_nid)
1876 return 0;
1877
Takashi Iwai25250502011-06-30 17:24:47 +02001878 if (spec->hp_dac_nid && !spec->hp_indep_shared)
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001879 path = &spec->hp_path;
1880 else
1881 path = &spec->hp_dep_path;
1882 err = create_ch_ctls(codec, "Headphone", 3, false, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001883 if (err < 0)
1884 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001885 if (spec->hp_dac_nid) {
1886 spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
1887 spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
1888 }
Harald Welte0aa62ae2008-09-09 15:58:27 +08001889
Joseph Chanc577b8a2006-11-29 15:29:40 +01001890 return 0;
1891}
1892
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001893static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1894{
1895 struct via_spec *spec = codec->spec;
1896 hda_nid_t pin, dac;
1897
1898 pin = spec->autocfg.speaker_pins[0];
1899 if (!spec->autocfg.speaker_outs || !pin)
1900 return 0;
1901
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001902 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1903 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001904 spec->multiout.extra_out_nid[0] = dac;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001905 return create_ch_ctls(codec, "Speaker", 3, true,
1906 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001907 }
1908 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001909 &spec->speaker_path))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001910 return create_ch_ctls(codec, "Speaker", 3, false,
1911 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001912
1913 return 0;
1914}
1915
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001916/* look for ADCs */
1917static int via_fill_adcs(struct hda_codec *codec)
1918{
1919 struct via_spec *spec = codec->spec;
1920 hda_nid_t nid = codec->start_nid;
1921 int i;
1922
1923 for (i = 0; i < codec->num_nodes; i++, nid++) {
1924 unsigned int wcaps = get_wcaps(codec, nid);
1925 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1926 continue;
1927 if (wcaps & AC_WCAP_DIGITAL)
1928 continue;
1929 if (!(wcaps & AC_WCAP_CONN_LIST))
1930 continue;
1931 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1932 return -ENOMEM;
1933 spec->adc_nids[spec->num_adc_nids++] = nid;
1934 }
1935 return 0;
1936}
1937
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001938/* input-src control */
1939static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
1940 struct snd_ctl_elem_info *uinfo)
1941{
1942 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1943 struct via_spec *spec = codec->spec;
1944
1945 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1946 uinfo->count = 1;
1947 uinfo->value.enumerated.items = spec->num_inputs;
1948 if (uinfo->value.enumerated.item >= spec->num_inputs)
1949 uinfo->value.enumerated.item = spec->num_inputs - 1;
1950 strcpy(uinfo->value.enumerated.name,
1951 spec->inputs[uinfo->value.enumerated.item].label);
1952 return 0;
1953}
1954
1955static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
1956 struct snd_ctl_elem_value *ucontrol)
1957{
1958 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1959 struct via_spec *spec = codec->spec;
1960 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1961
1962 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
1963 return 0;
1964}
1965
1966static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
1967 struct snd_ctl_elem_value *ucontrol)
1968{
1969 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1970 struct via_spec *spec = codec->spec;
1971 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1972 hda_nid_t mux;
1973 int cur;
1974
1975 cur = ucontrol->value.enumerated.item[0];
1976 if (cur < 0 || cur >= spec->num_inputs)
1977 return -EINVAL;
1978 if (spec->cur_mux[idx] == cur)
1979 return 0;
1980 spec->cur_mux[idx] = cur;
1981 if (spec->dyn_adc_switch) {
1982 int adc_idx = spec->inputs[cur].adc_idx;
1983 mux = spec->mux_nids[adc_idx];
1984 via_dyn_adc_pcm_resetup(codec, cur);
1985 } else {
1986 mux = spec->mux_nids[idx];
1987 if (snd_BUG_ON(!mux))
1988 return -EINVAL;
1989 }
1990
1991 if (mux) {
1992 /* switch to D0 beofre change index */
1993 if (snd_hda_codec_read(codec, mux, 0,
1994 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
1995 snd_hda_codec_write(codec, mux, 0,
1996 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
1997 snd_hda_codec_write(codec, mux, 0,
1998 AC_VERB_SET_CONNECT_SEL,
1999 spec->inputs[cur].mux_idx);
2000 }
2001
2002 /* update jack power state */
2003 set_widgets_power_state(codec);
2004 return 0;
2005}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002006
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002007static const struct snd_kcontrol_new via_input_src_ctl = {
2008 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2009 /* The multiple "Capture Source" controls confuse alsamixer
2010 * So call somewhat different..
2011 */
2012 /* .name = "Capture Source", */
2013 .name = "Input Source",
2014 .info = via_mux_enum_info,
2015 .get = via_mux_enum_get,
2016 .put = via_mux_enum_put,
2017};
2018
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002019static int create_input_src_ctls(struct hda_codec *codec, int count)
2020{
2021 struct via_spec *spec = codec->spec;
2022 struct snd_kcontrol_new *knew;
2023
2024 if (spec->num_inputs <= 1 || !count)
2025 return 0; /* no need for single src */
2026
2027 knew = via_clone_control(spec, &via_input_src_ctl);
2028 if (!knew)
2029 return -ENOMEM;
2030 knew->count = count;
2031 return 0;
2032}
2033
2034/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002035static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2036{
2037 struct hda_amp_list *list;
2038
2039 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2040 return;
2041 list = spec->loopback_list + spec->num_loopbacks;
2042 list->nid = mix;
2043 list->dir = HDA_INPUT;
2044 list->idx = idx;
2045 spec->num_loopbacks++;
2046 spec->loopback.amplist = spec->loopback_list;
2047}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002048
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002049static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002050 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002051{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002052 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002053}
2054
2055/* add the input-route to the given pin */
2056static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002057{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002058 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002059 int c, idx;
2060
2061 spec->inputs[spec->num_inputs].adc_idx = -1;
2062 spec->inputs[spec->num_inputs].pin = pin;
2063 for (c = 0; c < spec->num_adc_nids; c++) {
2064 if (spec->mux_nids[c]) {
2065 idx = get_connection_index(codec, spec->mux_nids[c],
2066 pin);
2067 if (idx < 0)
2068 continue;
2069 spec->inputs[spec->num_inputs].mux_idx = idx;
2070 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002071 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002072 continue;
2073 }
2074 spec->inputs[spec->num_inputs].adc_idx = c;
2075 /* Can primary ADC satisfy all inputs? */
2076 if (!spec->dyn_adc_switch &&
2077 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2078 snd_printd(KERN_INFO
2079 "via: dynamic ADC switching enabled\n");
2080 spec->dyn_adc_switch = 1;
2081 }
2082 return true;
2083 }
2084 return false;
2085}
2086
2087static int get_mux_nids(struct hda_codec *codec);
2088
2089/* parse input-routes; fill ADCs, MUXs and input-src entries */
2090static int parse_analog_inputs(struct hda_codec *codec)
2091{
2092 struct via_spec *spec = codec->spec;
2093 const struct auto_pin_cfg *cfg = &spec->autocfg;
2094 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002095
2096 err = via_fill_adcs(codec);
2097 if (err < 0)
2098 return err;
2099 err = get_mux_nids(codec);
2100 if (err < 0)
2101 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002102
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002103 /* fill all input-routes */
2104 for (i = 0; i < cfg->num_inputs; i++) {
2105 if (add_input_route(codec, cfg->inputs[i].pin))
2106 spec->inputs[spec->num_inputs++].label =
2107 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002108 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002109
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002110 /* check for internal loopback recording */
2111 if (spec->aa_mix_nid &&
2112 add_input_route(codec, spec->aa_mix_nid))
2113 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2114
2115 return 0;
2116}
2117
2118/* create analog-loopback volume/switch controls */
2119static int create_loopback_ctls(struct hda_codec *codec)
2120{
2121 struct via_spec *spec = codec->spec;
2122 const struct auto_pin_cfg *cfg = &spec->autocfg;
2123 const char *prev_label = NULL;
2124 int type_idx = 0;
2125 int i, j, err, idx;
2126
2127 if (!spec->aa_mix_nid)
2128 return 0;
2129
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002130 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002131 hda_nid_t pin = cfg->inputs[i].pin;
2132 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2133
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002134 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002135 type_idx++;
2136 else
2137 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002138 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002139 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2140 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002141 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002142 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002143 if (err < 0)
2144 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002145 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002146 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002147
2148 /* remember the label for smart51 control */
2149 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002150 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002151 spec->smart51_idxs[j] = idx;
2152 spec->smart51_labels[j] = label;
2153 break;
2154 }
2155 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002156 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002157 return 0;
2158}
2159
2160/* create mic-boost controls (if present) */
2161static int create_mic_boost_ctls(struct hda_codec *codec)
2162{
2163 struct via_spec *spec = codec->spec;
2164 const struct auto_pin_cfg *cfg = &spec->autocfg;
2165 int i, err;
2166
2167 for (i = 0; i < cfg->num_inputs; i++) {
2168 hda_nid_t pin = cfg->inputs[i].pin;
2169 unsigned int caps;
2170 const char *label;
2171 char name[32];
2172
2173 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2174 continue;
2175 caps = query_amp_caps(codec, pin, HDA_INPUT);
2176 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2177 continue;
2178 label = hda_get_autocfg_input_label(codec, cfg, i);
2179 snprintf(name, sizeof(name), "%s Boost Volume", label);
2180 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2181 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2182 if (err < 0)
2183 return err;
2184 }
2185 return 0;
2186}
2187
2188/* create capture and input-src controls for multiple streams */
2189static int create_multi_adc_ctls(struct hda_codec *codec)
2190{
2191 struct via_spec *spec = codec->spec;
2192 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002193
2194 /* create capture mixer elements */
2195 for (i = 0; i < spec->num_adc_nids; i++) {
2196 hda_nid_t adc = spec->adc_nids[i];
2197 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2198 "Capture Volume", i,
2199 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2200 HDA_INPUT));
2201 if (err < 0)
2202 return err;
2203 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2204 "Capture Switch", i,
2205 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2206 HDA_INPUT));
2207 if (err < 0)
2208 return err;
2209 }
2210
2211 /* input-source control */
2212 for (i = 0; i < spec->num_adc_nids; i++)
2213 if (!spec->mux_nids[i])
2214 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002215 err = create_input_src_ctls(codec, i);
2216 if (err < 0)
2217 return err;
2218 return 0;
2219}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002220
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002221/* bind capture volume/switch */
2222static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2223 HDA_BIND_VOL("Capture Volume", 0);
2224static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2225 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002226
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002227static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2228 struct hda_ctl_ops *ops)
2229{
2230 struct hda_bind_ctls *ctl;
2231 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002232
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002233 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2234 if (!ctl)
2235 return -ENOMEM;
2236 ctl->ops = ops;
2237 for (i = 0; i < spec->num_adc_nids; i++)
2238 ctl->values[i] =
2239 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2240 *ctl_ret = ctl;
2241 return 0;
2242}
2243
2244/* create capture and input-src controls for dynamic ADC-switch case */
2245static int create_dyn_adc_ctls(struct hda_codec *codec)
2246{
2247 struct via_spec *spec = codec->spec;
2248 struct snd_kcontrol_new *knew;
2249 int err;
2250
2251 /* set up the bind capture ctls */
2252 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2253 if (err < 0)
2254 return err;
2255 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2256 if (err < 0)
2257 return err;
2258
2259 /* create capture mixer elements */
2260 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2261 if (!knew)
2262 return -ENOMEM;
2263 knew->private_value = (long)spec->bind_cap_vol;
2264
2265 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2266 if (!knew)
2267 return -ENOMEM;
2268 knew->private_value = (long)spec->bind_cap_sw;
2269
2270 /* input-source control */
2271 err = create_input_src_ctls(codec, 1);
2272 if (err < 0)
2273 return err;
2274 return 0;
2275}
2276
2277/* parse and create capture-related stuff */
2278static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2279{
2280 struct via_spec *spec = codec->spec;
2281 int err;
2282
2283 err = parse_analog_inputs(codec);
2284 if (err < 0)
2285 return err;
2286 if (spec->dyn_adc_switch)
2287 err = create_dyn_adc_ctls(codec);
2288 else
2289 err = create_multi_adc_ctls(codec);
2290 if (err < 0)
2291 return err;
2292 err = create_loopback_ctls(codec);
2293 if (err < 0)
2294 return err;
2295 err = create_mic_boost_ctls(codec);
2296 if (err < 0)
2297 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002298 return 0;
2299}
2300
Harald Welte76d9b0d2008-09-09 15:50:37 +08002301static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2302{
2303 unsigned int def_conf;
2304 unsigned char seqassoc;
2305
Takashi Iwai2f334f92009-02-20 14:37:42 +01002306 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002307 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2308 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002309 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2310 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2311 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2312 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002313 }
2314
2315 return;
2316}
2317
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002318static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002319 struct snd_ctl_elem_value *ucontrol)
2320{
2321 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2322 struct via_spec *spec = codec->spec;
2323
2324 if (spec->codec_type != VT1708)
2325 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002326 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002327 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002328 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002329 return 0;
2330}
2331
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002332static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002333 struct snd_ctl_elem_value *ucontrol)
2334{
2335 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2336 struct via_spec *spec = codec->spec;
2337 int change;
2338
2339 if (spec->codec_type != VT1708)
2340 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002341 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002342 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002343 == !spec->vt1708_jack_detect;
2344 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002345 mute_aa_path(codec, 1);
2346 notify_aa_path_ctls(codec);
2347 }
2348 return change;
2349}
2350
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002351static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2352 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2353 .name = "Jack Detect",
2354 .count = 1,
2355 .info = snd_ctl_boolean_mono_info,
2356 .get = vt1708_jack_detect_get,
2357 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002358};
2359
Takashi Iwai12daef62011-06-18 17:45:49 +02002360static void fill_dig_outs(struct hda_codec *codec);
2361static void fill_dig_in(struct hda_codec *codec);
2362
2363static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002364{
2365 struct via_spec *spec = codec->spec;
2366 int err;
2367
2368 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2369 if (err < 0)
2370 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002371 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002372 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002373
Takashi Iwai4a796162011-06-17 17:53:38 +02002374 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002375 if (err < 0)
2376 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002377 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002378 if (err < 0)
2379 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002380 err = via_auto_create_speaker_ctls(codec);
2381 if (err < 0)
2382 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002383 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002384 if (err < 0)
2385 return err;
2386
2387 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2388
Takashi Iwai12daef62011-06-18 17:45:49 +02002389 fill_dig_outs(codec);
2390 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002391
Takashi Iwai603c4012008-07-30 15:01:44 +02002392 if (spec->kctls.list)
2393 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002394
Joseph Chanc577b8a2006-11-29 15:29:40 +01002395
Takashi Iwai8df2a312011-06-21 11:48:29 +02002396 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002397 err = via_hp_build(codec);
2398 if (err < 0)
2399 return err;
2400 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002401
Takashi Iwaif4a78282011-06-17 18:46:48 +02002402 err = via_smart51_build(codec);
2403 if (err < 0)
2404 return err;
2405
Takashi Iwai5d417622011-06-20 11:32:27 +02002406 /* assign slave outs */
2407 if (spec->slave_dig_outs[0])
2408 codec->slave_dig_outs = spec->slave_dig_outs;
2409
Joseph Chanc577b8a2006-11-29 15:29:40 +01002410 return 1;
2411}
2412
Takashi Iwai5d417622011-06-20 11:32:27 +02002413static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002414{
Lydia Wang25eaba22009-10-10 19:08:43 +08002415 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002416 if (spec->multiout.dig_out_nid)
2417 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2418 if (spec->slave_dig_outs[0])
2419 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2420}
Lydia Wang25eaba22009-10-10 19:08:43 +08002421
Takashi Iwai5d417622011-06-20 11:32:27 +02002422static void via_auto_init_dig_in(struct hda_codec *codec)
2423{
2424 struct via_spec *spec = codec->spec;
2425 if (!spec->dig_in_nid)
2426 return;
2427 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2428 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2429}
2430
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002431/* initialize the unsolicited events */
2432static void via_auto_init_unsol_event(struct hda_codec *codec)
2433{
2434 struct via_spec *spec = codec->spec;
2435 struct auto_pin_cfg *cfg = &spec->autocfg;
2436 unsigned int ev;
2437 int i;
2438
2439 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2440 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2441 AC_VERB_SET_UNSOLICITED_ENABLE,
2442 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2443
2444 if (cfg->speaker_pins[0])
2445 ev = VIA_LINE_EVENT;
2446 else
2447 ev = 0;
2448 for (i = 0; i < cfg->line_outs; i++) {
2449 if (cfg->line_out_pins[i] &&
2450 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002451 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002452 AC_VERB_SET_UNSOLICITED_ENABLE,
2453 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2454 }
2455
2456 for (i = 0; i < cfg->num_inputs; i++) {
2457 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2458 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2459 AC_VERB_SET_UNSOLICITED_ENABLE,
2460 AC_USRSP_EN | VIA_JACK_EVENT);
2461 }
2462}
2463
Takashi Iwai5d417622011-06-20 11:32:27 +02002464static int via_init(struct hda_codec *codec)
2465{
2466 struct via_spec *spec = codec->spec;
2467 int i;
2468
2469 for (i = 0; i < spec->num_iverbs; i++)
2470 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2471
Joseph Chanc577b8a2006-11-29 15:29:40 +01002472 via_auto_init_multi_out(codec);
2473 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002474 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002475 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002476 via_auto_init_dig_outs(codec);
2477 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002478
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002479 via_auto_init_unsol_event(codec);
2480
2481 via_hp_automute(codec);
2482 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002483
Joseph Chanc577b8a2006-11-29 15:29:40 +01002484 return 0;
2485}
2486
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002487static void vt1708_update_hp_jack_state(struct work_struct *work)
2488{
2489 struct via_spec *spec = container_of(work, struct via_spec,
2490 vt1708_hp_work.work);
2491 if (spec->codec_type != VT1708)
2492 return;
2493 /* if jack state toggled */
2494 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002495 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002496 spec->vt1708_hp_present ^= 1;
2497 via_hp_automute(spec->codec);
2498 }
2499 vt1708_start_hp_work(spec);
2500}
2501
Takashi Iwai337b9d02009-07-07 18:18:59 +02002502static int get_mux_nids(struct hda_codec *codec)
2503{
2504 struct via_spec *spec = codec->spec;
2505 hda_nid_t nid, conn[8];
2506 unsigned int type;
2507 int i, n;
2508
2509 for (i = 0; i < spec->num_adc_nids; i++) {
2510 nid = spec->adc_nids[i];
2511 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002512 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002513 if (type == AC_WID_PIN)
2514 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002515 n = snd_hda_get_connections(codec, nid, conn,
2516 ARRAY_SIZE(conn));
2517 if (n <= 0)
2518 break;
2519 if (n > 1) {
2520 spec->mux_nids[i] = nid;
2521 break;
2522 }
2523 nid = conn[0];
2524 }
2525 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002526 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002527}
2528
Joseph Chanc577b8a2006-11-29 15:29:40 +01002529static int patch_vt1708(struct hda_codec *codec)
2530{
2531 struct via_spec *spec;
2532 int err;
2533
2534 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002535 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002536 if (spec == NULL)
2537 return -ENOMEM;
2538
Takashi Iwai620e2b22011-06-17 17:19:19 +02002539 spec->aa_mix_nid = 0x17;
2540
Takashi Iwai12daef62011-06-18 17:45:49 +02002541 /* Add HP and CD pin config connect bit re-config action */
2542 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2543 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2544
Joseph Chanc577b8a2006-11-29 15:29:40 +01002545 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002546 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002547 if (err < 0) {
2548 via_free(codec);
2549 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002550 }
2551
Takashi Iwai12daef62011-06-18 17:45:49 +02002552 /* add jack detect on/off control */
2553 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2554 return -ENOMEM;
2555
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002556 /* disable 32bit format on VT1708 */
2557 if (codec->vendor_id == 0x11061708)
2558 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002559
Lydia Wange322a362011-06-29 13:52:02 +08002560 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2561
Joseph Chanc577b8a2006-11-29 15:29:40 +01002562 codec->patch_ops = via_patch_ops;
2563
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002564 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002565 return 0;
2566}
2567
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002568static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002569{
2570 struct via_spec *spec;
2571 int err;
2572
2573 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002574 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002575 if (spec == NULL)
2576 return -ENOMEM;
2577
Takashi Iwai620e2b22011-06-17 17:19:19 +02002578 spec->aa_mix_nid = 0x18;
2579
Takashi Iwai12daef62011-06-18 17:45:49 +02002580 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002581 if (err < 0) {
2582 via_free(codec);
2583 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002584 }
2585
Joseph Chanc577b8a2006-11-29 15:29:40 +01002586 codec->patch_ops = via_patch_ops;
2587
Josepch Chanf7278fd2007-12-13 16:40:40 +01002588 return 0;
2589}
2590
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002591static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2592{
2593 struct via_spec *spec = codec->spec;
2594 int imux_is_smixer;
2595 unsigned int parm;
2596 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002597 if ((spec->codec_type != VT1708B_4CH) &&
2598 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002599 is_8ch = 1;
2600
2601 /* SW0 (17h) = stereo mixer */
2602 imux_is_smixer =
2603 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2604 == ((spec->codec_type == VT1708S) ? 5 : 0));
2605 /* inputs */
2606 /* PW 1/2/5 (1ah/1bh/1eh) */
2607 parm = AC_PWRST_D3;
2608 set_pin_power_state(codec, 0x1a, &parm);
2609 set_pin_power_state(codec, 0x1b, &parm);
2610 set_pin_power_state(codec, 0x1e, &parm);
2611 if (imux_is_smixer)
2612 parm = AC_PWRST_D0;
2613 /* SW0 (17h), AIW 0/1 (13h/14h) */
2614 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2615 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2616 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2617
2618 /* outputs */
2619 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2620 parm = AC_PWRST_D3;
2621 set_pin_power_state(codec, 0x19, &parm);
2622 if (spec->smart51_enabled)
2623 set_pin_power_state(codec, 0x1b, &parm);
2624 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2625 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2626
2627 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2628 if (is_8ch) {
2629 parm = AC_PWRST_D3;
2630 set_pin_power_state(codec, 0x22, &parm);
2631 if (spec->smart51_enabled)
2632 set_pin_power_state(codec, 0x1a, &parm);
2633 snd_hda_codec_write(codec, 0x26, 0,
2634 AC_VERB_SET_POWER_STATE, parm);
2635 snd_hda_codec_write(codec, 0x24, 0,
2636 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002637 } else if (codec->vendor_id == 0x11064397) {
2638 /* PW7(23h), SW2(27h), AOW2(25h) */
2639 parm = AC_PWRST_D3;
2640 set_pin_power_state(codec, 0x23, &parm);
2641 if (spec->smart51_enabled)
2642 set_pin_power_state(codec, 0x1a, &parm);
2643 snd_hda_codec_write(codec, 0x27, 0,
2644 AC_VERB_SET_POWER_STATE, parm);
2645 snd_hda_codec_write(codec, 0x25, 0,
2646 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002647 }
2648
2649 /* PW 3/4/7 (1ch/1dh/23h) */
2650 parm = AC_PWRST_D3;
2651 /* force to D0 for internal Speaker */
2652 set_pin_power_state(codec, 0x1c, &parm);
2653 set_pin_power_state(codec, 0x1d, &parm);
2654 if (is_8ch)
2655 set_pin_power_state(codec, 0x23, &parm);
2656
2657 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2658 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2659 imux_is_smixer ? AC_PWRST_D0 : parm);
2660 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2661 if (is_8ch) {
2662 snd_hda_codec_write(codec, 0x25, 0,
2663 AC_VERB_SET_POWER_STATE, parm);
2664 snd_hda_codec_write(codec, 0x27, 0,
2665 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002666 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2667 snd_hda_codec_write(codec, 0x25, 0,
2668 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002669}
2670
Lydia Wang518bf3b2009-10-10 19:07:29 +08002671static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002672static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002673{
2674 struct via_spec *spec;
2675 int err;
2676
Lydia Wang518bf3b2009-10-10 19:07:29 +08002677 if (get_codec_type(codec) == VT1708BCE)
2678 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002679
Josepch Chanf7278fd2007-12-13 16:40:40 +01002680 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002681 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002682 if (spec == NULL)
2683 return -ENOMEM;
2684
Takashi Iwai620e2b22011-06-17 17:19:19 +02002685 spec->aa_mix_nid = 0x16;
2686
Josepch Chanf7278fd2007-12-13 16:40:40 +01002687 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002688 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002689 if (err < 0) {
2690 via_free(codec);
2691 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002692 }
2693
Josepch Chanf7278fd2007-12-13 16:40:40 +01002694 codec->patch_ops = via_patch_ops;
2695
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002696 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2697
Josepch Chanf7278fd2007-12-13 16:40:40 +01002698 return 0;
2699}
2700
Harald Welted949cac2008-09-09 15:56:01 +08002701/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002702static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002703 /* Enable Mic Boost Volume backdoor */
2704 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002705 /* don't bybass mixer */
2706 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002707 { }
2708};
2709
Takashi Iwai9da29272009-05-07 16:31:14 +02002710/* fill out digital output widgets; one for master and one for slave outputs */
2711static void fill_dig_outs(struct hda_codec *codec)
2712{
2713 struct via_spec *spec = codec->spec;
2714 int i;
2715
2716 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2717 hda_nid_t nid;
2718 int conn;
2719
2720 nid = spec->autocfg.dig_out_pins[i];
2721 if (!nid)
2722 continue;
2723 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2724 if (conn < 1)
2725 continue;
2726 if (!spec->multiout.dig_out_nid)
2727 spec->multiout.dig_out_nid = nid;
2728 else {
2729 spec->slave_dig_outs[0] = nid;
2730 break; /* at most two dig outs */
2731 }
2732 }
2733}
2734
Takashi Iwai12daef62011-06-18 17:45:49 +02002735static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002736{
2737 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002738 hda_nid_t dig_nid;
2739 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002740
Takashi Iwai12daef62011-06-18 17:45:49 +02002741 if (!spec->autocfg.dig_in_pin)
2742 return;
Harald Welted949cac2008-09-09 15:56:01 +08002743
Takashi Iwai12daef62011-06-18 17:45:49 +02002744 dig_nid = codec->start_nid;
2745 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2746 unsigned int wcaps = get_wcaps(codec, dig_nid);
2747 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2748 continue;
2749 if (!(wcaps & AC_WCAP_DIGITAL))
2750 continue;
2751 if (!(wcaps & AC_WCAP_CONN_LIST))
2752 continue;
2753 err = get_connection_index(codec, dig_nid,
2754 spec->autocfg.dig_in_pin);
2755 if (err >= 0) {
2756 spec->dig_in_nid = dig_nid;
2757 break;
2758 }
2759 }
Harald Welted949cac2008-09-09 15:56:01 +08002760}
2761
Lydia Wang6369bcf2009-10-10 19:08:31 +08002762static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2763 int offset, int num_steps, int step_size)
2764{
2765 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2766 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2767 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2768 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2769 (0 << AC_AMPCAP_MUTE_SHIFT));
2770}
2771
Harald Welted949cac2008-09-09 15:56:01 +08002772static int patch_vt1708S(struct hda_codec *codec)
2773{
2774 struct via_spec *spec;
2775 int err;
2776
2777 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002778 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002779 if (spec == NULL)
2780 return -ENOMEM;
2781
Takashi Iwai620e2b22011-06-17 17:19:19 +02002782 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002783 override_mic_boost(codec, 0x1a, 0, 3, 40);
2784 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002785
Harald Welted949cac2008-09-09 15:56:01 +08002786 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002787 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002788 if (err < 0) {
2789 via_free(codec);
2790 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002791 }
2792
Takashi Iwai096a8852011-06-20 12:09:02 +02002793 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002794
Harald Welted949cac2008-09-09 15:56:01 +08002795 codec->patch_ops = via_patch_ops;
2796
Lydia Wang518bf3b2009-10-10 19:07:29 +08002797 /* correct names for VT1708BCE */
2798 if (get_codec_type(codec) == VT1708BCE) {
2799 kfree(codec->chip_name);
2800 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2801 snprintf(codec->bus->card->mixername,
2802 sizeof(codec->bus->card->mixername),
2803 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002804 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002805 /* correct names for VT1705 */
2806 if (codec->vendor_id == 0x11064397) {
2807 kfree(codec->chip_name);
2808 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2809 snprintf(codec->bus->card->mixername,
2810 sizeof(codec->bus->card->mixername),
2811 "%s %s", codec->vendor_name, codec->chip_name);
2812 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002813 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002814 return 0;
2815}
2816
2817/* Patch for VT1702 */
2818
Takashi Iwai096a8852011-06-20 12:09:02 +02002819static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002820 /* mixer enable */
2821 {0x1, 0xF88, 0x3},
2822 /* GPIO 0~2 */
2823 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002824 { }
2825};
2826
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002827static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2828{
2829 int imux_is_smixer =
2830 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2831 unsigned int parm;
2832 /* inputs */
2833 /* PW 1/2/5 (14h/15h/18h) */
2834 parm = AC_PWRST_D3;
2835 set_pin_power_state(codec, 0x14, &parm);
2836 set_pin_power_state(codec, 0x15, &parm);
2837 set_pin_power_state(codec, 0x18, &parm);
2838 if (imux_is_smixer)
2839 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2840 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2841 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2842 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2843 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2844 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2845
2846 /* outputs */
2847 /* PW 3/4 (16h/17h) */
2848 parm = AC_PWRST_D3;
2849 set_pin_power_state(codec, 0x17, &parm);
2850 set_pin_power_state(codec, 0x16, &parm);
2851 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2852 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2853 imux_is_smixer ? AC_PWRST_D0 : parm);
2854 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2855 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2856}
2857
Harald Welted949cac2008-09-09 15:56:01 +08002858static int patch_vt1702(struct hda_codec *codec)
2859{
2860 struct via_spec *spec;
2861 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002862
2863 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002864 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002865 if (spec == NULL)
2866 return -ENOMEM;
2867
Takashi Iwai620e2b22011-06-17 17:19:19 +02002868 spec->aa_mix_nid = 0x1a;
2869
Takashi Iwai12daef62011-06-18 17:45:49 +02002870 /* limit AA path volume to 0 dB */
2871 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2872 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2873 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2874 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2875 (1 << AC_AMPCAP_MUTE_SHIFT));
2876
Harald Welted949cac2008-09-09 15:56:01 +08002877 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002878 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002879 if (err < 0) {
2880 via_free(codec);
2881 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002882 }
2883
Takashi Iwai096a8852011-06-20 12:09:02 +02002884 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002885
Harald Welted949cac2008-09-09 15:56:01 +08002886 codec->patch_ops = via_patch_ops;
2887
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002888 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002889 return 0;
2890}
2891
Lydia Wangeb7188c2009-10-10 19:08:34 +08002892/* Patch for VT1718S */
2893
Takashi Iwai096a8852011-06-20 12:09:02 +02002894static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002895 /* Enable MW0 adjust Gain 5 */
2896 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002897 /* Enable Boost Volume backdoor */
2898 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002899
Lydia Wangeb7188c2009-10-10 19:08:34 +08002900 { }
2901};
2902
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002903static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2904{
2905 struct via_spec *spec = codec->spec;
2906 int imux_is_smixer;
2907 unsigned int parm;
2908 /* MUX6 (1eh) = stereo mixer */
2909 imux_is_smixer =
2910 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2911 /* inputs */
2912 /* PW 5/6/7 (29h/2ah/2bh) */
2913 parm = AC_PWRST_D3;
2914 set_pin_power_state(codec, 0x29, &parm);
2915 set_pin_power_state(codec, 0x2a, &parm);
2916 set_pin_power_state(codec, 0x2b, &parm);
2917 if (imux_is_smixer)
2918 parm = AC_PWRST_D0;
2919 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2920 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2921 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2922 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2923 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2924
2925 /* outputs */
2926 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2927 parm = AC_PWRST_D3;
2928 set_pin_power_state(codec, 0x27, &parm);
2929 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2930 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2931
2932 /* PW2 (26h), AOW2 (ah) */
2933 parm = AC_PWRST_D3;
2934 set_pin_power_state(codec, 0x26, &parm);
2935 if (spec->smart51_enabled)
2936 set_pin_power_state(codec, 0x2b, &parm);
2937 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2938
2939 /* PW0 (24h), AOW0 (8h) */
2940 parm = AC_PWRST_D3;
2941 set_pin_power_state(codec, 0x24, &parm);
2942 if (!spec->hp_independent_mode) /* check for redirected HP */
2943 set_pin_power_state(codec, 0x28, &parm);
2944 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2945 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2946 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2947 imux_is_smixer ? AC_PWRST_D0 : parm);
2948
2949 /* PW1 (25h), AOW1 (9h) */
2950 parm = AC_PWRST_D3;
2951 set_pin_power_state(codec, 0x25, &parm);
2952 if (spec->smart51_enabled)
2953 set_pin_power_state(codec, 0x2a, &parm);
2954 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2955
2956 if (spec->hp_independent_mode) {
2957 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2958 parm = AC_PWRST_D3;
2959 set_pin_power_state(codec, 0x28, &parm);
2960 snd_hda_codec_write(codec, 0x1b, 0,
2961 AC_VERB_SET_POWER_STATE, parm);
2962 snd_hda_codec_write(codec, 0x34, 0,
2963 AC_VERB_SET_POWER_STATE, parm);
2964 snd_hda_codec_write(codec, 0xc, 0,
2965 AC_VERB_SET_POWER_STATE, parm);
2966 }
2967}
2968
Lydia Wangeb7188c2009-10-10 19:08:34 +08002969static int patch_vt1718S(struct hda_codec *codec)
2970{
2971 struct via_spec *spec;
2972 int err;
2973
2974 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002975 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002976 if (spec == NULL)
2977 return -ENOMEM;
2978
Takashi Iwai620e2b22011-06-17 17:19:19 +02002979 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002980 override_mic_boost(codec, 0x2b, 0, 3, 40);
2981 override_mic_boost(codec, 0x29, 0, 3, 40);
Lydia Wangc4394f52011-07-04 16:54:15 +08002982 spec->dac_mixer_idx = 5;
Takashi Iwai620e2b22011-06-17 17:19:19 +02002983
Lydia Wangeb7188c2009-10-10 19:08:34 +08002984 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002985 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002986 if (err < 0) {
2987 via_free(codec);
2988 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002989 }
2990
Takashi Iwai096a8852011-06-20 12:09:02 +02002991 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002992
Lydia Wangeb7188c2009-10-10 19:08:34 +08002993 codec->patch_ops = via_patch_ops;
2994
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002995 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2996
Lydia Wangeb7188c2009-10-10 19:08:34 +08002997 return 0;
2998}
Lydia Wangf3db4232009-10-10 19:08:41 +08002999
3000/* Patch for VT1716S */
3001
3002static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3003 struct snd_ctl_elem_info *uinfo)
3004{
3005 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3006 uinfo->count = 1;
3007 uinfo->value.integer.min = 0;
3008 uinfo->value.integer.max = 1;
3009 return 0;
3010}
3011
3012static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3013 struct snd_ctl_elem_value *ucontrol)
3014{
3015 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3016 int index = 0;
3017
3018 index = snd_hda_codec_read(codec, 0x26, 0,
3019 AC_VERB_GET_CONNECT_SEL, 0);
3020 if (index != -1)
3021 *ucontrol->value.integer.value = index;
3022
3023 return 0;
3024}
3025
3026static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3027 struct snd_ctl_elem_value *ucontrol)
3028{
3029 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3030 struct via_spec *spec = codec->spec;
3031 int index = *ucontrol->value.integer.value;
3032
3033 snd_hda_codec_write(codec, 0x26, 0,
3034 AC_VERB_SET_CONNECT_SEL, index);
3035 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003036 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003037 return 1;
3038}
3039
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003040static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003041 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3042 {
3043 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3044 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003045 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003046 .count = 1,
3047 .info = vt1716s_dmic_info,
3048 .get = vt1716s_dmic_get,
3049 .put = vt1716s_dmic_put,
3050 },
3051 {} /* end */
3052};
3053
3054
3055/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003056static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003057 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3058 { } /* end */
3059};
3060
Takashi Iwai096a8852011-06-20 12:09:02 +02003061static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003062 /* Enable Boost Volume backdoor */
3063 {0x1, 0xf8a, 0x80},
3064 /* don't bybass mixer */
3065 {0x1, 0xf88, 0xc0},
3066 /* Enable mono output */
3067 {0x1, 0xf90, 0x08},
3068 { }
3069};
3070
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003071static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3072{
3073 struct via_spec *spec = codec->spec;
3074 int imux_is_smixer;
3075 unsigned int parm;
3076 unsigned int mono_out, present;
3077 /* SW0 (17h) = stereo mixer */
3078 imux_is_smixer =
3079 (snd_hda_codec_read(codec, 0x17, 0,
3080 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3081 /* inputs */
3082 /* PW 1/2/5 (1ah/1bh/1eh) */
3083 parm = AC_PWRST_D3;
3084 set_pin_power_state(codec, 0x1a, &parm);
3085 set_pin_power_state(codec, 0x1b, &parm);
3086 set_pin_power_state(codec, 0x1e, &parm);
3087 if (imux_is_smixer)
3088 parm = AC_PWRST_D0;
3089 /* SW0 (17h), AIW0(13h) */
3090 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3091 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3092
3093 parm = AC_PWRST_D3;
3094 set_pin_power_state(codec, 0x1e, &parm);
3095 /* PW11 (22h) */
3096 if (spec->dmic_enabled)
3097 set_pin_power_state(codec, 0x22, &parm);
3098 else
3099 snd_hda_codec_write(codec, 0x22, 0,
3100 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3101
3102 /* SW2(26h), AIW1(14h) */
3103 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3104 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3105
3106 /* outputs */
3107 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3108 parm = AC_PWRST_D3;
3109 set_pin_power_state(codec, 0x19, &parm);
3110 /* Smart 5.1 PW2(1bh) */
3111 if (spec->smart51_enabled)
3112 set_pin_power_state(codec, 0x1b, &parm);
3113 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3114 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3115
3116 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3117 parm = AC_PWRST_D3;
3118 set_pin_power_state(codec, 0x23, &parm);
3119 /* Smart 5.1 PW1(1ah) */
3120 if (spec->smart51_enabled)
3121 set_pin_power_state(codec, 0x1a, &parm);
3122 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3123
3124 /* Smart 5.1 PW5(1eh) */
3125 if (spec->smart51_enabled)
3126 set_pin_power_state(codec, 0x1e, &parm);
3127 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3128
3129 /* Mono out */
3130 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3131 present = snd_hda_jack_detect(codec, 0x1c);
3132
3133 if (present)
3134 mono_out = 0;
3135 else {
3136 present = snd_hda_jack_detect(codec, 0x1d);
3137 if (!spec->hp_independent_mode && present)
3138 mono_out = 0;
3139 else
3140 mono_out = 1;
3141 }
3142 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3143 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3144 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3145 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3146
3147 /* PW 3/4 (1ch/1dh) */
3148 parm = AC_PWRST_D3;
3149 set_pin_power_state(codec, 0x1c, &parm);
3150 set_pin_power_state(codec, 0x1d, &parm);
3151 /* HP Independent Mode, power on AOW3 */
3152 if (spec->hp_independent_mode)
3153 snd_hda_codec_write(codec, 0x25, 0,
3154 AC_VERB_SET_POWER_STATE, parm);
3155
3156 /* force to D0 for internal Speaker */
3157 /* MW0 (16h), AOW0 (10h) */
3158 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3159 imux_is_smixer ? AC_PWRST_D0 : parm);
3160 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3161 mono_out ? AC_PWRST_D0 : parm);
3162}
3163
Lydia Wangf3db4232009-10-10 19:08:41 +08003164static int patch_vt1716S(struct hda_codec *codec)
3165{
3166 struct via_spec *spec;
3167 int err;
3168
3169 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003170 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003171 if (spec == NULL)
3172 return -ENOMEM;
3173
Takashi Iwai620e2b22011-06-17 17:19:19 +02003174 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003175 override_mic_boost(codec, 0x1a, 0, 3, 40);
3176 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003177
Lydia Wangf3db4232009-10-10 19:08:41 +08003178 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003179 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003180 if (err < 0) {
3181 via_free(codec);
3182 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003183 }
3184
Takashi Iwai096a8852011-06-20 12:09:02 +02003185 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003186
Lydia Wangf3db4232009-10-10 19:08:41 +08003187 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3188 spec->num_mixers++;
3189
3190 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3191
3192 codec->patch_ops = via_patch_ops;
3193
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003194 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003195 return 0;
3196}
Lydia Wang25eaba22009-10-10 19:08:43 +08003197
3198/* for vt2002P */
3199
Takashi Iwai096a8852011-06-20 12:09:02 +02003200static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003201 /* Class-D speaker related verbs */
3202 {0x1, 0xfe0, 0x4},
3203 {0x1, 0xfe9, 0x80},
3204 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003205 /* Enable Boost Volume backdoor */
3206 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003207 /* Enable AOW0 to MW9 */
3208 {0x1, 0xfb8, 0x88},
3209 { }
3210};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003211
Takashi Iwai096a8852011-06-20 12:09:02 +02003212static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003213 /* Enable Boost Volume backdoor */
3214 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003215 /* Enable AOW0 to MW9 */
3216 {0x1, 0xfb8, 0x88},
3217 { }
3218};
Lydia Wang25eaba22009-10-10 19:08:43 +08003219
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003220static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3221{
3222 struct via_spec *spec = codec->spec;
3223 int imux_is_smixer;
3224 unsigned int parm;
3225 unsigned int present;
3226 /* MUX9 (1eh) = stereo mixer */
3227 imux_is_smixer =
3228 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3229 /* inputs */
3230 /* PW 5/6/7 (29h/2ah/2bh) */
3231 parm = AC_PWRST_D3;
3232 set_pin_power_state(codec, 0x29, &parm);
3233 set_pin_power_state(codec, 0x2a, &parm);
3234 set_pin_power_state(codec, 0x2b, &parm);
3235 parm = AC_PWRST_D0;
3236 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3237 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3238 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3239 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3240 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3241
3242 /* outputs */
3243 /* AOW0 (8h)*/
3244 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3245
Lydia Wang118909562011-03-23 17:57:34 +08003246 if (spec->codec_type == VT1802) {
3247 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3248 parm = AC_PWRST_D3;
3249 set_pin_power_state(codec, 0x28, &parm);
3250 snd_hda_codec_write(codec, 0x18, 0,
3251 AC_VERB_SET_POWER_STATE, parm);
3252 snd_hda_codec_write(codec, 0x38, 0,
3253 AC_VERB_SET_POWER_STATE, parm);
3254 } else {
3255 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3256 parm = AC_PWRST_D3;
3257 set_pin_power_state(codec, 0x26, &parm);
3258 snd_hda_codec_write(codec, 0x1c, 0,
3259 AC_VERB_SET_POWER_STATE, parm);
3260 snd_hda_codec_write(codec, 0x37, 0,
3261 AC_VERB_SET_POWER_STATE, parm);
3262 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003263
Lydia Wang118909562011-03-23 17:57:34 +08003264 if (spec->codec_type == VT1802) {
3265 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3266 parm = AC_PWRST_D3;
3267 set_pin_power_state(codec, 0x25, &parm);
3268 snd_hda_codec_write(codec, 0x15, 0,
3269 AC_VERB_SET_POWER_STATE, parm);
3270 snd_hda_codec_write(codec, 0x35, 0,
3271 AC_VERB_SET_POWER_STATE, parm);
3272 } else {
3273 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3274 parm = AC_PWRST_D3;
3275 set_pin_power_state(codec, 0x25, &parm);
3276 snd_hda_codec_write(codec, 0x19, 0,
3277 AC_VERB_SET_POWER_STATE, parm);
3278 snd_hda_codec_write(codec, 0x35, 0,
3279 AC_VERB_SET_POWER_STATE, parm);
3280 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003281
3282 if (spec->hp_independent_mode)
3283 snd_hda_codec_write(codec, 0x9, 0,
3284 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3285
3286 /* Class-D */
3287 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3288 present = snd_hda_jack_detect(codec, 0x25);
3289
3290 parm = AC_PWRST_D3;
3291 set_pin_power_state(codec, 0x24, &parm);
3292 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003293 if (spec->codec_type == VT1802)
3294 snd_hda_codec_write(codec, 0x14, 0,
3295 AC_VERB_SET_POWER_STATE, parm);
3296 else
3297 snd_hda_codec_write(codec, 0x18, 0,
3298 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003299 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3300
3301 /* Mono Out */
3302 present = snd_hda_jack_detect(codec, 0x26);
3303
3304 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003305 if (spec->codec_type == VT1802) {
3306 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3307 snd_hda_codec_write(codec, 0x33, 0,
3308 AC_VERB_SET_POWER_STATE, parm);
3309 snd_hda_codec_write(codec, 0x1c, 0,
3310 AC_VERB_SET_POWER_STATE, parm);
3311 snd_hda_codec_write(codec, 0x3c, 0,
3312 AC_VERB_SET_POWER_STATE, parm);
3313 } else {
3314 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3315 snd_hda_codec_write(codec, 0x31, 0,
3316 AC_VERB_SET_POWER_STATE, parm);
3317 snd_hda_codec_write(codec, 0x17, 0,
3318 AC_VERB_SET_POWER_STATE, parm);
3319 snd_hda_codec_write(codec, 0x3b, 0,
3320 AC_VERB_SET_POWER_STATE, parm);
3321 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003322 /* MW9 (21h) */
3323 if (imux_is_smixer || !is_aa_path_mute(codec))
3324 snd_hda_codec_write(codec, 0x21, 0,
3325 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3326 else
3327 snd_hda_codec_write(codec, 0x21, 0,
3328 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3329}
Lydia Wang25eaba22009-10-10 19:08:43 +08003330
3331/* patch for vt2002P */
3332static int patch_vt2002P(struct hda_codec *codec)
3333{
3334 struct via_spec *spec;
3335 int err;
3336
3337 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003338 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003339 if (spec == NULL)
3340 return -ENOMEM;
3341
Takashi Iwai620e2b22011-06-17 17:19:19 +02003342 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003343 override_mic_boost(codec, 0x2b, 0, 3, 40);
3344 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003345
Lydia Wang25eaba22009-10-10 19:08:43 +08003346 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003347 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003348 if (err < 0) {
3349 via_free(codec);
3350 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003351 }
3352
Lydia Wang118909562011-03-23 17:57:34 +08003353 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003354 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003355 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003356 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003357
Lydia Wang25eaba22009-10-10 19:08:43 +08003358 codec->patch_ops = via_patch_ops;
3359
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003360 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003361 return 0;
3362}
Lydia Wangab6734e2009-10-10 19:08:46 +08003363
3364/* for vt1812 */
3365
Takashi Iwai096a8852011-06-20 12:09:02 +02003366static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003367 /* Enable Boost Volume backdoor */
3368 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003369 /* Enable AOW0 to MW9 */
3370 {0x1, 0xfb8, 0xa8},
3371 { }
3372};
3373
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003374static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3375{
3376 struct via_spec *spec = codec->spec;
3377 int imux_is_smixer =
3378 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3379 unsigned int parm;
3380 unsigned int present;
3381 /* MUX10 (1eh) = stereo mixer */
3382 imux_is_smixer =
3383 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3384 /* inputs */
3385 /* PW 5/6/7 (29h/2ah/2bh) */
3386 parm = AC_PWRST_D3;
3387 set_pin_power_state(codec, 0x29, &parm);
3388 set_pin_power_state(codec, 0x2a, &parm);
3389 set_pin_power_state(codec, 0x2b, &parm);
3390 parm = AC_PWRST_D0;
3391 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3392 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3393 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3394 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3395 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3396
3397 /* outputs */
3398 /* AOW0 (8h)*/
3399 snd_hda_codec_write(codec, 0x8, 0,
3400 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3401
3402 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3403 parm = AC_PWRST_D3;
3404 set_pin_power_state(codec, 0x28, &parm);
3405 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3406 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3407
3408 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3409 parm = AC_PWRST_D3;
3410 set_pin_power_state(codec, 0x25, &parm);
3411 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3412 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3413 if (spec->hp_independent_mode)
3414 snd_hda_codec_write(codec, 0x9, 0,
3415 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3416
3417 /* Internal Speaker */
3418 /* PW0 (24h), MW0(14h), MUX0(34h) */
3419 present = snd_hda_jack_detect(codec, 0x25);
3420
3421 parm = AC_PWRST_D3;
3422 set_pin_power_state(codec, 0x24, &parm);
3423 if (present) {
3424 snd_hda_codec_write(codec, 0x14, 0,
3425 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3426 snd_hda_codec_write(codec, 0x34, 0,
3427 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3428 } else {
3429 snd_hda_codec_write(codec, 0x14, 0,
3430 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3431 snd_hda_codec_write(codec, 0x34, 0,
3432 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3433 }
3434
3435
3436 /* Mono Out */
3437 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3438 present = snd_hda_jack_detect(codec, 0x28);
3439
3440 parm = AC_PWRST_D3;
3441 set_pin_power_state(codec, 0x31, &parm);
3442 if (present) {
3443 snd_hda_codec_write(codec, 0x1c, 0,
3444 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3445 snd_hda_codec_write(codec, 0x3c, 0,
3446 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3447 snd_hda_codec_write(codec, 0x3e, 0,
3448 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3449 } else {
3450 snd_hda_codec_write(codec, 0x1c, 0,
3451 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3452 snd_hda_codec_write(codec, 0x3c, 0,
3453 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3454 snd_hda_codec_write(codec, 0x3e, 0,
3455 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3456 }
3457
3458 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3459 parm = AC_PWRST_D3;
3460 set_pin_power_state(codec, 0x33, &parm);
3461 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3462 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3463
3464}
Lydia Wangab6734e2009-10-10 19:08:46 +08003465
3466/* patch for vt1812 */
3467static int patch_vt1812(struct hda_codec *codec)
3468{
3469 struct via_spec *spec;
3470 int err;
3471
3472 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003473 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003474 if (spec == NULL)
3475 return -ENOMEM;
3476
Takashi Iwai620e2b22011-06-17 17:19:19 +02003477 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003478 override_mic_boost(codec, 0x2b, 0, 3, 40);
3479 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003480
Lydia Wangab6734e2009-10-10 19:08:46 +08003481 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003482 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003483 if (err < 0) {
3484 via_free(codec);
3485 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003486 }
3487
Takashi Iwai096a8852011-06-20 12:09:02 +02003488 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003489
Lydia Wangab6734e2009-10-10 19:08:46 +08003490 codec->patch_ops = via_patch_ops;
3491
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003492 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003493 return 0;
3494}
3495
Joseph Chanc577b8a2006-11-29 15:29:40 +01003496/*
3497 * patch entries
3498 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003499static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003500 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3501 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3502 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3503 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3504 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003505 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003506 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003507 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003508 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003509 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003510 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003511 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003512 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003513 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003514 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003515 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003516 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003517 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003518 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003519 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003520 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003521 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003522 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003523 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003524 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003525 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003526 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003527 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003528 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003529 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003530 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003531 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003532 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003533 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003534 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003535 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003536 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003537 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003538 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003539 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003540 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003541 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003542 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003543 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003544 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003545 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003546 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003547 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003548 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003549 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003550 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003551 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003552 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003553 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003554 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003555 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003556 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003557 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003558 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003559 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003560 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003561 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003562 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003563 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003564 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003565 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003566 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003567 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003568 { .id = 0x11060428, .name = "VT1718S",
3569 .patch = patch_vt1718S},
3570 { .id = 0x11064428, .name = "VT1718S",
3571 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003572 { .id = 0x11060441, .name = "VT2020",
3573 .patch = patch_vt1718S},
3574 { .id = 0x11064441, .name = "VT1828S",
3575 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003576 { .id = 0x11060433, .name = "VT1716S",
3577 .patch = patch_vt1716S},
3578 { .id = 0x1106a721, .name = "VT1716S",
3579 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003580 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3581 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003582 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003583 { .id = 0x11060440, .name = "VT1818S",
3584 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003585 { .id = 0x11060446, .name = "VT1802",
3586 .patch = patch_vt2002P},
3587 { .id = 0x11068446, .name = "VT1802",
3588 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003589 {} /* terminator */
3590};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003591
3592MODULE_ALIAS("snd-hda-codec-id:1106*");
3593
3594static struct hda_codec_preset_list via_list = {
3595 .preset = snd_hda_preset_via,
3596 .owner = THIS_MODULE,
3597};
3598
3599MODULE_LICENSE("GPL");
3600MODULE_DESCRIPTION("VIA HD-audio codec");
3601
3602static int __init patch_via_init(void)
3603{
3604 return snd_hda_add_codec_preset(&via_list);
3605}
3606
3607static void __exit patch_via_exit(void)
3608{
3609 snd_hda_delete_codec_preset(&via_list);
3610}
3611
3612module_init(patch_via_init)
3613module_exit(patch_via_exit)