blob: 819267a4e2df72bff669292afe1eb0a002525f09 [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 Iwai4a796162011-06-17 17:53:38 +020084struct nid_path {
85 int depth;
86 hda_nid_t path[5];
87 short idx[5];
88};
89
Lydia Wang1f2e99f2009-10-10 19:08:17 +080090struct via_spec {
91 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +020092 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +080093 unsigned int num_mixers;
94
Takashi Iwai90dd48a2011-05-02 12:38:19 +020095 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +080096 unsigned int num_iverbs;
97
Takashi Iwai82673bc2011-06-17 16:24:21 +020098 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +020099 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200100 const struct hda_pcm_stream *stream_analog_playback;
101 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800102
Takashi Iwai82673bc2011-06-17 16:24:21 +0200103 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200104 const struct hda_pcm_stream *stream_digital_playback;
105 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800106
107 /* playback */
108 struct hda_multi_out multiout;
109 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200110 hda_nid_t hp_dac_nid;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200111 int num_active_streams;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800112
Takashi Iwai4a796162011-06-17 17:53:38 +0200113 struct nid_path out_path[4];
114 struct nid_path hp_path;
115 struct nid_path hp_dep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200116 struct nid_path speaker_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200117
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800118 /* capture */
119 unsigned int num_adc_nids;
Takashi Iwaia766d0d2011-06-17 09:01:29 +0200120 hda_nid_t adc_nids[3];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800121 hda_nid_t mux_nids[3];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200122 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800123 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800124
125 /* capture source */
126 const struct hda_input_mux *input_mux;
127 unsigned int cur_mux[3];
128
129 /* PCM information */
130 struct hda_pcm pcm_rec[3];
131
132 /* dynamic controls, init_verbs and input_mux */
133 struct auto_pin_cfg autocfg;
134 struct snd_array kctls;
135 struct hda_input_mux private_imux[2];
136 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
137
138 /* HP mode source */
139 const struct hda_input_mux *hp_mux;
140 unsigned int hp_independent_mode;
141 unsigned int hp_independent_mode_index;
Lydia Wangf3db4232009-10-10 19:08:41 +0800142 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200143 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800144 enum VIA_HDA_CODEC codec_type;
145
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200146 /* smart51 setup */
147 unsigned int smart51_nums;
148 hda_nid_t smart51_pins[2];
149 int smart51_idxs[2];
150 const char *smart51_labels[2];
151 unsigned int smart51_enabled;
152
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800153 /* work to check hp jack state */
154 struct hda_codec *codec;
155 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200156 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800157 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800158
159 void (*set_widgets_power_state)(struct hda_codec *codec);
160
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800161 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200162 int num_loopbacks;
163 struct hda_amp_list loopback_list[8];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800164};
165
Lydia Wang0341ccd2011-03-22 16:25:03 +0800166static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100167static struct via_spec * via_new_spec(struct hda_codec *codec)
168{
169 struct via_spec *spec;
170
171 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
172 if (spec == NULL)
173 return NULL;
174
175 codec->spec = spec;
176 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800177 spec->codec_type = get_codec_type(codec);
178 /* VT1708BCE & VT1708S are almost same */
179 if (spec->codec_type == VT1708BCE)
180 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100181 return spec;
182}
183
Lydia Wang744ff5f2009-10-10 19:07:26 +0800184static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800185{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800186 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800187 u16 ven_id = vendor_id >> 16;
188 u16 dev_id = vendor_id & 0xffff;
189 enum VIA_HDA_CODEC codec_type;
190
191 /* get codec type */
192 if (ven_id != 0x1106)
193 codec_type = UNKNOWN;
194 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
195 codec_type = VT1708;
196 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
197 codec_type = VT1709_10CH;
198 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
199 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800200 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800201 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800202 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
203 codec_type = VT1708BCE;
204 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800205 codec_type = VT1708B_4CH;
206 else if ((dev_id & 0xfff) == 0x397
207 && (dev_id >> 12) < 8)
208 codec_type = VT1708S;
209 else if ((dev_id & 0xfff) == 0x398
210 && (dev_id >> 12) < 8)
211 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800212 else if ((dev_id & 0xfff) == 0x428
213 && (dev_id >> 12) < 8)
214 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800215 else if (dev_id == 0x0433 || dev_id == 0xa721)
216 codec_type = VT1716S;
Lydia Wangbb3c6bf2009-10-10 19:08:39 +0800217 else if (dev_id == 0x0441 || dev_id == 0x4441)
218 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800219 else if (dev_id == 0x0438 || dev_id == 0x4438)
220 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800221 else if (dev_id == 0x0448)
222 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800223 else if (dev_id == 0x0440)
224 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800225 else if ((dev_id & 0xfff) == 0x446)
226 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800227 else
228 codec_type = UNKNOWN;
229 return codec_type;
230};
231
Lydia Wangec7e7e42011-03-24 12:43:44 +0800232#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800233#define VIA_HP_EVENT 0x01
234#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200235#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800236
Joseph Chanc577b8a2006-11-29 15:29:40 +0100237enum {
238 VIA_CTL_WIDGET_VOL,
239 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800240 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100241};
242
Takashi Iwaiada509e2011-06-20 15:40:19 +0200243static void analog_low_current_mode(struct hda_codec *codec);
244static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800245
246static void vt1708_start_hp_work(struct via_spec *spec)
247{
248 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
249 return;
250 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200251 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800252 if (!delayed_work_pending(&spec->vt1708_hp_work))
253 schedule_delayed_work(&spec->vt1708_hp_work,
254 msecs_to_jiffies(100));
255}
256
257static void vt1708_stop_hp_work(struct via_spec *spec)
258{
259 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
260 return;
261 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
262 && !is_aa_path_mute(spec->codec))
263 return;
264 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200265 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100266 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800267}
Lydia Wangf5271102009-10-10 19:07:35 +0800268
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800269static void set_widgets_power_state(struct hda_codec *codec)
270{
271 struct via_spec *spec = codec->spec;
272 if (spec->set_widgets_power_state)
273 spec->set_widgets_power_state(codec);
274}
Lydia Wang25eaba22009-10-10 19:08:43 +0800275
Lydia Wangf5271102009-10-10 19:07:35 +0800276static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
277 struct snd_ctl_elem_value *ucontrol)
278{
279 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
280 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
281
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800282 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200283 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800284 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
285 if (is_aa_path_mute(codec))
286 vt1708_start_hp_work(codec->spec);
287 else
288 vt1708_stop_hp_work(codec->spec);
289 }
Lydia Wangf5271102009-10-10 19:07:35 +0800290 return change;
291}
292
293/* modify .put = snd_hda_mixer_amp_switch_put */
294#define ANALOG_INPUT_MUTE \
295 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
296 .name = NULL, \
297 .index = 0, \
298 .info = snd_hda_mixer_amp_switch_info, \
299 .get = snd_hda_mixer_amp_switch_get, \
300 .put = analog_input_switch_put, \
301 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
302
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200303static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100304 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
305 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800306 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100307};
308
Lydia Wangab6734e2009-10-10 19:08:46 +0800309
Joseph Chanc577b8a2006-11-29 15:29:40 +0100310/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200311static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
312 const struct snd_kcontrol_new *tmpl,
313 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100314{
315 struct snd_kcontrol_new *knew;
316
Takashi Iwai603c4012008-07-30 15:01:44 +0200317 snd_array_init(&spec->kctls, sizeof(*knew), 32);
318 knew = snd_array_new(&spec->kctls);
319 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200320 return NULL;
321 *knew = *tmpl;
322 if (!name)
323 name = tmpl->name;
324 if (name) {
325 knew->name = kstrdup(name, GFP_KERNEL);
326 if (!knew->name)
327 return NULL;
328 }
329 return knew;
330}
331
332static int __via_add_control(struct via_spec *spec, int type, const char *name,
333 int idx, unsigned long val)
334{
335 struct snd_kcontrol_new *knew;
336
337 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
338 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100339 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200340 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100341 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100342 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100343 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100344 return 0;
345}
346
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200347#define via_add_control(spec, type, name, val) \
348 __via_add_control(spec, type, name, 0, val)
349
Takashi Iwai291c9e32011-06-17 16:15:26 +0200350#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100351
Takashi Iwai603c4012008-07-30 15:01:44 +0200352static void via_free_kctls(struct hda_codec *codec)
353{
354 struct via_spec *spec = codec->spec;
355
356 if (spec->kctls.list) {
357 struct snd_kcontrol_new *kctl = spec->kctls.list;
358 int i;
359 for (i = 0; i < spec->kctls.used; i++)
360 kfree(kctl[i].name);
361 }
362 snd_array_free(&spec->kctls);
363}
364
Joseph Chanc577b8a2006-11-29 15:29:40 +0100365/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800366static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200367 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100368{
369 char name[32];
370 int err;
371
372 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200373 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100374 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
375 if (err < 0)
376 return err;
377 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200378 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100379 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
380 if (err < 0)
381 return err;
382 return 0;
383}
384
Takashi Iwai5d417622011-06-20 11:32:27 +0200385/* return the index of the given widget nid as the source of mux;
386 * return -1 if not found;
387 * if num_conns is non-NULL, set the total number of connections
388 */
389static int __get_connection_index(struct hda_codec *codec, hda_nid_t mux,
390 hda_nid_t nid, int *num_conns)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100391{
Takashi Iwai5d417622011-06-20 11:32:27 +0200392 hda_nid_t conn[HDA_MAX_NUM_INPUTS];
393 int i, nums;
394
395 nums = snd_hda_get_connections(codec, mux, conn, ARRAY_SIZE(conn));
396 if (num_conns)
397 *num_conns = nums;
398 for (i = 0; i < nums; i++)
399 if (conn[i] == nid)
400 return i;
401 return -1;
402}
403
404#define get_connection_index(codec, mux, nid) \
405 __get_connection_index(codec, mux, nid, NULL)
406
407/* unmute input amp and select the specificed source */
408static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid,
409 hda_nid_t src, hda_nid_t mix)
410{
411 int idx, num_conns;
412
413 idx = __get_connection_index(codec, nid, src, &num_conns);
414 if (idx < 0)
415 return;
416
417 /* select the route explicitly when multiple connections exist */
418 if (num_conns > 1)
Lydia Wang377ff312009-10-10 19:08:55 +0800419 snd_hda_codec_write(codec, nid, 0,
Takashi Iwai5d417622011-06-20 11:32:27 +0200420 AC_VERB_SET_CONNECT_SEL, idx);
421 /* unmute if the input amp is present */
422 if (!(query_amp_caps(codec, nid, HDA_INPUT) &
423 (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)))
424 return;
425 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
426 AMP_IN_UNMUTE(idx));
427
428 /* unmute AA-path if present */
429 if (!mix)
430 return;
431 idx = __get_connection_index(codec, nid, mix, NULL);
432 if (idx >= 0)
433 snd_hda_codec_write(codec, nid, 0,
434 AC_VERB_SET_AMP_GAIN_MUTE,
435 AMP_IN_UNMUTE(idx));
436}
437
438/* set the given pin as output */
439static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
440 int pin_type)
441{
442 if (!pin)
443 return;
444 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
445 pin_type);
446 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
447 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200448 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100449}
450
Takashi Iwai5d417622011-06-20 11:32:27 +0200451static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin,
452 int pin_type, struct nid_path *path)
453{
454 struct via_spec *spec = codec->spec;
455 unsigned int caps;
456 hda_nid_t nid;
457 int i;
458
459 if (!pin)
460 return;
461
462 init_output_pin(codec, pin, pin_type);
463 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
464 if (caps & AC_AMPCAP_MUTE) {
465 unsigned int val;
466 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
467 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
468 AMP_OUT_MUTE | val);
469 }
470
471 /* initialize the output path */
472 nid = pin;
473 for (i = 0; i < path->depth; i++) {
474 unmute_and_select(codec, nid, path->idx[i], spec->aa_mix_nid);
475 nid = path->path[i];
476 if (query_amp_caps(codec, nid, HDA_OUTPUT) &
477 (AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE))
478 snd_hda_codec_write(codec, nid, 0,
479 AC_VERB_SET_AMP_GAIN_MUTE,
480 AMP_OUT_UNMUTE);
481 }
482}
483
Joseph Chanc577b8a2006-11-29 15:29:40 +0100484
485static void via_auto_init_multi_out(struct hda_codec *codec)
486{
487 struct via_spec *spec = codec->spec;
488 int i;
489
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200490 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwai5d417622011-06-20 11:32:27 +0200491 via_auto_init_output(codec, spec->autocfg.line_out_pins[i],
492 PIN_OUT, &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100493}
494
495static void via_auto_init_hp_out(struct hda_codec *codec)
496{
497 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100498
Takashi Iwai5d417622011-06-20 11:32:27 +0200499 if (spec->hp_dac_nid)
500 via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
501 &spec->hp_path);
502 else
503 via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
504 &spec->hp_dep_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100505}
506
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200507static void via_auto_init_speaker_out(struct hda_codec *codec)
508{
509 struct via_spec *spec = codec->spec;
510
511 if (spec->autocfg.speaker_outs)
512 via_auto_init_output(codec, spec->autocfg.speaker_pins[0],
513 PIN_OUT, &spec->speaker_path);
514}
515
Takashi Iwaif4a78282011-06-17 18:46:48 +0200516static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200517
Joseph Chanc577b8a2006-11-29 15:29:40 +0100518static void via_auto_init_analog_input(struct hda_codec *codec)
519{
520 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200521 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200522 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200523 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200524 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100525
Takashi Iwai096a8852011-06-20 12:09:02 +0200526 /* init ADCs */
527 for (i = 0; i < spec->num_adc_nids; i++) {
528 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
529 AC_VERB_SET_AMP_GAIN_MUTE,
530 AMP_IN_UNMUTE(0));
531 }
532
533 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200534 for (i = 0; i < cfg->num_inputs; i++) {
535 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200536 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200537 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100538 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200539 ctl = PIN_VREF50;
540 else
541 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100542 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200543 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100544 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200545
546 /* init input-src */
547 for (i = 0; i < spec->num_adc_nids; i++) {
548 const struct hda_input_mux *imux = spec->input_mux;
549 if (!imux || !spec->mux_nids[i])
550 continue;
551 snd_hda_codec_write(codec, spec->mux_nids[i], 0,
552 AC_VERB_SET_CONNECT_SEL,
553 imux->items[spec->cur_mux[i]].index);
554 }
555
556 /* init aa-mixer */
557 if (!spec->aa_mix_nid)
558 return;
559 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
560 ARRAY_SIZE(conn));
561 for (i = 0; i < num_conns; i++) {
562 unsigned int caps = get_wcaps(codec, conn[i]);
563 if (get_wcaps_type(caps) == AC_WID_PIN)
564 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
565 AC_VERB_SET_AMP_GAIN_MUTE,
566 AMP_IN_MUTE(i));
567 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100568}
Lydia Wangf5271102009-10-10 19:07:35 +0800569
570static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
571 unsigned int *affected_parm)
572{
573 unsigned parm;
574 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
575 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
576 >> AC_DEFCFG_MISC_SHIFT
577 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800578 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200579 unsigned present = 0;
580
581 no_presence |= spec->no_pin_power_ctl;
582 if (!no_presence)
583 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200584 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800585 || ((no_presence || present)
586 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800587 *affected_parm = AC_PWRST_D0; /* if it's connected */
588 parm = AC_PWRST_D0;
589 } else
590 parm = AC_PWRST_D3;
591
592 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
593}
594
Takashi Iwai24088a52011-06-17 16:59:21 +0200595static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
596 struct snd_ctl_elem_info *uinfo)
597{
598 static const char * const texts[] = {
599 "Disabled", "Enabled"
600 };
601
602 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
603 uinfo->count = 1;
604 uinfo->value.enumerated.items = 2;
605 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
606 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
607 strcpy(uinfo->value.enumerated.name,
608 texts[uinfo->value.enumerated.item]);
609 return 0;
610}
611
612static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
613 struct snd_ctl_elem_value *ucontrol)
614{
615 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
616 struct via_spec *spec = codec->spec;
617 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
618 return 0;
619}
620
621static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
622 struct snd_ctl_elem_value *ucontrol)
623{
624 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
625 struct via_spec *spec = codec->spec;
626 unsigned int val = !ucontrol->value.enumerated.item[0];
627
628 if (val == spec->no_pin_power_ctl)
629 return 0;
630 spec->no_pin_power_ctl = val;
631 set_widgets_power_state(codec);
632 return 1;
633}
634
635static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
636 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
637 .name = "Dynamic Power-Control",
638 .info = via_pin_power_ctl_info,
639 .get = via_pin_power_ctl_get,
640 .put = via_pin_power_ctl_put,
641};
642
643
Joseph Chanc577b8a2006-11-29 15:29:40 +0100644/*
645 * input MUX handling
646 */
647static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
648 struct snd_ctl_elem_info *uinfo)
649{
650 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
651 struct via_spec *spec = codec->spec;
652 return snd_hda_input_mux_info(spec->input_mux, uinfo);
653}
654
655static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
656 struct snd_ctl_elem_value *ucontrol)
657{
658 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
659 struct via_spec *spec = codec->spec;
660 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
661
662 ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
663 return 0;
664}
665
666static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
667 struct snd_ctl_elem_value *ucontrol)
668{
669 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
670 struct via_spec *spec = codec->spec;
671 unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800672 int ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100673
Takashi Iwai337b9d02009-07-07 18:18:59 +0200674 if (!spec->mux_nids[adc_idx])
675 return -EINVAL;
Lydia Wanga80e6e32009-10-10 19:07:55 +0800676 /* switch to D0 beofre change index */
677 if (snd_hda_codec_read(codec, spec->mux_nids[adc_idx], 0,
678 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
679 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
680 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800681
682 ret = snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
683 spec->mux_nids[adc_idx],
684 &spec->cur_mux[adc_idx]);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800685 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800686 set_widgets_power_state(codec);
Lydia Wanga80e6e32009-10-10 19:07:55 +0800687
Lydia Wangbff5fbf2011-03-22 16:21:38 +0800688 return ret;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100689}
690
Harald Welte0aa62ae2008-09-09 15:58:27 +0800691static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
692 struct snd_ctl_elem_info *uinfo)
693{
694 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
695 struct via_spec *spec = codec->spec;
696 return snd_hda_input_mux_info(spec->hp_mux, uinfo);
697}
698
699static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
700 struct snd_ctl_elem_value *ucontrol)
701{
702 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800703 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800704
Takashi Iwaiece8d042011-06-19 16:24:21 +0200705 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800706 return 0;
707}
708
Harald Welte0aa62ae2008-09-09 15:58:27 +0800709static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
710 struct snd_ctl_elem_value *ucontrol)
711{
712 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
713 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100714 hda_nid_t nid = kcontrol->private_value;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800715 unsigned int pinsel = ucontrol->value.enumerated.item[0];
Lydia Wangcdc17842009-10-10 19:07:47 +0800716 /* Get Independent Mode index of headphone pin widget */
717 spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
718 ? 1 : 0;
Takashi Iwaiece8d042011-06-19 16:24:21 +0200719 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800720
Lydia Wangce0e5a92011-03-22 16:22:37 +0800721 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800722 set_widgets_power_state(codec);
Harald Welte0aa62ae2008-09-09 15:58:27 +0800723 return 0;
724}
725
Takashi Iwaiece8d042011-06-19 16:24:21 +0200726static const struct snd_kcontrol_new via_hp_mixer = {
727 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
728 .name = "Independent HP",
729 .info = via_independent_hp_info,
730 .get = via_independent_hp_get,
731 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800732};
733
Takashi Iwai3d83e572010-04-14 14:36:23 +0200734static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100735{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200736 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100737 struct snd_kcontrol_new *knew;
738 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100739
Takashi Iwaiece8d042011-06-19 16:24:21 +0200740 nid = spec->autocfg.hp_pins[0];
741 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200742 if (knew == NULL)
743 return -ENOMEM;
744
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100745 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
746 knew->private_value = nid;
747
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100748 return 0;
749}
750
Lydia Wang1564b282009-10-10 19:07:52 +0800751static void notify_aa_path_ctls(struct hda_codec *codec)
752{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200753 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800754 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800755
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200756 for (i = 0; i < spec->smart51_nums; i++) {
757 struct snd_kcontrol *ctl;
758 struct snd_ctl_elem_id id;
759 memset(&id, 0, sizeof(id));
760 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
761 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800762 ctl = snd_hda_find_mixer_ctl(codec, id.name);
763 if (ctl)
764 snd_ctl_notify(codec->bus->card,
765 SNDRV_CTL_EVENT_MASK_VALUE,
766 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800767 }
768}
769
770static void mute_aa_path(struct hda_codec *codec, int mute)
771{
772 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200773 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800774 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200775
Lydia Wang1564b282009-10-10 19:07:52 +0800776 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200777 for (i = 0; i < spec->smart51_nums; i++) {
778 if (spec->smart51_idxs[i] < 0)
779 continue;
780 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
781 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800782 HDA_AMP_MUTE, val);
783 }
784}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200785
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200786static bool is_smart51_candidate(struct hda_codec *codec, hda_nid_t pin)
Lydia Wang1564b282009-10-10 19:07:52 +0800787{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200788 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200789 const struct auto_pin_cfg *cfg = &spec->autocfg;
790 int i;
791
792 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaif4a78282011-06-17 18:46:48 +0200793 unsigned int defcfg;
794 if (pin != cfg->inputs[i].pin)
795 continue;
796 if (cfg->inputs[i].type > AUTO_PIN_LINE_IN)
797 return false;
798 defcfg = snd_hda_codec_get_pincfg(codec, pin);
799 if (snd_hda_get_input_pin_attr(defcfg) < INPUT_PIN_ATTR_NORMAL)
800 return false;
801 return true;
Lydia Wang1564b282009-10-10 19:07:52 +0800802 }
Takashi Iwaif4a78282011-06-17 18:46:48 +0200803 return false;
Lydia Wang1564b282009-10-10 19:07:52 +0800804}
805
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200806static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
807{
808 struct via_spec *spec = codec->spec;
809 int i;
810
811 for (i = 0; i < spec->smart51_nums; i++)
812 if (spec->smart51_pins[i] == pin)
813 return true;
814 return false;
815}
816
Lydia Wang1564b282009-10-10 19:07:52 +0800817static int via_smart51_info(struct snd_kcontrol *kcontrol,
818 struct snd_ctl_elem_info *uinfo)
819{
820 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
821 uinfo->count = 1;
822 uinfo->value.integer.min = 0;
823 uinfo->value.integer.max = 1;
824 return 0;
825}
826
827static int via_smart51_get(struct snd_kcontrol *kcontrol,
828 struct snd_ctl_elem_value *ucontrol)
829{
830 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
831 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800832 int on = 1;
833 int i;
834
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200835 for (i = 0; i < spec->smart51_nums; i++) {
836 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwaif4a78282011-06-17 18:46:48 +0200837 unsigned int ctl;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200838 ctl = snd_hda_codec_read(codec, nid, 0,
839 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200840 if ((ctl & AC_PINCTL_IN_EN) && !(ctl & AC_PINCTL_OUT_EN))
841 on = 0;
Lydia Wang1564b282009-10-10 19:07:52 +0800842 }
843 *ucontrol->value.integer.value = on;
844 return 0;
845}
846
847static int via_smart51_put(struct snd_kcontrol *kcontrol,
848 struct snd_ctl_elem_value *ucontrol)
849{
850 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
851 struct via_spec *spec = codec->spec;
852 int out_in = *ucontrol->value.integer.value
853 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800854 int i;
855
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200856 for (i = 0; i < spec->smart51_nums; i++) {
857 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200858 unsigned int parm;
859
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200860 parm = snd_hda_codec_read(codec, nid, 0,
861 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
862 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
863 parm |= out_in;
864 snd_hda_codec_write(codec, nid, 0,
865 AC_VERB_SET_PIN_WIDGET_CONTROL,
866 parm);
867 if (out_in == AC_PINCTL_OUT_EN) {
868 mute_aa_path(codec, 1);
869 notify_aa_path_ctls(codec);
870 }
Lydia Wang1564b282009-10-10 19:07:52 +0800871 }
872 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800873 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800874 return 1;
875}
876
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200877static const struct snd_kcontrol_new via_smart51_mixer = {
878 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
879 .name = "Smart 5.1",
880 .count = 1,
881 .info = via_smart51_info,
882 .get = via_smart51_get,
883 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800884};
885
Takashi Iwaif4a78282011-06-17 18:46:48 +0200886static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100887{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200888 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100889
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200890 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800891 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200892 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100893 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100894 return 0;
895}
896
Takashi Iwaiada509e2011-06-20 15:40:19 +0200897/* check AA path's mute status */
898static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800899{
Lydia Wangf5271102009-10-10 19:07:35 +0800900 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200901 const struct hda_amp_list *p;
902 int i, ch, v;
903
904 for (i = 0; i < spec->num_loopbacks; i++) {
905 p = &spec->loopback_list[i];
906 for (ch = 0; ch < 2; ch++) {
907 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
908 p->idx);
909 if (!(v & HDA_AMP_MUTE) && v > 0)
910 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800911 }
912 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200913 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800914}
915
916/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200917static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800918{
919 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200920 bool enable;
921 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800922
Takashi Iwaiada509e2011-06-20 15:40:19 +0200923 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800924
925 /* decide low current mode's verb & parameter */
926 switch (spec->codec_type) {
927 case VT1708B_8CH:
928 case VT1708B_4CH:
929 verb = 0xf70;
930 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
931 break;
932 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800933 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800934 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800935 verb = 0xf73;
936 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
937 break;
938 case VT1702:
939 verb = 0xf73;
940 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
941 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800942 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800943 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800944 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800945 verb = 0xf93;
946 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
947 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800948 default:
949 return; /* other codecs are not supported */
950 }
951 /* send verb */
952 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
953}
954
Joseph Chanc577b8a2006-11-29 15:29:40 +0100955/*
956 * generic initialization of ADC, input mixers and output mixers
957 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200958static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800959 /* power down jack detect function */
960 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +0100961 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100962};
963
Takashi Iwaiada509e2011-06-20 15:40:19 +0200964static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200965{
Takashi Iwaiada509e2011-06-20 15:40:19 +0200966 struct via_spec *spec = codec->spec;
967
968 if (active)
969 spec->num_active_streams++;
970 else
971 spec->num_active_streams--;
972 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200973}
974
Takashi Iwaiece8d042011-06-19 16:24:21 +0200975static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100976 struct hda_codec *codec,
977 struct snd_pcm_substream *substream)
978{
979 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200980 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +0200981
982 if (!spec->hp_independent_mode)
983 spec->multiout.hp_nid = spec->hp_dac_nid;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200984 set_stream_active(codec, true);
985 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
986 hinfo);
987 if (err < 0) {
988 spec->multiout.hp_nid = 0;
989 set_stream_active(codec, false);
990 return err;
991 }
992 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100993}
994
Takashi Iwaiece8d042011-06-19 16:24:21 +0200995static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +0200996 struct hda_codec *codec,
997 struct snd_pcm_substream *substream)
998{
Takashi Iwaiece8d042011-06-19 16:24:21 +0200999 struct via_spec *spec = codec->spec;
1000
1001 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001002 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001003 return 0;
1004}
1005
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001006static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1007 struct hda_codec *codec,
1008 struct snd_pcm_substream *substream)
1009{
1010 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001011
Takashi Iwaiece8d042011-06-19 16:24:21 +02001012 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001013 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001014 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1015 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001016 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001017 return 0;
1018}
1019
1020static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1021 struct hda_codec *codec,
1022 struct snd_pcm_substream *substream)
1023{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001024 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001025 return 0;
1026}
1027
1028static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1029 struct hda_codec *codec,
1030 unsigned int stream_tag,
1031 unsigned int format,
1032 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001033{
1034 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001035
Takashi Iwaiece8d042011-06-19 16:24:21 +02001036 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1037 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001038 vt1708_start_hp_work(spec);
1039 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001040}
1041
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001042static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1043 struct hda_codec *codec,
1044 unsigned int stream_tag,
1045 unsigned int format,
1046 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001047{
1048 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001049
Takashi Iwaiece8d042011-06-19 16:24:21 +02001050 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1051 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001052 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001053 return 0;
1054}
1055
1056static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1057 struct hda_codec *codec,
1058 struct snd_pcm_substream *substream)
1059{
1060 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001061
Takashi Iwaiece8d042011-06-19 16:24:21 +02001062 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001063 vt1708_stop_hp_work(spec);
1064 return 0;
1065}
1066
1067static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1068 struct hda_codec *codec,
1069 struct snd_pcm_substream *substream)
1070{
1071 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001072
Takashi Iwaiece8d042011-06-19 16:24:21 +02001073 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001074 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001075 return 0;
1076}
1077
Joseph Chanc577b8a2006-11-29 15:29:40 +01001078/*
1079 * Digital out
1080 */
1081static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1082 struct hda_codec *codec,
1083 struct snd_pcm_substream *substream)
1084{
1085 struct via_spec *spec = codec->spec;
1086 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1087}
1088
1089static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1090 struct hda_codec *codec,
1091 struct snd_pcm_substream *substream)
1092{
1093 struct via_spec *spec = codec->spec;
1094 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1095}
1096
Harald Welte5691ec72008-09-15 22:42:26 +08001097static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001098 struct hda_codec *codec,
1099 unsigned int stream_tag,
1100 unsigned int format,
1101 struct snd_pcm_substream *substream)
1102{
1103 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001104 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1105 stream_tag, format, substream);
1106}
Harald Welte5691ec72008-09-15 22:42:26 +08001107
Takashi Iwai9da29272009-05-07 16:31:14 +02001108static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1109 struct hda_codec *codec,
1110 struct snd_pcm_substream *substream)
1111{
1112 struct via_spec *spec = codec->spec;
1113 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001114 return 0;
1115}
1116
Joseph Chanc577b8a2006-11-29 15:29:40 +01001117/*
1118 * Analog capture
1119 */
1120static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1121 struct hda_codec *codec,
1122 unsigned int stream_tag,
1123 unsigned int format,
1124 struct snd_pcm_substream *substream)
1125{
1126 struct via_spec *spec = codec->spec;
1127
1128 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1129 stream_tag, 0, format);
1130 return 0;
1131}
1132
1133static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1134 struct hda_codec *codec,
1135 struct snd_pcm_substream *substream)
1136{
1137 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001138 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001139 return 0;
1140}
1141
Takashi Iwai9af74212011-06-18 16:17:45 +02001142static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001143 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001144 .channels_min = 2,
1145 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001146 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001147 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001148 .open = via_playback_multi_pcm_open,
1149 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001150 .prepare = via_playback_multi_pcm_prepare,
1151 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001152 },
1153};
1154
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001155static const struct hda_pcm_stream via_pcm_hp_playback = {
1156 .substreams = 1,
1157 .channels_min = 2,
1158 .channels_max = 2,
1159 /* NID is set in via_build_pcms */
1160 .ops = {
1161 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001162 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001163 .prepare = via_playback_hp_pcm_prepare,
1164 .cleanup = via_playback_hp_pcm_cleanup
1165 },
1166};
1167
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001168static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001169 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001170 .channels_min = 2,
1171 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001172 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001173 /* We got noisy outputs on the right channel on VT1708 when
1174 * 24bit samples are used. Until any workaround is found,
1175 * disable the 24bit format, so far.
1176 */
1177 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1178 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001179 .open = via_playback_multi_pcm_open,
1180 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001181 .prepare = via_playback_multi_pcm_prepare,
1182 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001183 },
1184};
1185
Takashi Iwai9af74212011-06-18 16:17:45 +02001186static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001187 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001188 .channels_min = 2,
1189 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001190 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001191 .ops = {
1192 .prepare = via_capture_pcm_prepare,
1193 .cleanup = via_capture_pcm_cleanup
1194 },
1195};
1196
Takashi Iwai9af74212011-06-18 16:17:45 +02001197static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001198 .substreams = 1,
1199 .channels_min = 2,
1200 .channels_max = 2,
1201 /* NID is set in via_build_pcms */
1202 .ops = {
1203 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001204 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001205 .prepare = via_dig_playback_pcm_prepare,
1206 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001207 },
1208};
1209
Takashi Iwai9af74212011-06-18 16:17:45 +02001210static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001211 .substreams = 1,
1212 .channels_min = 2,
1213 .channels_max = 2,
1214};
1215
Takashi Iwai370bafb2011-06-20 12:47:45 +02001216/*
1217 * slave controls for virtual master
1218 */
1219static const char * const via_slave_vols[] = {
1220 "Front Playback Volume",
1221 "Surround Playback Volume",
1222 "Center Playback Volume",
1223 "LFE Playback Volume",
1224 "Side Playback Volume",
1225 "Headphone Playback Volume",
1226 "Speaker Playback Volume",
1227 NULL,
1228};
1229
1230static const char * const via_slave_sws[] = {
1231 "Front Playback Switch",
1232 "Surround Playback Switch",
1233 "Center Playback Switch",
1234 "LFE Playback Switch",
1235 "Side Playback Switch",
1236 "Headphone Playback Switch",
1237 "Speaker Playback Switch",
1238 NULL,
1239};
1240
Joseph Chanc577b8a2006-11-29 15:29:40 +01001241static int via_build_controls(struct hda_codec *codec)
1242{
1243 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001244 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001245 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001246
Takashi Iwai24088a52011-06-17 16:59:21 +02001247 if (spec->set_widgets_power_state)
1248 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1249 return -ENOMEM;
1250
Joseph Chanc577b8a2006-11-29 15:29:40 +01001251 for (i = 0; i < spec->num_mixers; i++) {
1252 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1253 if (err < 0)
1254 return err;
1255 }
1256
1257 if (spec->multiout.dig_out_nid) {
1258 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001259 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001260 spec->multiout.dig_out_nid);
1261 if (err < 0)
1262 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001263 err = snd_hda_create_spdif_share_sw(codec,
1264 &spec->multiout);
1265 if (err < 0)
1266 return err;
1267 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001268 }
1269 if (spec->dig_in_nid) {
1270 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1271 if (err < 0)
1272 return err;
1273 }
Lydia Wang17314372009-10-10 19:07:37 +08001274
Takashi Iwai370bafb2011-06-20 12:47:45 +02001275 /* if we have no master control, let's create it */
1276 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1277 unsigned int vmaster_tlv[4];
1278 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1279 HDA_OUTPUT, vmaster_tlv);
1280 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1281 vmaster_tlv, via_slave_vols);
1282 if (err < 0)
1283 return err;
1284 }
1285 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1286 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1287 NULL, via_slave_sws);
1288 if (err < 0)
1289 return err;
1290 }
1291
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001292 /* assign Capture Source enums to NID */
1293 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1294 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001295 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001296 if (err < 0)
1297 return err;
1298 }
1299
Lydia Wang17314372009-10-10 19:07:37 +08001300 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001301 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001302 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001303
Takashi Iwai603c4012008-07-30 15:01:44 +02001304 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001305 return 0;
1306}
1307
1308static int via_build_pcms(struct hda_codec *codec)
1309{
1310 struct via_spec *spec = codec->spec;
1311 struct hda_pcm *info = spec->pcm_rec;
1312
1313 codec->num_pcms = 1;
1314 codec->pcm_info = info;
1315
Takashi Iwai82673bc2011-06-17 16:24:21 +02001316 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1317 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001318 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001319
1320 if (!spec->stream_analog_playback)
1321 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001322 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001323 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001324 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1325 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001326 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1327 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001328
1329 if (!spec->stream_analog_capture)
1330 spec->stream_analog_capture = &via_pcm_analog_capture;
1331 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1332 *spec->stream_analog_capture;
1333 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
1334 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1335 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001336
1337 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1338 codec->num_pcms++;
1339 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001340 snprintf(spec->stream_name_digital,
1341 sizeof(spec->stream_name_digital),
1342 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001343 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001344 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001345 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001346 if (!spec->stream_digital_playback)
1347 spec->stream_digital_playback =
1348 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001349 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001350 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001351 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1352 spec->multiout.dig_out_nid;
1353 }
1354 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001355 if (!spec->stream_digital_capture)
1356 spec->stream_digital_capture =
1357 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001358 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001359 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001360 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1361 spec->dig_in_nid;
1362 }
1363 }
1364
Takashi Iwaiece8d042011-06-19 16:24:21 +02001365 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001366 codec->num_pcms++;
1367 info++;
1368 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1369 "%s HP", codec->chip_name);
1370 info->name = spec->stream_name_hp;
1371 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1372 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001373 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001374 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001375 return 0;
1376}
1377
1378static void via_free(struct hda_codec *codec)
1379{
1380 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001381
1382 if (!spec)
1383 return;
1384
Takashi Iwai603c4012008-07-30 15:01:44 +02001385 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001386 vt1708_stop_hp_work(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001387 kfree(codec->spec);
1388}
1389
Takashi Iwai64be2852011-06-17 16:51:39 +02001390/* mute/unmute outputs */
1391static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1392 hda_nid_t *pins, bool mute)
1393{
1394 int i;
1395 for (i = 0; i < num_pins; i++)
1396 snd_hda_codec_write(codec, pins[i], 0,
1397 AC_VERB_SET_PIN_WIDGET_CONTROL,
1398 mute ? 0 : PIN_OUT);
1399}
1400
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001401/* mute internal speaker if line-out is plugged */
1402static void via_line_automute(struct hda_codec *codec, int present)
1403{
1404 struct via_spec *spec = codec->spec;
1405
1406 if (!spec->autocfg.speaker_outs)
1407 return;
1408 if (!present)
1409 present = snd_hda_jack_detect(codec,
1410 spec->autocfg.line_out_pins[0]);
1411 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1412 spec->autocfg.speaker_pins,
1413 present);
1414}
1415
Harald Welte69e52a82008-09-09 15:57:32 +08001416/* mute internal speaker if HP is plugged */
1417static void via_hp_automute(struct hda_codec *codec)
1418{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001419 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001420 struct via_spec *spec = codec->spec;
1421
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001422 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
1423 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai64be2852011-06-17 16:51:39 +02001424 toggle_output_mutes(codec, spec->autocfg.line_outs,
1425 spec->autocfg.line_out_pins,
1426 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001427 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001428 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001429}
1430
Harald Welte69e52a82008-09-09 15:57:32 +08001431static void via_gpio_control(struct hda_codec *codec)
1432{
1433 unsigned int gpio_data;
1434 unsigned int vol_counter;
1435 unsigned int vol;
1436 unsigned int master_vol;
1437
1438 struct via_spec *spec = codec->spec;
1439
1440 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1441 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1442
1443 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1444 0xF84, 0) & 0x3F0000) >> 16;
1445
1446 vol = vol_counter & 0x1F;
1447 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1448 AC_VERB_GET_AMP_GAIN_MUTE,
1449 AC_AMP_GET_INPUT);
1450
1451 if (gpio_data == 0x02) {
1452 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001453 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1454 AC_VERB_SET_PIN_WIDGET_CONTROL,
1455 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001456 if (vol_counter & 0x20) {
1457 /* decrease volume */
1458 if (vol > master_vol)
1459 vol = master_vol;
1460 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1461 0, HDA_AMP_VOLMASK,
1462 master_vol-vol);
1463 } else {
1464 /* increase volume */
1465 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1466 HDA_AMP_VOLMASK,
1467 ((master_vol+vol) > 0x2A) ? 0x2A :
1468 (master_vol+vol));
1469 }
1470 } else if (!(gpio_data & 0x02)) {
1471 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001472 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1473 AC_VERB_SET_PIN_WIDGET_CONTROL,
1474 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001475 }
1476}
1477
1478/* unsolicited event for jack sensing */
1479static void via_unsol_event(struct hda_codec *codec,
1480 unsigned int res)
1481{
1482 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001483
Lydia Wanga34df192009-10-10 19:08:01 +08001484 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001485 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001486
1487 res &= ~VIA_JACK_EVENT;
1488
1489 if (res == VIA_HP_EVENT)
1490 via_hp_automute(codec);
1491 else if (res == VIA_GPIO_EVENT)
1492 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001493 else if (res == VIA_LINE_EVENT)
1494 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001495}
1496
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001497#ifdef SND_HDA_NEEDS_RESUME
1498static int via_suspend(struct hda_codec *codec, pm_message_t state)
1499{
1500 struct via_spec *spec = codec->spec;
1501 vt1708_stop_hp_work(spec);
1502 return 0;
1503}
1504#endif
1505
Takashi Iwaicb53c622007-08-10 17:21:45 +02001506#ifdef CONFIG_SND_HDA_POWER_SAVE
1507static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1508{
1509 struct via_spec *spec = codec->spec;
1510 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1511}
1512#endif
1513
Joseph Chanc577b8a2006-11-29 15:29:40 +01001514/*
1515 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001516
1517static int via_init(struct hda_codec *codec);
1518
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001519static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001520 .build_controls = via_build_controls,
1521 .build_pcms = via_build_pcms,
1522 .init = via_init,
1523 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001524 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001525#ifdef SND_HDA_NEEDS_RESUME
1526 .suspend = via_suspend,
1527#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001528#ifdef CONFIG_SND_HDA_POWER_SAVE
1529 .check_power_status = via_check_power_status,
1530#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001531};
1532
Takashi Iwai4a796162011-06-17 17:53:38 +02001533static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001534{
Takashi Iwai4a796162011-06-17 17:53:38 +02001535 struct via_spec *spec = codec->spec;
1536 int i;
1537
1538 for (i = 0; i < spec->multiout.num_dacs; i++) {
1539 if (spec->multiout.dac_nids[i] == dac)
1540 return false;
1541 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001542 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001543 return false;
1544 return true;
1545}
1546
1547static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1548 hda_nid_t target_dac, struct nid_path *path,
1549 int depth, int wid_type)
1550{
1551 hda_nid_t conn[8];
1552 int i, nums;
1553
1554 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1555 for (i = 0; i < nums; i++) {
1556 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1557 continue;
1558 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1559 path->path[depth] = conn[i];
1560 path->idx[depth] = i;
1561 path->depth = ++depth;
1562 return true;
1563 }
1564 }
1565 if (depth > 4)
1566 return false;
1567 for (i = 0; i < nums; i++) {
1568 unsigned int type;
1569 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1570 if (type == AC_WID_AUD_OUT ||
1571 (wid_type != -1 && type != wid_type))
1572 continue;
1573 if (parse_output_path(codec, conn[i], target_dac,
1574 path, depth + 1, AC_WID_AUD_SEL)) {
1575 path->path[depth] = conn[i];
1576 path->idx[depth] = i;
1577 return true;
1578 }
1579 }
1580 return false;
1581}
1582
1583static int via_auto_fill_dac_nids(struct hda_codec *codec)
1584{
1585 struct via_spec *spec = codec->spec;
1586 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001587 int i;
1588 hda_nid_t nid;
1589
Joseph Chanc577b8a2006-11-29 15:29:40 +01001590 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001591 spec->multiout.num_dacs = cfg->line_outs;
1592 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001593 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001594 if (!nid)
1595 continue;
1596 if (parse_output_path(codec, nid, 0, &spec->out_path[i], 0, -1))
1597 spec->private_dac_nids[i] =
1598 spec->out_path[i].path[spec->out_path[i].depth - 1];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001599 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001600 return 0;
1601}
1602
Takashi Iwai4a796162011-06-17 17:53:38 +02001603static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
1604 hda_nid_t pin, hda_nid_t dac, int chs)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001605{
Takashi Iwai4a796162011-06-17 17:53:38 +02001606 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001607 char name[32];
Takashi Iwai4a796162011-06-17 17:53:38 +02001608 hda_nid_t nid;
1609 int err;
1610
1611 if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
1612 nid = dac;
1613 else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_NUM_STEPS)
1614 nid = pin;
1615 else
1616 nid = 0;
1617 if (nid) {
1618 sprintf(name, "%s Playback Volume", pfx);
1619 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1620 HDA_COMPOSE_AMP_VAL(dac, chs, 0, HDA_OUTPUT));
1621 if (err < 0)
1622 return err;
1623 }
1624
1625 if (dac && query_amp_caps(codec, dac, HDA_OUTPUT) & AC_AMPCAP_MUTE)
1626 nid = dac;
1627 else if (query_amp_caps(codec, pin, HDA_OUTPUT) & AC_AMPCAP_MUTE)
1628 nid = pin;
1629 else
1630 nid = 0;
1631 if (nid) {
1632 sprintf(name, "%s Playback Switch", pfx);
1633 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1634 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1635 if (err < 0)
1636 return err;
1637 }
1638 return 0;
1639}
1640
Takashi Iwaif4a78282011-06-17 18:46:48 +02001641static void mangle_smart51(struct hda_codec *codec)
1642{
1643 struct via_spec *spec = codec->spec;
1644 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001645 int i, nums = 0;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001646
1647 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001648 if (is_smart51_candidate(codec, cfg->inputs[i].pin))
1649 nums++;
1650 }
1651 if (cfg->line_outs + nums < 3)
1652 return;
1653 for (i = 0; i < cfg->num_inputs; i++) {
1654 if (!is_smart51_candidate(codec, cfg->inputs[i].pin))
Takashi Iwaif4a78282011-06-17 18:46:48 +02001655 continue;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001656 spec->smart51_pins[spec->smart51_nums++] = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001657 cfg->line_out_pins[cfg->line_outs++] = cfg->inputs[i].pin;
1658 if (cfg->line_outs == 3)
1659 break;
1660 }
1661}
1662
Takashi Iwai4a796162011-06-17 17:53:38 +02001663/* add playback controls from the parsed DAC table */
1664static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1665{
1666 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001667 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001668 static const char * const chname[4] = {
1669 "Front", "Surround", "C/LFE", "Side"
1670 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001671 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001672 int old_line_outs;
1673
1674 /* check smart51 */
1675 old_line_outs = cfg->line_outs;
1676 if (cfg->line_outs == 1)
1677 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001678
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001679 err = via_auto_fill_dac_nids(codec);
1680 if (err < 0)
1681 return err;
1682
Takashi Iwai4a796162011-06-17 17:53:38 +02001683 for (i = 0; i < cfg->line_outs; i++) {
1684 hda_nid_t pin, dac;
1685 pin = cfg->line_out_pins[i];
1686 dac = spec->multiout.dac_nids[i];
1687 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001688 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001689 if (i == HDA_CLFE) {
Takashi Iwai4a796162011-06-17 17:53:38 +02001690 err = create_ch_ctls(codec, "Center", pin, dac, 1);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001691 if (err < 0)
1692 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02001693 err = create_ch_ctls(codec, "LFE", pin, dac, 2);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001694 if (err < 0)
1695 return err;
1696 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001697 const char *pfx = chname[i];
1698 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1699 cfg->line_outs == 1)
1700 pfx = "Speaker";
1701 err = create_ch_ctls(codec, pfx, pin, dac, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001702 if (err < 0)
1703 return err;
1704 }
1705 }
1706
Takashi Iwai4a796162011-06-17 17:53:38 +02001707 idx = get_connection_index(codec, spec->aa_mix_nid,
1708 spec->multiout.dac_nids[0]);
1709 if (idx >= 0) {
1710 /* add control to mixer */
1711 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1712 "PCM Playback Volume",
1713 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1714 idx, HDA_INPUT));
1715 if (err < 0)
1716 return err;
1717 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1718 "PCM Playback Switch",
1719 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1720 idx, HDA_INPUT));
1721 if (err < 0)
1722 return err;
1723 }
1724
Takashi Iwaif4a78282011-06-17 18:46:48 +02001725 cfg->line_outs = old_line_outs;
1726
Joseph Chanc577b8a2006-11-29 15:29:40 +01001727 return 0;
1728}
1729
Harald Welte0aa62ae2008-09-09 15:58:27 +08001730static void create_hp_imux(struct via_spec *spec)
1731{
1732 int i;
1733 struct hda_input_mux *imux = &spec->private_imux[1];
Takashi Iwaiea734962011-01-17 11:29:34 +01001734 static const char * const texts[] = { "OFF", "ON", NULL};
Harald Welte0aa62ae2008-09-09 15:58:27 +08001735
1736 /* for hp mode select */
Takashi Iwai10a20af2010-09-09 16:28:02 +02001737 for (i = 0; texts[i]; i++)
1738 snd_hda_add_imux_item(imux, texts[i], i, NULL);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001739
1740 spec->hp_mux = &spec->private_imux[1];
1741}
1742
Takashi Iwai4a796162011-06-17 17:53:38 +02001743static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001744{
Takashi Iwai4a796162011-06-17 17:53:38 +02001745 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001746 int err;
1747
1748 if (!pin)
1749 return 0;
1750
Takashi Iwai4a796162011-06-17 17:53:38 +02001751 if (parse_output_path(codec, pin, 0, &spec->hp_path, 0, -1)) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001752 spec->hp_dac_nid = spec->hp_path.path[spec->hp_path.depth - 1];
Takashi Iwai4a796162011-06-17 17:53:38 +02001753 spec->hp_independent_mode_index =
1754 spec->hp_path.idx[spec->hp_path.depth - 1];
1755 create_hp_imux(spec);
1756 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001757
Takashi Iwaiece8d042011-06-19 16:24:21 +02001758 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
1759 &spec->hp_dep_path, 0, -1) &&
1760 !spec->hp_dac_nid)
1761 return 0;
1762
1763
1764 err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001765 if (err < 0)
1766 return err;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001767
Joseph Chanc577b8a2006-11-29 15:29:40 +01001768 return 0;
1769}
1770
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001771static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1772{
1773 struct via_spec *spec = codec->spec;
1774 hda_nid_t pin, dac;
1775
1776 pin = spec->autocfg.speaker_pins[0];
1777 if (!spec->autocfg.speaker_outs || !pin)
1778 return 0;
1779
1780 if (parse_output_path(codec, pin, 0, &spec->speaker_path, 0, -1)) {
1781 dac = spec->speaker_path.path[spec->speaker_path.depth - 1];
1782 spec->multiout.extra_out_nid[0] = dac;
1783 return create_ch_ctls(codec, "Speaker", pin, dac, 3);
1784 }
1785 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
1786 &spec->speaker_path, 0, -1))
1787 return create_ch_ctls(codec, "Headphone", pin, 0, 3);
1788
1789 return 0;
1790}
1791
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001792/* look for ADCs */
1793static int via_fill_adcs(struct hda_codec *codec)
1794{
1795 struct via_spec *spec = codec->spec;
1796 hda_nid_t nid = codec->start_nid;
1797 int i;
1798
1799 for (i = 0; i < codec->num_nodes; i++, nid++) {
1800 unsigned int wcaps = get_wcaps(codec, nid);
1801 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1802 continue;
1803 if (wcaps & AC_WCAP_DIGITAL)
1804 continue;
1805 if (!(wcaps & AC_WCAP_CONN_LIST))
1806 continue;
1807 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1808 return -ENOMEM;
1809 spec->adc_nids[spec->num_adc_nids++] = nid;
1810 }
1811 return 0;
1812}
1813
1814static int get_mux_nids(struct hda_codec *codec);
1815
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001816static const struct snd_kcontrol_new via_input_src_ctl = {
1817 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1818 /* The multiple "Capture Source" controls confuse alsamixer
1819 * So call somewhat different..
1820 */
1821 /* .name = "Capture Source", */
1822 .name = "Input Source",
1823 .info = via_mux_enum_info,
1824 .get = via_mux_enum_get,
1825 .put = via_mux_enum_put,
1826};
1827
Takashi Iwai13af8e72011-06-20 14:05:46 +02001828static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
1829{
1830 struct hda_amp_list *list;
1831
1832 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
1833 return;
1834 list = spec->loopback_list + spec->num_loopbacks;
1835 list->nid = mix;
1836 list->dir = HDA_INPUT;
1837 list->idx = idx;
1838 spec->num_loopbacks++;
1839 spec->loopback.amplist = spec->loopback_list;
1840}
Takashi Iwai13af8e72011-06-20 14:05:46 +02001841
Joseph Chanc577b8a2006-11-29 15:29:40 +01001842/* create playback/capture controls for input pins */
Takashi Iwai620e2b22011-06-17 17:19:19 +02001843static int via_auto_create_analog_input_ctls(struct hda_codec *codec,
1844 const struct auto_pin_cfg *cfg)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001845{
Takashi Iwai10a20af2010-09-09 16:28:02 +02001846 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001847 struct hda_input_mux *imux = &spec->private_imux[0];
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001848 int i, j, err, idx, idx2, type, type_idx = 0;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001849 hda_nid_t cap_nid;
1850 hda_nid_t pin_idxs[8];
1851 int num_idxs;
1852
1853 err = via_fill_adcs(codec);
1854 if (err < 0)
1855 return err;
1856 err = get_mux_nids(codec);
1857 if (err < 0)
1858 return err;
1859 cap_nid = spec->mux_nids[0];
1860
1861 num_idxs = snd_hda_get_connections(codec, cap_nid, pin_idxs,
1862 ARRAY_SIZE(pin_idxs));
1863 if (num_idxs <= 0)
1864 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001865
1866 /* for internal loopback recording select */
Takashi Iwaif3268512010-08-30 11:00:19 +02001867 for (idx = 0; idx < num_idxs; idx++) {
Takashi Iwai620e2b22011-06-17 17:19:19 +02001868 if (pin_idxs[idx] == spec->aa_mix_nid) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001869 snd_hda_add_imux_item(imux, "Stereo Mixer", idx, NULL);
Takashi Iwaif3268512010-08-30 11:00:19 +02001870 break;
1871 }
1872 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001873
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001874 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwai10a20af2010-09-09 16:28:02 +02001875 const char *label;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001876 type = cfg->inputs[i].type;
Takashi Iwaif3268512010-08-30 11:00:19 +02001877 for (idx = 0; idx < num_idxs; idx++)
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001878 if (pin_idxs[idx] == cfg->inputs[i].pin)
Takashi Iwaif3268512010-08-30 11:00:19 +02001879 break;
1880 if (idx >= num_idxs)
1881 continue;
Takashi Iwai7b315bb2010-08-30 13:06:30 +02001882 if (i > 0 && type == cfg->inputs[i - 1].type)
1883 type_idx++;
1884 else
1885 type_idx = 0;
Takashi Iwai10a20af2010-09-09 16:28:02 +02001886 label = hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwai620e2b22011-06-17 17:19:19 +02001887 idx2 = get_connection_index(codec, spec->aa_mix_nid,
1888 pin_idxs[idx]);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001889 if (idx2 >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08001890 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwai620e2b22011-06-17 17:19:19 +02001891 idx2, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02001892 if (err < 0)
1893 return err;
1894 add_loopback_list(spec, spec->aa_mix_nid, idx2);
1895 }
Takashi Iwai10a20af2010-09-09 16:28:02 +02001896 snd_hda_add_imux_item(imux, label, idx, NULL);
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001897
1898 /* remember the label for smart51 control */
1899 for (j = 0; j < spec->smart51_nums; j++) {
1900 if (spec->smart51_pins[j] == cfg->inputs[i].pin) {
1901 spec->smart51_idxs[j] = idx;
1902 spec->smart51_labels[j] = label;
1903 break;
1904 }
1905 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001906 }
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001907
1908 /* create capture mixer elements */
1909 for (i = 0; i < spec->num_adc_nids; i++) {
1910 hda_nid_t adc = spec->adc_nids[i];
1911 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
1912 "Capture Volume", i,
1913 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1914 HDA_INPUT));
1915 if (err < 0)
1916 return err;
1917 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1918 "Capture Switch", i,
1919 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
1920 HDA_INPUT));
1921 if (err < 0)
1922 return err;
1923 }
1924
1925 /* input-source control */
1926 for (i = 0; i < spec->num_adc_nids; i++)
1927 if (!spec->mux_nids[i])
1928 break;
1929 if (i) {
1930 struct snd_kcontrol_new *knew;
1931 knew = via_clone_control(spec, &via_input_src_ctl);
1932 if (!knew)
1933 return -ENOMEM;
1934 knew->count = i;
1935 }
1936
1937 /* mic-boosts */
1938 for (i = 0; i < cfg->num_inputs; i++) {
1939 hda_nid_t pin = cfg->inputs[i].pin;
1940 unsigned int caps;
1941 const char *label;
1942 char name[32];
1943
1944 if (cfg->inputs[i].type != AUTO_PIN_MIC)
1945 continue;
1946 caps = query_amp_caps(codec, pin, HDA_INPUT);
1947 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
1948 continue;
1949 label = hda_get_autocfg_input_label(codec, cfg, i);
1950 snprintf(name, sizeof(name), "%s Boost Capture Volume", label);
1951 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
1952 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
1953 if (err < 0)
1954 return err;
1955 }
1956
Joseph Chanc577b8a2006-11-29 15:29:40 +01001957 return 0;
1958}
1959
Harald Welte76d9b0d2008-09-09 15:50:37 +08001960static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
1961{
1962 unsigned int def_conf;
1963 unsigned char seqassoc;
1964
Takashi Iwai2f334f92009-02-20 14:37:42 +01001965 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08001966 seqassoc = (unsigned char) get_defcfg_association(def_conf);
1967 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08001968 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
1969 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
1970 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
1971 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08001972 }
1973
1974 return;
1975}
1976
Takashi Iwaie06e5a22011-06-17 15:46:13 +02001977static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001978 struct snd_ctl_elem_value *ucontrol)
1979{
1980 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1981 struct via_spec *spec = codec->spec;
1982
1983 if (spec->codec_type != VT1708)
1984 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02001985 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001986 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02001987 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001988 return 0;
1989}
1990
Takashi Iwaie06e5a22011-06-17 15:46:13 +02001991static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001992 struct snd_ctl_elem_value *ucontrol)
1993{
1994 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1995 struct via_spec *spec = codec->spec;
1996 int change;
1997
1998 if (spec->codec_type != VT1708)
1999 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002000 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002001 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002002 == !spec->vt1708_jack_detect;
2003 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002004 mute_aa_path(codec, 1);
2005 notify_aa_path_ctls(codec);
2006 }
2007 return change;
2008}
2009
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002010static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2011 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2012 .name = "Jack Detect",
2013 .count = 1,
2014 .info = snd_ctl_boolean_mono_info,
2015 .get = vt1708_jack_detect_get,
2016 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002017};
2018
Takashi Iwai12daef62011-06-18 17:45:49 +02002019static void fill_dig_outs(struct hda_codec *codec);
2020static void fill_dig_in(struct hda_codec *codec);
2021
2022static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002023{
2024 struct via_spec *spec = codec->spec;
2025 int err;
2026
2027 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2028 if (err < 0)
2029 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002030 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002031 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002032
Takashi Iwai4a796162011-06-17 17:53:38 +02002033 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002034 if (err < 0)
2035 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002036 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002037 if (err < 0)
2038 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002039 err = via_auto_create_speaker_ctls(codec);
2040 if (err < 0)
2041 return err;
Takashi Iwai620e2b22011-06-17 17:19:19 +02002042 err = via_auto_create_analog_input_ctls(codec, &spec->autocfg);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002043 if (err < 0)
2044 return err;
2045
2046 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2047
Takashi Iwai12daef62011-06-18 17:45:49 +02002048 fill_dig_outs(codec);
2049 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002050
Takashi Iwai603c4012008-07-30 15:01:44 +02002051 if (spec->kctls.list)
2052 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002053
Takashi Iwai096a8852011-06-20 12:09:02 +02002054 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002055
Harald Welte0aa62ae2008-09-09 15:58:27 +08002056 spec->input_mux = &spec->private_imux[0];
2057
Takashi Iwaiece8d042011-06-19 16:24:21 +02002058 if (spec->hp_mux) {
2059 err = via_hp_build(codec);
2060 if (err < 0)
2061 return err;
2062 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002063
Takashi Iwaif4a78282011-06-17 18:46:48 +02002064 err = via_smart51_build(codec);
2065 if (err < 0)
2066 return err;
2067
Takashi Iwai5d417622011-06-20 11:32:27 +02002068 /* assign slave outs */
2069 if (spec->slave_dig_outs[0])
2070 codec->slave_dig_outs = spec->slave_dig_outs;
2071
Joseph Chanc577b8a2006-11-29 15:29:40 +01002072 return 1;
2073}
2074
Takashi Iwai5d417622011-06-20 11:32:27 +02002075static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002076{
Lydia Wang25eaba22009-10-10 19:08:43 +08002077 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002078 if (spec->multiout.dig_out_nid)
2079 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2080 if (spec->slave_dig_outs[0])
2081 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2082}
Lydia Wang25eaba22009-10-10 19:08:43 +08002083
Takashi Iwai5d417622011-06-20 11:32:27 +02002084static void via_auto_init_dig_in(struct hda_codec *codec)
2085{
2086 struct via_spec *spec = codec->spec;
2087 if (!spec->dig_in_nid)
2088 return;
2089 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2090 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2091}
2092
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002093/* initialize the unsolicited events */
2094static void via_auto_init_unsol_event(struct hda_codec *codec)
2095{
2096 struct via_spec *spec = codec->spec;
2097 struct auto_pin_cfg *cfg = &spec->autocfg;
2098 unsigned int ev;
2099 int i;
2100
2101 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2102 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2103 AC_VERB_SET_UNSOLICITED_ENABLE,
2104 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2105
2106 if (cfg->speaker_pins[0])
2107 ev = VIA_LINE_EVENT;
2108 else
2109 ev = 0;
2110 for (i = 0; i < cfg->line_outs; i++) {
2111 if (cfg->line_out_pins[i] &&
2112 is_jack_detectable(codec, cfg->line_out_pins[i]))
2113 snd_hda_codec_write(codec, cfg->line_out_pins[0], 0,
2114 AC_VERB_SET_UNSOLICITED_ENABLE,
2115 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2116 }
2117
2118 for (i = 0; i < cfg->num_inputs; i++) {
2119 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2120 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2121 AC_VERB_SET_UNSOLICITED_ENABLE,
2122 AC_USRSP_EN | VIA_JACK_EVENT);
2123 }
2124}
2125
Takashi Iwai5d417622011-06-20 11:32:27 +02002126static int via_init(struct hda_codec *codec)
2127{
2128 struct via_spec *spec = codec->spec;
2129 int i;
2130
2131 for (i = 0; i < spec->num_iverbs; i++)
2132 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2133
Joseph Chanc577b8a2006-11-29 15:29:40 +01002134 via_auto_init_multi_out(codec);
2135 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002136 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002137 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002138 via_auto_init_dig_outs(codec);
2139 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002140
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002141 via_auto_init_unsol_event(codec);
2142
2143 via_hp_automute(codec);
2144 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002145
Joseph Chanc577b8a2006-11-29 15:29:40 +01002146 return 0;
2147}
2148
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002149static void vt1708_update_hp_jack_state(struct work_struct *work)
2150{
2151 struct via_spec *spec = container_of(work, struct via_spec,
2152 vt1708_hp_work.work);
2153 if (spec->codec_type != VT1708)
2154 return;
2155 /* if jack state toggled */
2156 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002157 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002158 spec->vt1708_hp_present ^= 1;
2159 via_hp_automute(spec->codec);
2160 }
2161 vt1708_start_hp_work(spec);
2162}
2163
Takashi Iwai337b9d02009-07-07 18:18:59 +02002164static int get_mux_nids(struct hda_codec *codec)
2165{
2166 struct via_spec *spec = codec->spec;
2167 hda_nid_t nid, conn[8];
2168 unsigned int type;
2169 int i, n;
2170
2171 for (i = 0; i < spec->num_adc_nids; i++) {
2172 nid = spec->adc_nids[i];
2173 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002174 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002175 if (type == AC_WID_PIN)
2176 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002177 n = snd_hda_get_connections(codec, nid, conn,
2178 ARRAY_SIZE(conn));
2179 if (n <= 0)
2180 break;
2181 if (n > 1) {
2182 spec->mux_nids[i] = nid;
2183 break;
2184 }
2185 nid = conn[0];
2186 }
2187 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002188 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002189}
2190
Joseph Chanc577b8a2006-11-29 15:29:40 +01002191static int patch_vt1708(struct hda_codec *codec)
2192{
2193 struct via_spec *spec;
2194 int err;
2195
2196 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002197 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002198 if (spec == NULL)
2199 return -ENOMEM;
2200
Takashi Iwai620e2b22011-06-17 17:19:19 +02002201 spec->aa_mix_nid = 0x17;
2202
Takashi Iwai12daef62011-06-18 17:45:49 +02002203 /* Add HP and CD pin config connect bit re-config action */
2204 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2205 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2206
Joseph Chanc577b8a2006-11-29 15:29:40 +01002207 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002208 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002209 if (err < 0) {
2210 via_free(codec);
2211 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002212 }
2213
Takashi Iwai12daef62011-06-18 17:45:49 +02002214 /* add jack detect on/off control */
2215 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2216 return -ENOMEM;
2217
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002218 /* disable 32bit format on VT1708 */
2219 if (codec->vendor_id == 0x11061708)
2220 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002221
Joseph Chanc577b8a2006-11-29 15:29:40 +01002222 codec->patch_ops = via_patch_ops;
2223
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002224 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002225 return 0;
2226}
2227
Joseph Chanc577b8a2006-11-29 15:29:40 +01002228static int patch_vt1709_10ch(struct hda_codec *codec)
2229{
2230 struct via_spec *spec;
2231 int err;
2232
2233 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002234 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002235 if (spec == NULL)
2236 return -ENOMEM;
2237
Takashi Iwai620e2b22011-06-17 17:19:19 +02002238 spec->aa_mix_nid = 0x18;
2239
Takashi Iwai12daef62011-06-18 17:45:49 +02002240 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002241 if (err < 0) {
2242 via_free(codec);
2243 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002244 }
2245
Joseph Chanc577b8a2006-11-29 15:29:40 +01002246 codec->patch_ops = via_patch_ops;
2247
Joseph Chanc577b8a2006-11-29 15:29:40 +01002248 return 0;
2249}
2250/*
2251 * generic initialization of ADC, input mixers and output mixers
2252 */
Joseph Chanc577b8a2006-11-29 15:29:40 +01002253static int patch_vt1709_6ch(struct hda_codec *codec)
2254{
2255 struct via_spec *spec;
2256 int err;
2257
2258 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002259 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002260 if (spec == NULL)
2261 return -ENOMEM;
2262
Takashi Iwai620e2b22011-06-17 17:19:19 +02002263 spec->aa_mix_nid = 0x18;
2264
Takashi Iwai12daef62011-06-18 17:45:49 +02002265 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002266 if (err < 0) {
2267 via_free(codec);
2268 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002269 }
2270
Joseph Chanc577b8a2006-11-29 15:29:40 +01002271 codec->patch_ops = via_patch_ops;
2272
Josepch Chanf7278fd2007-12-13 16:40:40 +01002273 return 0;
2274}
2275
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002276static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2277{
2278 struct via_spec *spec = codec->spec;
2279 int imux_is_smixer;
2280 unsigned int parm;
2281 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002282 if ((spec->codec_type != VT1708B_4CH) &&
2283 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002284 is_8ch = 1;
2285
2286 /* SW0 (17h) = stereo mixer */
2287 imux_is_smixer =
2288 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2289 == ((spec->codec_type == VT1708S) ? 5 : 0));
2290 /* inputs */
2291 /* PW 1/2/5 (1ah/1bh/1eh) */
2292 parm = AC_PWRST_D3;
2293 set_pin_power_state(codec, 0x1a, &parm);
2294 set_pin_power_state(codec, 0x1b, &parm);
2295 set_pin_power_state(codec, 0x1e, &parm);
2296 if (imux_is_smixer)
2297 parm = AC_PWRST_D0;
2298 /* SW0 (17h), AIW 0/1 (13h/14h) */
2299 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2300 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2301 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2302
2303 /* outputs */
2304 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2305 parm = AC_PWRST_D3;
2306 set_pin_power_state(codec, 0x19, &parm);
2307 if (spec->smart51_enabled)
2308 set_pin_power_state(codec, 0x1b, &parm);
2309 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2310 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2311
2312 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2313 if (is_8ch) {
2314 parm = AC_PWRST_D3;
2315 set_pin_power_state(codec, 0x22, &parm);
2316 if (spec->smart51_enabled)
2317 set_pin_power_state(codec, 0x1a, &parm);
2318 snd_hda_codec_write(codec, 0x26, 0,
2319 AC_VERB_SET_POWER_STATE, parm);
2320 snd_hda_codec_write(codec, 0x24, 0,
2321 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002322 } else if (codec->vendor_id == 0x11064397) {
2323 /* PW7(23h), SW2(27h), AOW2(25h) */
2324 parm = AC_PWRST_D3;
2325 set_pin_power_state(codec, 0x23, &parm);
2326 if (spec->smart51_enabled)
2327 set_pin_power_state(codec, 0x1a, &parm);
2328 snd_hda_codec_write(codec, 0x27, 0,
2329 AC_VERB_SET_POWER_STATE, parm);
2330 snd_hda_codec_write(codec, 0x25, 0,
2331 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002332 }
2333
2334 /* PW 3/4/7 (1ch/1dh/23h) */
2335 parm = AC_PWRST_D3;
2336 /* force to D0 for internal Speaker */
2337 set_pin_power_state(codec, 0x1c, &parm);
2338 set_pin_power_state(codec, 0x1d, &parm);
2339 if (is_8ch)
2340 set_pin_power_state(codec, 0x23, &parm);
2341
2342 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2343 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2344 imux_is_smixer ? AC_PWRST_D0 : parm);
2345 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2346 if (is_8ch) {
2347 snd_hda_codec_write(codec, 0x25, 0,
2348 AC_VERB_SET_POWER_STATE, parm);
2349 snd_hda_codec_write(codec, 0x27, 0,
2350 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002351 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2352 snd_hda_codec_write(codec, 0x25, 0,
2353 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002354}
2355
Lydia Wang518bf3b2009-10-10 19:07:29 +08002356static int patch_vt1708S(struct hda_codec *codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002357static int patch_vt1708B_8ch(struct hda_codec *codec)
2358{
2359 struct via_spec *spec;
2360 int err;
2361
Lydia Wang518bf3b2009-10-10 19:07:29 +08002362 if (get_codec_type(codec) == VT1708BCE)
2363 return patch_vt1708S(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002364 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002365 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002366 if (spec == NULL)
2367 return -ENOMEM;
2368
Takashi Iwai620e2b22011-06-17 17:19:19 +02002369 spec->aa_mix_nid = 0x16;
2370
Josepch Chanf7278fd2007-12-13 16:40:40 +01002371 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002372 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002373 if (err < 0) {
2374 via_free(codec);
2375 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002376 }
2377
Josepch Chanf7278fd2007-12-13 16:40:40 +01002378 codec->patch_ops = via_patch_ops;
2379
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002380 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2381
Josepch Chanf7278fd2007-12-13 16:40:40 +01002382 return 0;
2383}
2384
2385static int patch_vt1708B_4ch(struct hda_codec *codec)
2386{
2387 struct via_spec *spec;
2388 int err;
2389
2390 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002391 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002392 if (spec == NULL)
2393 return -ENOMEM;
2394
Josepch Chanf7278fd2007-12-13 16:40:40 +01002395 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002396 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002397 if (err < 0) {
2398 via_free(codec);
2399 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002400 }
2401
Josepch Chanf7278fd2007-12-13 16:40:40 +01002402 codec->patch_ops = via_patch_ops;
2403
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002404 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2405
Joseph Chanc577b8a2006-11-29 15:29:40 +01002406 return 0;
2407}
2408
Harald Welted949cac2008-09-09 15:56:01 +08002409/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002410static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002411 /* Enable Mic Boost Volume backdoor */
2412 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002413 /* don't bybass mixer */
2414 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002415 { }
2416};
2417
Takashi Iwai9da29272009-05-07 16:31:14 +02002418/* fill out digital output widgets; one for master and one for slave outputs */
2419static void fill_dig_outs(struct hda_codec *codec)
2420{
2421 struct via_spec *spec = codec->spec;
2422 int i;
2423
2424 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2425 hda_nid_t nid;
2426 int conn;
2427
2428 nid = spec->autocfg.dig_out_pins[i];
2429 if (!nid)
2430 continue;
2431 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2432 if (conn < 1)
2433 continue;
2434 if (!spec->multiout.dig_out_nid)
2435 spec->multiout.dig_out_nid = nid;
2436 else {
2437 spec->slave_dig_outs[0] = nid;
2438 break; /* at most two dig outs */
2439 }
2440 }
2441}
2442
Takashi Iwai12daef62011-06-18 17:45:49 +02002443static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002444{
2445 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002446 hda_nid_t dig_nid;
2447 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002448
Takashi Iwai12daef62011-06-18 17:45:49 +02002449 if (!spec->autocfg.dig_in_pin)
2450 return;
Harald Welted949cac2008-09-09 15:56:01 +08002451
Takashi Iwai12daef62011-06-18 17:45:49 +02002452 dig_nid = codec->start_nid;
2453 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2454 unsigned int wcaps = get_wcaps(codec, dig_nid);
2455 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2456 continue;
2457 if (!(wcaps & AC_WCAP_DIGITAL))
2458 continue;
2459 if (!(wcaps & AC_WCAP_CONN_LIST))
2460 continue;
2461 err = get_connection_index(codec, dig_nid,
2462 spec->autocfg.dig_in_pin);
2463 if (err >= 0) {
2464 spec->dig_in_nid = dig_nid;
2465 break;
2466 }
2467 }
Harald Welted949cac2008-09-09 15:56:01 +08002468}
2469
Lydia Wang6369bcf2009-10-10 19:08:31 +08002470static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2471 int offset, int num_steps, int step_size)
2472{
2473 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2474 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2475 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2476 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2477 (0 << AC_AMPCAP_MUTE_SHIFT));
2478}
2479
Harald Welted949cac2008-09-09 15:56:01 +08002480static int patch_vt1708S(struct hda_codec *codec)
2481{
2482 struct via_spec *spec;
2483 int err;
2484
2485 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002486 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002487 if (spec == NULL)
2488 return -ENOMEM;
2489
Takashi Iwai620e2b22011-06-17 17:19:19 +02002490 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002491 override_mic_boost(codec, 0x1a, 0, 3, 40);
2492 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002493
Harald Welted949cac2008-09-09 15:56:01 +08002494 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002495 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002496 if (err < 0) {
2497 via_free(codec);
2498 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002499 }
2500
Takashi Iwai096a8852011-06-20 12:09:02 +02002501 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002502
Harald Welted949cac2008-09-09 15:56:01 +08002503 codec->patch_ops = via_patch_ops;
2504
Lydia Wang518bf3b2009-10-10 19:07:29 +08002505 /* correct names for VT1708BCE */
2506 if (get_codec_type(codec) == VT1708BCE) {
2507 kfree(codec->chip_name);
2508 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2509 snprintf(codec->bus->card->mixername,
2510 sizeof(codec->bus->card->mixername),
2511 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002512 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002513 /* correct names for VT1705 */
2514 if (codec->vendor_id == 0x11064397) {
2515 kfree(codec->chip_name);
2516 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2517 snprintf(codec->bus->card->mixername,
2518 sizeof(codec->bus->card->mixername),
2519 "%s %s", codec->vendor_name, codec->chip_name);
2520 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002521 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002522 return 0;
2523}
2524
2525/* Patch for VT1702 */
2526
Takashi Iwai096a8852011-06-20 12:09:02 +02002527static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002528 /* mixer enable */
2529 {0x1, 0xF88, 0x3},
2530 /* GPIO 0~2 */
2531 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002532 { }
2533};
2534
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002535static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2536{
2537 int imux_is_smixer =
2538 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2539 unsigned int parm;
2540 /* inputs */
2541 /* PW 1/2/5 (14h/15h/18h) */
2542 parm = AC_PWRST_D3;
2543 set_pin_power_state(codec, 0x14, &parm);
2544 set_pin_power_state(codec, 0x15, &parm);
2545 set_pin_power_state(codec, 0x18, &parm);
2546 if (imux_is_smixer)
2547 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2548 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2549 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2550 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2551 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2552 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2553
2554 /* outputs */
2555 /* PW 3/4 (16h/17h) */
2556 parm = AC_PWRST_D3;
2557 set_pin_power_state(codec, 0x17, &parm);
2558 set_pin_power_state(codec, 0x16, &parm);
2559 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2560 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2561 imux_is_smixer ? AC_PWRST_D0 : parm);
2562 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2563 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2564}
2565
Harald Welted949cac2008-09-09 15:56:01 +08002566static int patch_vt1702(struct hda_codec *codec)
2567{
2568 struct via_spec *spec;
2569 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002570
2571 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002572 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002573 if (spec == NULL)
2574 return -ENOMEM;
2575
Takashi Iwai620e2b22011-06-17 17:19:19 +02002576 spec->aa_mix_nid = 0x1a;
2577
Takashi Iwai12daef62011-06-18 17:45:49 +02002578 /* limit AA path volume to 0 dB */
2579 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2580 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2581 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2582 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2583 (1 << AC_AMPCAP_MUTE_SHIFT));
2584
Harald Welted949cac2008-09-09 15:56:01 +08002585 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002586 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002587 if (err < 0) {
2588 via_free(codec);
2589 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002590 }
2591
Takashi Iwai096a8852011-06-20 12:09:02 +02002592 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002593
Harald Welted949cac2008-09-09 15:56:01 +08002594 codec->patch_ops = via_patch_ops;
2595
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002596 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002597 return 0;
2598}
2599
Lydia Wangeb7188c2009-10-10 19:08:34 +08002600/* Patch for VT1718S */
2601
Takashi Iwai096a8852011-06-20 12:09:02 +02002602static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002603 /* Enable MW0 adjust Gain 5 */
2604 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002605 /* Enable Boost Volume backdoor */
2606 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002607
Lydia Wangeb7188c2009-10-10 19:08:34 +08002608 { }
2609};
2610
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002611static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2612{
2613 struct via_spec *spec = codec->spec;
2614 int imux_is_smixer;
2615 unsigned int parm;
2616 /* MUX6 (1eh) = stereo mixer */
2617 imux_is_smixer =
2618 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2619 /* inputs */
2620 /* PW 5/6/7 (29h/2ah/2bh) */
2621 parm = AC_PWRST_D3;
2622 set_pin_power_state(codec, 0x29, &parm);
2623 set_pin_power_state(codec, 0x2a, &parm);
2624 set_pin_power_state(codec, 0x2b, &parm);
2625 if (imux_is_smixer)
2626 parm = AC_PWRST_D0;
2627 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2628 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2629 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2630 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2631 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2632
2633 /* outputs */
2634 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2635 parm = AC_PWRST_D3;
2636 set_pin_power_state(codec, 0x27, &parm);
2637 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2638 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2639
2640 /* PW2 (26h), AOW2 (ah) */
2641 parm = AC_PWRST_D3;
2642 set_pin_power_state(codec, 0x26, &parm);
2643 if (spec->smart51_enabled)
2644 set_pin_power_state(codec, 0x2b, &parm);
2645 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2646
2647 /* PW0 (24h), AOW0 (8h) */
2648 parm = AC_PWRST_D3;
2649 set_pin_power_state(codec, 0x24, &parm);
2650 if (!spec->hp_independent_mode) /* check for redirected HP */
2651 set_pin_power_state(codec, 0x28, &parm);
2652 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2653 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2654 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2655 imux_is_smixer ? AC_PWRST_D0 : parm);
2656
2657 /* PW1 (25h), AOW1 (9h) */
2658 parm = AC_PWRST_D3;
2659 set_pin_power_state(codec, 0x25, &parm);
2660 if (spec->smart51_enabled)
2661 set_pin_power_state(codec, 0x2a, &parm);
2662 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2663
2664 if (spec->hp_independent_mode) {
2665 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2666 parm = AC_PWRST_D3;
2667 set_pin_power_state(codec, 0x28, &parm);
2668 snd_hda_codec_write(codec, 0x1b, 0,
2669 AC_VERB_SET_POWER_STATE, parm);
2670 snd_hda_codec_write(codec, 0x34, 0,
2671 AC_VERB_SET_POWER_STATE, parm);
2672 snd_hda_codec_write(codec, 0xc, 0,
2673 AC_VERB_SET_POWER_STATE, parm);
2674 }
2675}
2676
Lydia Wangeb7188c2009-10-10 19:08:34 +08002677static int patch_vt1718S(struct hda_codec *codec)
2678{
2679 struct via_spec *spec;
2680 int err;
2681
2682 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002683 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002684 if (spec == NULL)
2685 return -ENOMEM;
2686
Takashi Iwai620e2b22011-06-17 17:19:19 +02002687 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002688 override_mic_boost(codec, 0x2b, 0, 3, 40);
2689 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002690
Lydia Wangeb7188c2009-10-10 19:08:34 +08002691 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002692 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002693 if (err < 0) {
2694 via_free(codec);
2695 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002696 }
2697
Takashi Iwai096a8852011-06-20 12:09:02 +02002698 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002699
Lydia Wangeb7188c2009-10-10 19:08:34 +08002700 codec->patch_ops = via_patch_ops;
2701
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002702 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2703
Lydia Wangeb7188c2009-10-10 19:08:34 +08002704 return 0;
2705}
Lydia Wangf3db4232009-10-10 19:08:41 +08002706
2707/* Patch for VT1716S */
2708
2709static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2710 struct snd_ctl_elem_info *uinfo)
2711{
2712 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2713 uinfo->count = 1;
2714 uinfo->value.integer.min = 0;
2715 uinfo->value.integer.max = 1;
2716 return 0;
2717}
2718
2719static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2720 struct snd_ctl_elem_value *ucontrol)
2721{
2722 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2723 int index = 0;
2724
2725 index = snd_hda_codec_read(codec, 0x26, 0,
2726 AC_VERB_GET_CONNECT_SEL, 0);
2727 if (index != -1)
2728 *ucontrol->value.integer.value = index;
2729
2730 return 0;
2731}
2732
2733static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
2734 struct snd_ctl_elem_value *ucontrol)
2735{
2736 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2737 struct via_spec *spec = codec->spec;
2738 int index = *ucontrol->value.integer.value;
2739
2740 snd_hda_codec_write(codec, 0x26, 0,
2741 AC_VERB_SET_CONNECT_SEL, index);
2742 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002743 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002744 return 1;
2745}
2746
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002747static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002748 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
2749 {
2750 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2751 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002752 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08002753 .count = 1,
2754 .info = vt1716s_dmic_info,
2755 .get = vt1716s_dmic_get,
2756 .put = vt1716s_dmic_put,
2757 },
2758 {} /* end */
2759};
2760
2761
2762/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02002763static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002764 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
2765 { } /* end */
2766};
2767
Takashi Iwai096a8852011-06-20 12:09:02 +02002768static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08002769 /* Enable Boost Volume backdoor */
2770 {0x1, 0xf8a, 0x80},
2771 /* don't bybass mixer */
2772 {0x1, 0xf88, 0xc0},
2773 /* Enable mono output */
2774 {0x1, 0xf90, 0x08},
2775 { }
2776};
2777
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002778static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
2779{
2780 struct via_spec *spec = codec->spec;
2781 int imux_is_smixer;
2782 unsigned int parm;
2783 unsigned int mono_out, present;
2784 /* SW0 (17h) = stereo mixer */
2785 imux_is_smixer =
2786 (snd_hda_codec_read(codec, 0x17, 0,
2787 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
2788 /* inputs */
2789 /* PW 1/2/5 (1ah/1bh/1eh) */
2790 parm = AC_PWRST_D3;
2791 set_pin_power_state(codec, 0x1a, &parm);
2792 set_pin_power_state(codec, 0x1b, &parm);
2793 set_pin_power_state(codec, 0x1e, &parm);
2794 if (imux_is_smixer)
2795 parm = AC_PWRST_D0;
2796 /* SW0 (17h), AIW0(13h) */
2797 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2798 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2799
2800 parm = AC_PWRST_D3;
2801 set_pin_power_state(codec, 0x1e, &parm);
2802 /* PW11 (22h) */
2803 if (spec->dmic_enabled)
2804 set_pin_power_state(codec, 0x22, &parm);
2805 else
2806 snd_hda_codec_write(codec, 0x22, 0,
2807 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
2808
2809 /* SW2(26h), AIW1(14h) */
2810 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
2811 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2812
2813 /* outputs */
2814 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2815 parm = AC_PWRST_D3;
2816 set_pin_power_state(codec, 0x19, &parm);
2817 /* Smart 5.1 PW2(1bh) */
2818 if (spec->smart51_enabled)
2819 set_pin_power_state(codec, 0x1b, &parm);
2820 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2821 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2822
2823 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
2824 parm = AC_PWRST_D3;
2825 set_pin_power_state(codec, 0x23, &parm);
2826 /* Smart 5.1 PW1(1ah) */
2827 if (spec->smart51_enabled)
2828 set_pin_power_state(codec, 0x1a, &parm);
2829 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
2830
2831 /* Smart 5.1 PW5(1eh) */
2832 if (spec->smart51_enabled)
2833 set_pin_power_state(codec, 0x1e, &parm);
2834 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
2835
2836 /* Mono out */
2837 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
2838 present = snd_hda_jack_detect(codec, 0x1c);
2839
2840 if (present)
2841 mono_out = 0;
2842 else {
2843 present = snd_hda_jack_detect(codec, 0x1d);
2844 if (!spec->hp_independent_mode && present)
2845 mono_out = 0;
2846 else
2847 mono_out = 1;
2848 }
2849 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
2850 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
2851 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
2852 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
2853
2854 /* PW 3/4 (1ch/1dh) */
2855 parm = AC_PWRST_D3;
2856 set_pin_power_state(codec, 0x1c, &parm);
2857 set_pin_power_state(codec, 0x1d, &parm);
2858 /* HP Independent Mode, power on AOW3 */
2859 if (spec->hp_independent_mode)
2860 snd_hda_codec_write(codec, 0x25, 0,
2861 AC_VERB_SET_POWER_STATE, parm);
2862
2863 /* force to D0 for internal Speaker */
2864 /* MW0 (16h), AOW0 (10h) */
2865 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2866 imux_is_smixer ? AC_PWRST_D0 : parm);
2867 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
2868 mono_out ? AC_PWRST_D0 : parm);
2869}
2870
Lydia Wangf3db4232009-10-10 19:08:41 +08002871static int patch_vt1716S(struct hda_codec *codec)
2872{
2873 struct via_spec *spec;
2874 int err;
2875
2876 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002877 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002878 if (spec == NULL)
2879 return -ENOMEM;
2880
Takashi Iwai620e2b22011-06-17 17:19:19 +02002881 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002882 override_mic_boost(codec, 0x1a, 0, 3, 40);
2883 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002884
Lydia Wangf3db4232009-10-10 19:08:41 +08002885 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002886 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08002887 if (err < 0) {
2888 via_free(codec);
2889 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08002890 }
2891
Takashi Iwai096a8852011-06-20 12:09:02 +02002892 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08002893
Lydia Wangf3db4232009-10-10 19:08:41 +08002894 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
2895 spec->num_mixers++;
2896
2897 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
2898
2899 codec->patch_ops = via_patch_ops;
2900
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002901 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08002902 return 0;
2903}
Lydia Wang25eaba22009-10-10 19:08:43 +08002904
2905/* for vt2002P */
2906
Takashi Iwai096a8852011-06-20 12:09:02 +02002907static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08002908 /* Class-D speaker related verbs */
2909 {0x1, 0xfe0, 0x4},
2910 {0x1, 0xfe9, 0x80},
2911 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08002912 /* Enable Boost Volume backdoor */
2913 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08002914 /* Enable AOW0 to MW9 */
2915 {0x1, 0xfb8, 0x88},
2916 { }
2917};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002918
Takashi Iwai096a8852011-06-20 12:09:02 +02002919static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08002920 /* Enable Boost Volume backdoor */
2921 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08002922 /* Enable AOW0 to MW9 */
2923 {0x1, 0xfb8, 0x88},
2924 { }
2925};
Lydia Wang25eaba22009-10-10 19:08:43 +08002926
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002927static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
2928{
2929 struct via_spec *spec = codec->spec;
2930 int imux_is_smixer;
2931 unsigned int parm;
2932 unsigned int present;
2933 /* MUX9 (1eh) = stereo mixer */
2934 imux_is_smixer =
2935 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2936 /* inputs */
2937 /* PW 5/6/7 (29h/2ah/2bh) */
2938 parm = AC_PWRST_D3;
2939 set_pin_power_state(codec, 0x29, &parm);
2940 set_pin_power_state(codec, 0x2a, &parm);
2941 set_pin_power_state(codec, 0x2b, &parm);
2942 parm = AC_PWRST_D0;
2943 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
2944 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2945 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2946 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2947 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2948
2949 /* outputs */
2950 /* AOW0 (8h)*/
2951 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2952
Lydia Wang118909562011-03-23 17:57:34 +08002953 if (spec->codec_type == VT1802) {
2954 /* PW4 (28h), MW4 (18h), MUX4(38h) */
2955 parm = AC_PWRST_D3;
2956 set_pin_power_state(codec, 0x28, &parm);
2957 snd_hda_codec_write(codec, 0x18, 0,
2958 AC_VERB_SET_POWER_STATE, parm);
2959 snd_hda_codec_write(codec, 0x38, 0,
2960 AC_VERB_SET_POWER_STATE, parm);
2961 } else {
2962 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
2963 parm = AC_PWRST_D3;
2964 set_pin_power_state(codec, 0x26, &parm);
2965 snd_hda_codec_write(codec, 0x1c, 0,
2966 AC_VERB_SET_POWER_STATE, parm);
2967 snd_hda_codec_write(codec, 0x37, 0,
2968 AC_VERB_SET_POWER_STATE, parm);
2969 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002970
Lydia Wang118909562011-03-23 17:57:34 +08002971 if (spec->codec_type == VT1802) {
2972 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
2973 parm = AC_PWRST_D3;
2974 set_pin_power_state(codec, 0x25, &parm);
2975 snd_hda_codec_write(codec, 0x15, 0,
2976 AC_VERB_SET_POWER_STATE, parm);
2977 snd_hda_codec_write(codec, 0x35, 0,
2978 AC_VERB_SET_POWER_STATE, parm);
2979 } else {
2980 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
2981 parm = AC_PWRST_D3;
2982 set_pin_power_state(codec, 0x25, &parm);
2983 snd_hda_codec_write(codec, 0x19, 0,
2984 AC_VERB_SET_POWER_STATE, parm);
2985 snd_hda_codec_write(codec, 0x35, 0,
2986 AC_VERB_SET_POWER_STATE, parm);
2987 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002988
2989 if (spec->hp_independent_mode)
2990 snd_hda_codec_write(codec, 0x9, 0,
2991 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2992
2993 /* Class-D */
2994 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
2995 present = snd_hda_jack_detect(codec, 0x25);
2996
2997 parm = AC_PWRST_D3;
2998 set_pin_power_state(codec, 0x24, &parm);
2999 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003000 if (spec->codec_type == VT1802)
3001 snd_hda_codec_write(codec, 0x14, 0,
3002 AC_VERB_SET_POWER_STATE, parm);
3003 else
3004 snd_hda_codec_write(codec, 0x18, 0,
3005 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003006 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3007
3008 /* Mono Out */
3009 present = snd_hda_jack_detect(codec, 0x26);
3010
3011 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003012 if (spec->codec_type == VT1802) {
3013 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3014 snd_hda_codec_write(codec, 0x33, 0,
3015 AC_VERB_SET_POWER_STATE, parm);
3016 snd_hda_codec_write(codec, 0x1c, 0,
3017 AC_VERB_SET_POWER_STATE, parm);
3018 snd_hda_codec_write(codec, 0x3c, 0,
3019 AC_VERB_SET_POWER_STATE, parm);
3020 } else {
3021 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3022 snd_hda_codec_write(codec, 0x31, 0,
3023 AC_VERB_SET_POWER_STATE, parm);
3024 snd_hda_codec_write(codec, 0x17, 0,
3025 AC_VERB_SET_POWER_STATE, parm);
3026 snd_hda_codec_write(codec, 0x3b, 0,
3027 AC_VERB_SET_POWER_STATE, parm);
3028 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003029 /* MW9 (21h) */
3030 if (imux_is_smixer || !is_aa_path_mute(codec))
3031 snd_hda_codec_write(codec, 0x21, 0,
3032 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3033 else
3034 snd_hda_codec_write(codec, 0x21, 0,
3035 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3036}
Lydia Wang25eaba22009-10-10 19:08:43 +08003037
3038/* patch for vt2002P */
3039static int patch_vt2002P(struct hda_codec *codec)
3040{
3041 struct via_spec *spec;
3042 int err;
3043
3044 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003045 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003046 if (spec == NULL)
3047 return -ENOMEM;
3048
Takashi Iwai620e2b22011-06-17 17:19:19 +02003049 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003050 override_mic_boost(codec, 0x2b, 0, 3, 40);
3051 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003052
Lydia Wang25eaba22009-10-10 19:08:43 +08003053 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003054 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003055 if (err < 0) {
3056 via_free(codec);
3057 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003058 }
3059
Lydia Wang118909562011-03-23 17:57:34 +08003060 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003061 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003062 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003063 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003064
Lydia Wang25eaba22009-10-10 19:08:43 +08003065 codec->patch_ops = via_patch_ops;
3066
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003067 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003068 return 0;
3069}
Lydia Wangab6734e2009-10-10 19:08:46 +08003070
3071/* for vt1812 */
3072
Takashi Iwai096a8852011-06-20 12:09:02 +02003073static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003074 /* Enable Boost Volume backdoor */
3075 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003076 /* Enable AOW0 to MW9 */
3077 {0x1, 0xfb8, 0xa8},
3078 { }
3079};
3080
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003081static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3082{
3083 struct via_spec *spec = codec->spec;
3084 int imux_is_smixer =
3085 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3086 unsigned int parm;
3087 unsigned int present;
3088 /* MUX10 (1eh) = stereo mixer */
3089 imux_is_smixer =
3090 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3091 /* inputs */
3092 /* PW 5/6/7 (29h/2ah/2bh) */
3093 parm = AC_PWRST_D3;
3094 set_pin_power_state(codec, 0x29, &parm);
3095 set_pin_power_state(codec, 0x2a, &parm);
3096 set_pin_power_state(codec, 0x2b, &parm);
3097 parm = AC_PWRST_D0;
3098 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3099 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3100 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3101 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3102 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3103
3104 /* outputs */
3105 /* AOW0 (8h)*/
3106 snd_hda_codec_write(codec, 0x8, 0,
3107 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3108
3109 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3110 parm = AC_PWRST_D3;
3111 set_pin_power_state(codec, 0x28, &parm);
3112 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3113 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3114
3115 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3116 parm = AC_PWRST_D3;
3117 set_pin_power_state(codec, 0x25, &parm);
3118 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3119 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3120 if (spec->hp_independent_mode)
3121 snd_hda_codec_write(codec, 0x9, 0,
3122 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3123
3124 /* Internal Speaker */
3125 /* PW0 (24h), MW0(14h), MUX0(34h) */
3126 present = snd_hda_jack_detect(codec, 0x25);
3127
3128 parm = AC_PWRST_D3;
3129 set_pin_power_state(codec, 0x24, &parm);
3130 if (present) {
3131 snd_hda_codec_write(codec, 0x14, 0,
3132 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3133 snd_hda_codec_write(codec, 0x34, 0,
3134 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3135 } else {
3136 snd_hda_codec_write(codec, 0x14, 0,
3137 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3138 snd_hda_codec_write(codec, 0x34, 0,
3139 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3140 }
3141
3142
3143 /* Mono Out */
3144 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3145 present = snd_hda_jack_detect(codec, 0x28);
3146
3147 parm = AC_PWRST_D3;
3148 set_pin_power_state(codec, 0x31, &parm);
3149 if (present) {
3150 snd_hda_codec_write(codec, 0x1c, 0,
3151 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3152 snd_hda_codec_write(codec, 0x3c, 0,
3153 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3154 snd_hda_codec_write(codec, 0x3e, 0,
3155 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3156 } else {
3157 snd_hda_codec_write(codec, 0x1c, 0,
3158 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3159 snd_hda_codec_write(codec, 0x3c, 0,
3160 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3161 snd_hda_codec_write(codec, 0x3e, 0,
3162 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3163 }
3164
3165 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3166 parm = AC_PWRST_D3;
3167 set_pin_power_state(codec, 0x33, &parm);
3168 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3169 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3170
3171}
Lydia Wangab6734e2009-10-10 19:08:46 +08003172
3173/* patch for vt1812 */
3174static int patch_vt1812(struct hda_codec *codec)
3175{
3176 struct via_spec *spec;
3177 int err;
3178
3179 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003180 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003181 if (spec == NULL)
3182 return -ENOMEM;
3183
Takashi Iwai620e2b22011-06-17 17:19:19 +02003184 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003185 override_mic_boost(codec, 0x2b, 0, 3, 40);
3186 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003187
Lydia Wangab6734e2009-10-10 19:08:46 +08003188 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003189 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003190 if (err < 0) {
3191 via_free(codec);
3192 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003193 }
3194
Takashi Iwai096a8852011-06-20 12:09:02 +02003195 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003196
Lydia Wangab6734e2009-10-10 19:08:46 +08003197 codec->patch_ops = via_patch_ops;
3198
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003199 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003200 return 0;
3201}
3202
Joseph Chanc577b8a2006-11-29 15:29:40 +01003203/*
3204 * patch entries
3205 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003206static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003207 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3208 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3209 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3210 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3211 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003212 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003213 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003214 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003215 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003216 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003217 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003218 .patch = patch_vt1709_10ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003219 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003220 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003221 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003222 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003223 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003224 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003225 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003226 .patch = patch_vt1709_6ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003227 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003228 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003229 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003230 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003231 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003232 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003233 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003234 .patch = patch_vt1708B_8ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003235 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003236 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003237 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003238 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003239 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003240 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003241 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Josepch Chanf7278fd2007-12-13 16:40:40 +01003242 .patch = patch_vt1708B_4ch},
Takashi Iwai3218c172008-12-18 09:17:56 +01003243 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003244 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003245 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003246 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003247 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003248 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003249 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003250 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003251 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003252 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003253 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003254 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003255 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003256 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003257 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003258 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003259 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003260 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003261 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003262 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003263 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003264 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003265 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003266 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003267 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003268 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003269 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003270 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003271 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003272 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003273 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003274 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003275 { .id = 0x11060428, .name = "VT1718S",
3276 .patch = patch_vt1718S},
3277 { .id = 0x11064428, .name = "VT1718S",
3278 .patch = patch_vt1718S},
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08003279 { .id = 0x11060441, .name = "VT2020",
3280 .patch = patch_vt1718S},
3281 { .id = 0x11064441, .name = "VT1828S",
3282 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003283 { .id = 0x11060433, .name = "VT1716S",
3284 .patch = patch_vt1716S},
3285 { .id = 0x1106a721, .name = "VT1716S",
3286 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003287 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3288 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003289 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003290 { .id = 0x11060440, .name = "VT1818S",
3291 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003292 { .id = 0x11060446, .name = "VT1802",
3293 .patch = patch_vt2002P},
3294 { .id = 0x11068446, .name = "VT1802",
3295 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003296 {} /* terminator */
3297};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003298
3299MODULE_ALIAS("snd-hda-codec-id:1106*");
3300
3301static struct hda_codec_preset_list via_list = {
3302 .preset = snd_hda_preset_via,
3303 .owner = THIS_MODULE,
3304};
3305
3306MODULE_LICENSE("GPL");
3307MODULE_DESCRIPTION("VIA HD-audio codec");
3308
3309static int __init patch_via_init(void)
3310{
3311 return snd_hda_add_codec_preset(&via_list);
3312}
3313
3314static void __exit patch_via_exit(void)
3315{
3316 snd_hda_delete_codec_preset(&via_list);
3317}
3318
3319module_init(patch_via_init)
3320module_exit(patch_via_exit)