blob: 7305f4de07ece7269cd83a46f8674b4691d686a4 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010052#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080053#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010054#include "hda_codec.h"
55#include "hda_local.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010056
Joseph Chanc577b8a2006-11-29 15:29:40 +010057/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080058#define VT1708_HP_PIN_NID 0x20
59#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010060
Harald Welted7426322008-09-15 22:43:23 +080061enum VIA_HDA_CODEC {
62 UNKNOWN = -1,
63 VT1708,
64 VT1709_10CH,
65 VT1709_6CH,
66 VT1708B_8CH,
67 VT1708B_4CH,
68 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080069 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080070 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080071 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080072 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080073 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080074 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080075 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080076 CODEC_TYPES,
77};
78
Lydia Wang118909562011-03-23 17:57:34 +080079#define VT2002P_COMPATIBLE(spec) \
80 ((spec)->codec_type == VT2002P ||\
81 (spec)->codec_type == VT1812 ||\
82 (spec)->codec_type == VT1802)
83
Takashi Iwai8e3679d2011-06-21 09:01:36 +020084#define MAX_NID_PATH_DEPTH 5
85
Takashi Iwai09a9ad692011-06-21 15:57:44 +020086/* output-path: DAC -> ... -> pin
87 * idx[] contains the source index number of the next widget;
88 * e.g. idx[0] is the index of the DAC selected by path[1] widget
89 * multi[] indicates whether it's a selector widget with multi-connectors
90 * (i.e. the connection selection is mandatory)
91 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
92 */
Takashi Iwai4a796162011-06-17 17:53:38 +020093struct nid_path {
94 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020095 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad692011-06-21 15:57:44 +020096 unsigned char idx[MAX_NID_PATH_DEPTH];
97 unsigned char multi[MAX_NID_PATH_DEPTH];
98 unsigned int vol_ctl;
99 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200100};
101
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200102/* input-path */
103struct via_input {
104 hda_nid_t pin; /* input-pin or aa-mix */
105 int adc_idx; /* ADC index to be used */
106 int mux_idx; /* MUX index (if any) */
107 const char *label; /* input-source label */
108};
109
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800110struct via_spec {
111 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200112 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800113 unsigned int num_mixers;
114
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200115 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800116 unsigned int num_iverbs;
117
Takashi Iwai82673bc2011-06-17 16:24:21 +0200118 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200119 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200120 const struct hda_pcm_stream *stream_analog_playback;
121 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800122
Takashi Iwai82673bc2011-06-17 16:24:21 +0200123 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200124 const struct hda_pcm_stream *stream_digital_playback;
125 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800126
127 /* playback */
128 struct hda_multi_out multiout;
129 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200130 hda_nid_t hp_dac_nid;
Takashi Iwai25250502011-06-30 17:24:47 +0200131 bool hp_indep_shared; /* indep HP-DAC is shared with side ch */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200132 int num_active_streams;
Lydia Wangc4394f52011-07-04 16:54:15 +0800133 int dac_mixer_idx;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800134
Takashi Iwai4a796162011-06-17 17:53:38 +0200135 struct nid_path out_path[4];
136 struct nid_path hp_path;
137 struct nid_path hp_dep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200138 struct nid_path speaker_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200139
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800140 /* capture */
141 unsigned int num_adc_nids;
Takashi Iwaia766d0d2011-06-17 09:01:29 +0200142 hda_nid_t adc_nids[3];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800143 hda_nid_t mux_nids[3];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200144 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800145 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800146
147 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200148 bool dyn_adc_switch;
149 int num_inputs;
150 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800151 unsigned int cur_mux[3];
152
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200153 /* dynamic ADC switching */
154 hda_nid_t cur_adc;
155 unsigned int cur_adc_stream_tag;
156 unsigned int cur_adc_format;
157
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800158 /* PCM information */
159 struct hda_pcm pcm_rec[3];
160
161 /* dynamic controls, init_verbs and input_mux */
162 struct auto_pin_cfg autocfg;
163 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800164 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
165
166 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800167 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800168 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200169 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800170 enum VIA_HDA_CODEC codec_type;
171
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200172 /* smart51 setup */
173 unsigned int smart51_nums;
174 hda_nid_t smart51_pins[2];
175 int smart51_idxs[2];
176 const char *smart51_labels[2];
177 unsigned int smart51_enabled;
178
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800179 /* work to check hp jack state */
180 struct hda_codec *codec;
181 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200182 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800183 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800184
185 void (*set_widgets_power_state)(struct hda_codec *codec);
186
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800187 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200188 int num_loopbacks;
189 struct hda_amp_list loopback_list[8];
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200190
191 /* bind capture-volume */
192 struct hda_bind_ctls *bind_cap_vol;
193 struct hda_bind_ctls *bind_cap_sw;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800194};
195
Lydia Wang0341ccd2011-03-22 16:25:03 +0800196static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100197static struct via_spec * via_new_spec(struct hda_codec *codec)
198{
199 struct via_spec *spec;
200
201 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
202 if (spec == NULL)
203 return NULL;
204
205 codec->spec = spec;
206 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800207 spec->codec_type = get_codec_type(codec);
208 /* VT1708BCE & VT1708S are almost same */
209 if (spec->codec_type == VT1708BCE)
210 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100211 return spec;
212}
213
Lydia Wang744ff5f2009-10-10 19:07:26 +0800214static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800215{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800216 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800217 u16 ven_id = vendor_id >> 16;
218 u16 dev_id = vendor_id & 0xffff;
219 enum VIA_HDA_CODEC codec_type;
220
221 /* get codec type */
222 if (ven_id != 0x1106)
223 codec_type = UNKNOWN;
224 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
225 codec_type = VT1708;
226 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
227 codec_type = VT1709_10CH;
228 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
229 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800230 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800231 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800232 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
233 codec_type = VT1708BCE;
234 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800235 codec_type = VT1708B_4CH;
236 else if ((dev_id & 0xfff) == 0x397
237 && (dev_id >> 12) < 8)
238 codec_type = VT1708S;
239 else if ((dev_id & 0xfff) == 0x398
240 && (dev_id >> 12) < 8)
241 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800242 else if ((dev_id & 0xfff) == 0x428
243 && (dev_id >> 12) < 8)
244 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800245 else if (dev_id == 0x0433 || dev_id == 0xa721)
246 codec_type = VT1716S;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +0800247 else if (dev_id == 0x0441 || dev_id == 0x4441)
248 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800249 else if (dev_id == 0x0438 || dev_id == 0x4438)
250 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800251 else if (dev_id == 0x0448)
252 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800253 else if (dev_id == 0x0440)
254 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800255 else if ((dev_id & 0xfff) == 0x446)
256 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800257 else
258 codec_type = UNKNOWN;
259 return codec_type;
260};
261
Lydia Wangec7e7e42011-03-24 12:43:44 +0800262#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800263#define VIA_HP_EVENT 0x01
264#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200265#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800266
Joseph Chanc577b8a2006-11-29 15:29:40 +0100267enum {
268 VIA_CTL_WIDGET_VOL,
269 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800270 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100271};
272
Takashi Iwaiada509e2011-06-20 15:40:19 +0200273static void analog_low_current_mode(struct hda_codec *codec);
274static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800275
276static void vt1708_start_hp_work(struct via_spec *spec)
277{
278 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
279 return;
280 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200281 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800282 if (!delayed_work_pending(&spec->vt1708_hp_work))
283 schedule_delayed_work(&spec->vt1708_hp_work,
284 msecs_to_jiffies(100));
285}
286
287static void vt1708_stop_hp_work(struct via_spec *spec)
288{
289 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
290 return;
291 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
292 && !is_aa_path_mute(spec->codec))
293 return;
294 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200295 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100296 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800297}
Lydia Wangf5271102009-10-10 19:07:35 +0800298
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800299static void set_widgets_power_state(struct hda_codec *codec)
300{
301 struct via_spec *spec = codec->spec;
302 if (spec->set_widgets_power_state)
303 spec->set_widgets_power_state(codec);
304}
Lydia Wang25eaba22009-10-10 19:08:43 +0800305
Lydia Wangf5271102009-10-10 19:07:35 +0800306static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
307 struct snd_ctl_elem_value *ucontrol)
308{
309 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
310 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
311
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800312 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200313 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800314 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
315 if (is_aa_path_mute(codec))
316 vt1708_start_hp_work(codec->spec);
317 else
318 vt1708_stop_hp_work(codec->spec);
319 }
Lydia Wangf5271102009-10-10 19:07:35 +0800320 return change;
321}
322
323/* modify .put = snd_hda_mixer_amp_switch_put */
324#define ANALOG_INPUT_MUTE \
325 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
326 .name = NULL, \
327 .index = 0, \
328 .info = snd_hda_mixer_amp_switch_info, \
329 .get = snd_hda_mixer_amp_switch_get, \
330 .put = analog_input_switch_put, \
331 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
332
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200333static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100334 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
335 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800336 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100337};
338
Lydia Wangab6734e2009-10-10 19:08:46 +0800339
Joseph Chanc577b8a2006-11-29 15:29:40 +0100340/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200341static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
342 const struct snd_kcontrol_new *tmpl,
343 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100344{
345 struct snd_kcontrol_new *knew;
346
Takashi Iwai603c4012008-07-30 15:01:44 +0200347 snd_array_init(&spec->kctls, sizeof(*knew), 32);
348 knew = snd_array_new(&spec->kctls);
349 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200350 return NULL;
351 *knew = *tmpl;
352 if (!name)
353 name = tmpl->name;
354 if (name) {
355 knew->name = kstrdup(name, GFP_KERNEL);
356 if (!knew->name)
357 return NULL;
358 }
359 return knew;
360}
361
362static int __via_add_control(struct via_spec *spec, int type, const char *name,
363 int idx, unsigned long val)
364{
365 struct snd_kcontrol_new *knew;
366
367 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
368 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100369 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200370 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100371 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100372 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100373 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100374 return 0;
375}
376
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200377#define via_add_control(spec, type, name, val) \
378 __via_add_control(spec, type, name, 0, val)
379
Takashi Iwai291c9e32011-06-17 16:15:26 +0200380#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100381
Takashi Iwai603c4012008-07-30 15:01:44 +0200382static void via_free_kctls(struct hda_codec *codec)
383{
384 struct via_spec *spec = codec->spec;
385
386 if (spec->kctls.list) {
387 struct snd_kcontrol_new *kctl = spec->kctls.list;
388 int i;
389 for (i = 0; i < spec->kctls.used; i++)
390 kfree(kctl[i].name);
391 }
392 snd_array_free(&spec->kctls);
393}
394
Joseph Chanc577b8a2006-11-29 15:29:40 +0100395/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800396static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200397 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100398{
399 char name[32];
400 int err;
401
402 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200403 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100404 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
405 if (err < 0)
406 return err;
407 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200408 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100409 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
410 if (err < 0)
411 return err;
412 return 0;
413}
414
Takashi Iwai5d417622011-06-20 11:32:27 +0200415#define get_connection_index(codec, mux, nid) \
Takashi Iwai8d087c72011-06-28 12:45:47 +0200416 snd_hda_get_conn_index(codec, mux, nid, 0)
Takashi Iwai5d417622011-06-20 11:32:27 +0200417
Takashi Iwai8df2a312011-06-21 11:48:29 +0200418static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
419 unsigned int mask)
420{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200421 unsigned int caps;
422 if (!nid)
423 return false;
424 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200425 if (dir == HDA_INPUT)
426 caps &= AC_WCAP_IN_AMP;
427 else
428 caps &= AC_WCAP_OUT_AMP;
429 if (!caps)
430 return false;
431 if (query_amp_caps(codec, nid, dir) & mask)
432 return true;
433 return false;
434}
435
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200436#define have_mute(codec, nid, dir) \
437 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200438
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200439/* enable/disable the output-route */
440static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
441 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200442{
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200443 int i;
444 for (i = 0; i < path->depth; i++) {
445 hda_nid_t src, dst;
446 int idx = path->idx[i];
447 src = path->path[i];
448 if (i < path->depth - 1)
449 dst = path->path[i + 1];
450 else
451 dst = 0;
452 if (enable && path->multi[i])
453 snd_hda_codec_write(codec, dst, 0,
454 AC_VERB_SET_CONNECT_SEL, idx);
Lydia Wangb89596a2011-07-04 17:01:33 +0800455 if (!force
456 && get_wcaps_type(get_wcaps(codec, src)) == AC_WID_AUD_OUT
457 && get_wcaps_type(get_wcaps(codec, dst)) == AC_WID_AUD_MIX)
Lydia Wange5e14682011-07-01 10:55:07 +0800458 continue;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200459 if (have_mute(codec, dst, HDA_INPUT)) {
460 int val = enable ? AMP_IN_UNMUTE(idx) :
461 AMP_IN_MUTE(idx);
462 snd_hda_codec_write(codec, dst, 0,
463 AC_VERB_SET_AMP_GAIN_MUTE, val);
464 }
465 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
466 continue;
467 if (have_mute(codec, src, HDA_OUTPUT)) {
468 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
469 snd_hda_codec_write(codec, src, 0,
470 AC_VERB_SET_AMP_GAIN_MUTE, val);
471 }
472 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200473}
474
475/* set the given pin as output */
476static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
477 int pin_type)
478{
479 if (!pin)
480 return;
481 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
482 pin_type);
483 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
484 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200485 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100486}
487
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200488static void via_auto_init_output(struct hda_codec *codec,
489 struct nid_path *path, int pin_type,
490 bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200491{
492 struct via_spec *spec = codec->spec;
493 unsigned int caps;
Lydia Wangb89596a2011-07-04 17:01:33 +0800494 hda_nid_t pin, nid, pre_nid;
495 int i, idx, j, num;
Takashi Iwai5d417622011-06-20 11:32:27 +0200496
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200497 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200498 return;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200499 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200500
501 init_output_pin(codec, pin, pin_type);
502 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
503 if (caps & AC_AMPCAP_MUTE) {
504 unsigned int val;
505 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
506 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
507 AMP_OUT_MUTE | val);
508 }
509
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200510 activate_output_path(codec, path, true, force);
511
512 /* initialize the AA-path */
513 if (!spec->aa_mix_nid)
514 return;
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200515 for (i = path->depth - 1; i > 0; i--) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200516 nid = path->path[i];
Lydia Wangb89596a2011-07-04 17:01:33 +0800517 pre_nid = path->path[i - 1];
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200518 idx = get_connection_index(codec, nid, spec->aa_mix_nid);
519 if (idx >= 0) {
Lydia Wangb89596a2011-07-04 17:01:33 +0800520 if (have_mute(codec, nid, HDA_INPUT)) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200521 snd_hda_codec_write(codec, nid, 0,
522 AC_VERB_SET_AMP_GAIN_MUTE,
523 AMP_IN_UNMUTE(idx));
Lydia Wangb89596a2011-07-04 17:01:33 +0800524 if (pre_nid == spec->multiout.dac_nids[0]) {
525 num = snd_hda_get_conn_list(codec, nid,
526 NULL);
527 for (j = 0; j < num; j++) {
528 if (j == idx)
529 continue;
530 snd_hda_codec_write(codec,
531 nid, 0,
532 AC_VERB_SET_AMP_GAIN_MUTE,
533 AMP_IN_MUTE(j));
534 }
535 }
536 }
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200537 break;
538 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200539 }
540}
541
Joseph Chanc577b8a2006-11-29 15:29:40 +0100542static void via_auto_init_multi_out(struct hda_codec *codec)
543{
544 struct via_spec *spec = codec->spec;
545 int i;
546
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200547 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200548 via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, true);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100549}
550
551static void via_auto_init_hp_out(struct hda_codec *codec)
552{
553 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100554
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200555 if (!spec->hp_dac_nid) {
556 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
557 return;
558 }
559 if (spec->hp_independent_mode) {
560 activate_output_path(codec, &spec->hp_dep_path, false, false);
561 via_auto_init_output(codec, &spec->hp_path, PIN_HP, true);
562 } else {
563 activate_output_path(codec, &spec->hp_path, false, false);
564 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
565 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100566}
567
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200568static void via_auto_init_speaker_out(struct hda_codec *codec)
569{
570 struct via_spec *spec = codec->spec;
571
572 if (spec->autocfg.speaker_outs)
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200573 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, true);
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200574}
575
Takashi Iwaif4a78282011-06-17 18:46:48 +0200576static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200577
Joseph Chanc577b8a2006-11-29 15:29:40 +0100578static void via_auto_init_analog_input(struct hda_codec *codec)
579{
580 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200581 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200582 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200583 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200584 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100585
Takashi Iwai096a8852011-06-20 12:09:02 +0200586 /* init ADCs */
587 for (i = 0; i < spec->num_adc_nids; i++) {
588 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
589 AC_VERB_SET_AMP_GAIN_MUTE,
590 AMP_IN_UNMUTE(0));
591 }
592
593 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200594 for (i = 0; i < cfg->num_inputs; i++) {
595 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200596 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200597 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100598 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200599 ctl = PIN_VREF50;
600 else
601 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100602 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200603 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100604 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200605
606 /* init input-src */
607 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200608 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
609 if (spec->mux_nids[adc_idx]) {
610 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
611 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
612 AC_VERB_SET_CONNECT_SEL,
613 mux_idx);
614 }
615 if (spec->dyn_adc_switch)
616 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200617 }
618
619 /* init aa-mixer */
620 if (!spec->aa_mix_nid)
621 return;
622 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
623 ARRAY_SIZE(conn));
624 for (i = 0; i < num_conns; i++) {
625 unsigned int caps = get_wcaps(codec, conn[i]);
626 if (get_wcaps_type(caps) == AC_WID_PIN)
627 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
628 AC_VERB_SET_AMP_GAIN_MUTE,
629 AMP_IN_MUTE(i));
630 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100631}
Lydia Wangf5271102009-10-10 19:07:35 +0800632
633static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
634 unsigned int *affected_parm)
635{
636 unsigned parm;
637 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
638 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
639 >> AC_DEFCFG_MISC_SHIFT
640 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800641 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200642 unsigned present = 0;
643
644 no_presence |= spec->no_pin_power_ctl;
645 if (!no_presence)
646 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200647 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800648 || ((no_presence || present)
649 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800650 *affected_parm = AC_PWRST_D0; /* if it's connected */
651 parm = AC_PWRST_D0;
652 } else
653 parm = AC_PWRST_D3;
654
655 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
656}
657
Takashi Iwai24088a52011-06-17 16:59:21 +0200658static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
659 struct snd_ctl_elem_info *uinfo)
660{
661 static const char * const texts[] = {
662 "Disabled", "Enabled"
663 };
664
665 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
666 uinfo->count = 1;
667 uinfo->value.enumerated.items = 2;
668 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
669 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
670 strcpy(uinfo->value.enumerated.name,
671 texts[uinfo->value.enumerated.item]);
672 return 0;
673}
674
675static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
676 struct snd_ctl_elem_value *ucontrol)
677{
678 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
679 struct via_spec *spec = codec->spec;
680 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
681 return 0;
682}
683
684static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
685 struct snd_ctl_elem_value *ucontrol)
686{
687 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
688 struct via_spec *spec = codec->spec;
689 unsigned int val = !ucontrol->value.enumerated.item[0];
690
691 if (val == spec->no_pin_power_ctl)
692 return 0;
693 spec->no_pin_power_ctl = val;
694 set_widgets_power_state(codec);
695 return 1;
696}
697
698static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
699 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
700 .name = "Dynamic Power-Control",
701 .info = via_pin_power_ctl_info,
702 .get = via_pin_power_ctl_get,
703 .put = via_pin_power_ctl_put,
704};
705
706
Harald Welte0aa62ae2008-09-09 15:58:27 +0800707static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
708 struct snd_ctl_elem_info *uinfo)
709{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200710 static const char * const texts[] = { "OFF", "ON" };
711
712 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
713 uinfo->count = 1;
714 uinfo->value.enumerated.items = 2;
715 if (uinfo->value.enumerated.item >= 2)
716 uinfo->value.enumerated.item = 1;
717 strcpy(uinfo->value.enumerated.name,
718 texts[uinfo->value.enumerated.item]);
719 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800720}
721
722static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
723 struct snd_ctl_elem_value *ucontrol)
724{
725 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800726 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800727
Takashi Iwaiece8d042011-06-19 16:24:21 +0200728 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800729 return 0;
730}
731
Harald Welte0aa62ae2008-09-09 15:58:27 +0800732static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
733 struct snd_ctl_elem_value *ucontrol)
734{
735 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
736 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +0200737 int cur;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200738
Takashi Iwai25250502011-06-30 17:24:47 +0200739 /* no independent-hp status change during PCM playback is running */
740 if (spec->num_active_streams)
741 return -EBUSY;
742
743 cur = !!ucontrol->value.enumerated.item[0];
744 if (spec->hp_independent_mode == cur)
745 return 0;
746 spec->hp_independent_mode = cur;
747 if (cur) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200748 activate_output_path(codec, &spec->hp_dep_path, false, false);
749 activate_output_path(codec, &spec->hp_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200750 if (spec->hp_indep_shared)
751 activate_output_path(codec, &spec->out_path[HDA_SIDE],
752 false, false);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200753 } else {
754 activate_output_path(codec, &spec->hp_path, false, false);
755 activate_output_path(codec, &spec->hp_dep_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200756 if (spec->hp_indep_shared)
757 activate_output_path(codec, &spec->out_path[HDA_SIDE],
758 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200759 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800760
Lydia Wangce0e5a92011-03-22 16:22:37 +0800761 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800762 set_widgets_power_state(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200763 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800764}
765
Takashi Iwaiece8d042011-06-19 16:24:21 +0200766static const struct snd_kcontrol_new via_hp_mixer = {
767 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
768 .name = "Independent HP",
769 .info = via_independent_hp_info,
770 .get = via_independent_hp_get,
771 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800772};
773
Takashi Iwai3d83e572010-04-14 14:36:23 +0200774static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100775{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200776 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100777 struct snd_kcontrol_new *knew;
778 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100779
Takashi Iwaiece8d042011-06-19 16:24:21 +0200780 nid = spec->autocfg.hp_pins[0];
781 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200782 if (knew == NULL)
783 return -ENOMEM;
784
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100785 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100786
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100787 return 0;
788}
789
Lydia Wang1564b282009-10-10 19:07:52 +0800790static void notify_aa_path_ctls(struct hda_codec *codec)
791{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200792 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800793 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800794
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200795 for (i = 0; i < spec->smart51_nums; i++) {
796 struct snd_kcontrol *ctl;
797 struct snd_ctl_elem_id id;
798 memset(&id, 0, sizeof(id));
799 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
800 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800801 ctl = snd_hda_find_mixer_ctl(codec, id.name);
802 if (ctl)
803 snd_ctl_notify(codec->bus->card,
804 SNDRV_CTL_EVENT_MASK_VALUE,
805 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800806 }
807}
808
809static void mute_aa_path(struct hda_codec *codec, int mute)
810{
811 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200812 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800813 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200814
Lydia Wang1564b282009-10-10 19:07:52 +0800815 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200816 for (i = 0; i < spec->smart51_nums; i++) {
817 if (spec->smart51_idxs[i] < 0)
818 continue;
819 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
820 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800821 HDA_AMP_MUTE, val);
822 }
823}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200824
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200825static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
826{
827 struct via_spec *spec = codec->spec;
828 int i;
829
830 for (i = 0; i < spec->smart51_nums; i++)
831 if (spec->smart51_pins[i] == pin)
832 return true;
833 return false;
834}
835
Lydia Wang1564b282009-10-10 19:07:52 +0800836static int via_smart51_get(struct snd_kcontrol *kcontrol,
837 struct snd_ctl_elem_value *ucontrol)
838{
839 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
840 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800841
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200842 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800843 return 0;
844}
845
846static int via_smart51_put(struct snd_kcontrol *kcontrol,
847 struct snd_ctl_elem_value *ucontrol)
848{
849 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
850 struct via_spec *spec = codec->spec;
851 int out_in = *ucontrol->value.integer.value
852 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800853 int i;
854
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200855 for (i = 0; i < spec->smart51_nums; i++) {
856 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200857 unsigned int parm;
858
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200859 parm = snd_hda_codec_read(codec, nid, 0,
860 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
861 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
862 parm |= out_in;
863 snd_hda_codec_write(codec, nid, 0,
864 AC_VERB_SET_PIN_WIDGET_CONTROL,
865 parm);
866 if (out_in == AC_PINCTL_OUT_EN) {
867 mute_aa_path(codec, 1);
868 notify_aa_path_ctls(codec);
869 }
Lydia Wang1564b282009-10-10 19:07:52 +0800870 }
871 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800872 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800873 return 1;
874}
875
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200876static const struct snd_kcontrol_new via_smart51_mixer = {
877 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
878 .name = "Smart 5.1",
879 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200880 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200881 .get = via_smart51_get,
882 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800883};
884
Takashi Iwaif4a78282011-06-17 18:46:48 +0200885static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100886{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200887 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100888
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200889 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800890 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200891 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100892 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100893 return 0;
894}
895
Takashi Iwaiada509e2011-06-20 15:40:19 +0200896/* check AA path's mute status */
897static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800898{
Lydia Wangf5271102009-10-10 19:07:35 +0800899 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200900 const struct hda_amp_list *p;
901 int i, ch, v;
902
903 for (i = 0; i < spec->num_loopbacks; i++) {
904 p = &spec->loopback_list[i];
905 for (ch = 0; ch < 2; ch++) {
906 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
907 p->idx);
908 if (!(v & HDA_AMP_MUTE) && v > 0)
909 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800910 }
911 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200912 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800913}
914
915/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200916static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800917{
918 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200919 bool enable;
920 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800921
Takashi Iwaiada509e2011-06-20 15:40:19 +0200922 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800923
924 /* decide low current mode's verb & parameter */
925 switch (spec->codec_type) {
926 case VT1708B_8CH:
927 case VT1708B_4CH:
928 verb = 0xf70;
929 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
930 break;
931 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800932 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800933 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800934 verb = 0xf73;
935 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
936 break;
937 case VT1702:
938 verb = 0xf73;
939 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
940 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800941 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800942 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800943 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800944 verb = 0xf93;
945 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
946 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800947 default:
948 return; /* other codecs are not supported */
949 }
950 /* send verb */
951 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
952}
953
Joseph Chanc577b8a2006-11-29 15:29:40 +0100954/*
955 * generic initialization of ADC, input mixers and output mixers
956 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200957static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800958 /* power down jack detect function */
959 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +0100960 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100961};
962
Takashi Iwaiada509e2011-06-20 15:40:19 +0200963static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200964{
Takashi Iwaiada509e2011-06-20 15:40:19 +0200965 struct via_spec *spec = codec->spec;
966
967 if (active)
968 spec->num_active_streams++;
969 else
970 spec->num_active_streams--;
971 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200972}
973
Takashi Iwaiece8d042011-06-19 16:24:21 +0200974static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100975 struct hda_codec *codec,
976 struct snd_pcm_substream *substream)
977{
978 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +0200979 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200980 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +0200981
Takashi Iwai25250502011-06-30 17:24:47 +0200982 spec->multiout.hp_nid = 0;
983 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
984 if (!spec->hp_independent_mode) {
985 if (!spec->hp_indep_shared)
986 spec->multiout.hp_nid = spec->hp_dac_nid;
987 } else {
988 if (spec->hp_indep_shared)
989 spec->multiout.num_dacs = cfg->line_outs - 1;
990 }
991 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200992 set_stream_active(codec, true);
993 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
994 hinfo);
995 if (err < 0) {
996 spec->multiout.hp_nid = 0;
997 set_stream_active(codec, false);
998 return err;
999 }
1000 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001001}
1002
Takashi Iwaiece8d042011-06-19 16:24:21 +02001003static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001004 struct hda_codec *codec,
1005 struct snd_pcm_substream *substream)
1006{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001007 struct via_spec *spec = codec->spec;
1008
1009 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001010 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001011 return 0;
1012}
1013
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001014static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1015 struct hda_codec *codec,
1016 struct snd_pcm_substream *substream)
1017{
1018 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001019
Takashi Iwaiece8d042011-06-19 16:24:21 +02001020 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001021 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001022 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1023 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001024 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001025 return 0;
1026}
1027
1028static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1029 struct hda_codec *codec,
1030 struct snd_pcm_substream *substream)
1031{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001032 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001033 return 0;
1034}
1035
1036static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1037 struct hda_codec *codec,
1038 unsigned int stream_tag,
1039 unsigned int format,
1040 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001041{
1042 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001043
Takashi Iwaiece8d042011-06-19 16:24:21 +02001044 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1045 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001046 vt1708_start_hp_work(spec);
1047 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001048}
1049
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001050static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1051 struct hda_codec *codec,
1052 unsigned int stream_tag,
1053 unsigned int format,
1054 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001055{
1056 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001057
Takashi Iwaiece8d042011-06-19 16:24:21 +02001058 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1059 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001060 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001061 return 0;
1062}
1063
1064static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1065 struct hda_codec *codec,
1066 struct snd_pcm_substream *substream)
1067{
1068 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001069
Takashi Iwaiece8d042011-06-19 16:24:21 +02001070 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001071 vt1708_stop_hp_work(spec);
1072 return 0;
1073}
1074
1075static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1076 struct hda_codec *codec,
1077 struct snd_pcm_substream *substream)
1078{
1079 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001080
Takashi Iwaiece8d042011-06-19 16:24:21 +02001081 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001082 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001083 return 0;
1084}
1085
Joseph Chanc577b8a2006-11-29 15:29:40 +01001086/*
1087 * Digital out
1088 */
1089static int via_dig_playback_pcm_open(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_open(codec, &spec->multiout);
1095}
1096
1097static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1098 struct hda_codec *codec,
1099 struct snd_pcm_substream *substream)
1100{
1101 struct via_spec *spec = codec->spec;
1102 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1103}
1104
Harald Welte5691ec72008-09-15 22:42:26 +08001105static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001106 struct hda_codec *codec,
1107 unsigned int stream_tag,
1108 unsigned int format,
1109 struct snd_pcm_substream *substream)
1110{
1111 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001112 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1113 stream_tag, format, substream);
1114}
Harald Welte5691ec72008-09-15 22:42:26 +08001115
Takashi Iwai9da29272009-05-07 16:31:14 +02001116static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1117 struct hda_codec *codec,
1118 struct snd_pcm_substream *substream)
1119{
1120 struct via_spec *spec = codec->spec;
1121 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001122 return 0;
1123}
1124
Joseph Chanc577b8a2006-11-29 15:29:40 +01001125/*
1126 * Analog capture
1127 */
1128static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1129 struct hda_codec *codec,
1130 unsigned int stream_tag,
1131 unsigned int format,
1132 struct snd_pcm_substream *substream)
1133{
1134 struct via_spec *spec = codec->spec;
1135
1136 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1137 stream_tag, 0, format);
1138 return 0;
1139}
1140
1141static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1142 struct hda_codec *codec,
1143 struct snd_pcm_substream *substream)
1144{
1145 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001146 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001147 return 0;
1148}
1149
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001150/* analog capture with dynamic ADC switching */
1151static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1152 struct hda_codec *codec,
1153 unsigned int stream_tag,
1154 unsigned int format,
1155 struct snd_pcm_substream *substream)
1156{
1157 struct via_spec *spec = codec->spec;
1158 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1159
1160 spec->cur_adc = spec->adc_nids[adc_idx];
1161 spec->cur_adc_stream_tag = stream_tag;
1162 spec->cur_adc_format = format;
1163 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
1164 return 0;
1165}
1166
1167static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1168 struct hda_codec *codec,
1169 struct snd_pcm_substream *substream)
1170{
1171 struct via_spec *spec = codec->spec;
1172
1173 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1174 spec->cur_adc = 0;
1175 return 0;
1176}
1177
1178/* re-setup the stream if running; called from input-src put */
1179static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1180{
1181 struct via_spec *spec = codec->spec;
1182 int adc_idx = spec->inputs[cur].adc_idx;
1183 hda_nid_t adc = spec->adc_nids[adc_idx];
1184
1185 if (spec->cur_adc && spec->cur_adc != adc) {
1186 /* stream is running, let's swap the current ADC */
1187 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1188 spec->cur_adc = adc;
1189 snd_hda_codec_setup_stream(codec, adc,
1190 spec->cur_adc_stream_tag, 0,
1191 spec->cur_adc_format);
1192 return true;
1193 }
1194 return false;
1195}
1196
Takashi Iwai9af74212011-06-18 16:17:45 +02001197static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001198 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001199 .channels_min = 2,
1200 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001201 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001202 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001203 .open = via_playback_multi_pcm_open,
1204 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001205 .prepare = via_playback_multi_pcm_prepare,
1206 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001207 },
1208};
1209
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001210static const struct hda_pcm_stream via_pcm_hp_playback = {
1211 .substreams = 1,
1212 .channels_min = 2,
1213 .channels_max = 2,
1214 /* NID is set in via_build_pcms */
1215 .ops = {
1216 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001217 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001218 .prepare = via_playback_hp_pcm_prepare,
1219 .cleanup = via_playback_hp_pcm_cleanup
1220 },
1221};
1222
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001223static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001224 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001225 .channels_min = 2,
1226 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001227 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001228 /* We got noisy outputs on the right channel on VT1708 when
1229 * 24bit samples are used. Until any workaround is found,
1230 * disable the 24bit format, so far.
1231 */
1232 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1233 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001234 .open = via_playback_multi_pcm_open,
1235 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001236 .prepare = via_playback_multi_pcm_prepare,
1237 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001238 },
1239};
1240
Takashi Iwai9af74212011-06-18 16:17:45 +02001241static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001242 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001243 .channels_min = 2,
1244 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001245 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001246 .ops = {
1247 .prepare = via_capture_pcm_prepare,
1248 .cleanup = via_capture_pcm_cleanup
1249 },
1250};
1251
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001252static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1253 .substreams = 1,
1254 .channels_min = 2,
1255 .channels_max = 2,
1256 /* NID is set in via_build_pcms */
1257 .ops = {
1258 .prepare = via_dyn_adc_capture_pcm_prepare,
1259 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1260 },
1261};
1262
Takashi Iwai9af74212011-06-18 16:17:45 +02001263static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001264 .substreams = 1,
1265 .channels_min = 2,
1266 .channels_max = 2,
1267 /* NID is set in via_build_pcms */
1268 .ops = {
1269 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001270 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001271 .prepare = via_dig_playback_pcm_prepare,
1272 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001273 },
1274};
1275
Takashi Iwai9af74212011-06-18 16:17:45 +02001276static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001277 .substreams = 1,
1278 .channels_min = 2,
1279 .channels_max = 2,
1280};
1281
Takashi Iwai370bafb2011-06-20 12:47:45 +02001282/*
1283 * slave controls for virtual master
1284 */
1285static const char * const via_slave_vols[] = {
1286 "Front Playback Volume",
1287 "Surround Playback Volume",
1288 "Center Playback Volume",
1289 "LFE Playback Volume",
1290 "Side Playback Volume",
1291 "Headphone Playback Volume",
1292 "Speaker Playback Volume",
1293 NULL,
1294};
1295
1296static const char * const via_slave_sws[] = {
1297 "Front Playback Switch",
1298 "Surround Playback Switch",
1299 "Center Playback Switch",
1300 "LFE Playback Switch",
1301 "Side Playback Switch",
1302 "Headphone Playback Switch",
1303 "Speaker Playback Switch",
1304 NULL,
1305};
1306
Joseph Chanc577b8a2006-11-29 15:29:40 +01001307static int via_build_controls(struct hda_codec *codec)
1308{
1309 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001310 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001311 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001312
Takashi Iwai24088a52011-06-17 16:59:21 +02001313 if (spec->set_widgets_power_state)
1314 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1315 return -ENOMEM;
1316
Joseph Chanc577b8a2006-11-29 15:29:40 +01001317 for (i = 0; i < spec->num_mixers; i++) {
1318 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1319 if (err < 0)
1320 return err;
1321 }
1322
1323 if (spec->multiout.dig_out_nid) {
1324 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001325 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001326 spec->multiout.dig_out_nid);
1327 if (err < 0)
1328 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001329 err = snd_hda_create_spdif_share_sw(codec,
1330 &spec->multiout);
1331 if (err < 0)
1332 return err;
1333 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001334 }
1335 if (spec->dig_in_nid) {
1336 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1337 if (err < 0)
1338 return err;
1339 }
Lydia Wang17314372009-10-10 19:07:37 +08001340
Takashi Iwai370bafb2011-06-20 12:47:45 +02001341 /* if we have no master control, let's create it */
1342 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1343 unsigned int vmaster_tlv[4];
1344 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1345 HDA_OUTPUT, vmaster_tlv);
1346 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1347 vmaster_tlv, via_slave_vols);
1348 if (err < 0)
1349 return err;
1350 }
1351 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1352 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1353 NULL, via_slave_sws);
1354 if (err < 0)
1355 return err;
1356 }
1357
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001358 /* assign Capture Source enums to NID */
1359 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1360 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001361 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001362 if (err < 0)
1363 return err;
1364 }
1365
Lydia Wang17314372009-10-10 19:07:37 +08001366 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001367 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001368 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001369
Takashi Iwai603c4012008-07-30 15:01:44 +02001370 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001371 return 0;
1372}
1373
1374static int via_build_pcms(struct hda_codec *codec)
1375{
1376 struct via_spec *spec = codec->spec;
1377 struct hda_pcm *info = spec->pcm_rec;
1378
1379 codec->num_pcms = 1;
1380 codec->pcm_info = info;
1381
Takashi Iwai82673bc2011-06-17 16:24:21 +02001382 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1383 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001384 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001385
1386 if (!spec->stream_analog_playback)
1387 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001388 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001389 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001390 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1391 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001392 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1393 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001394
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001395 if (!spec->stream_analog_capture) {
1396 if (spec->dyn_adc_switch)
1397 spec->stream_analog_capture =
1398 &via_pcm_dyn_adc_analog_capture;
1399 else
1400 spec->stream_analog_capture = &via_pcm_analog_capture;
1401 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001402 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1403 *spec->stream_analog_capture;
1404 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001405 if (!spec->dyn_adc_switch)
1406 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1407 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001408
1409 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1410 codec->num_pcms++;
1411 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001412 snprintf(spec->stream_name_digital,
1413 sizeof(spec->stream_name_digital),
1414 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001415 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001416 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001417 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001418 if (!spec->stream_digital_playback)
1419 spec->stream_digital_playback =
1420 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001421 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001422 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001423 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1424 spec->multiout.dig_out_nid;
1425 }
1426 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001427 if (!spec->stream_digital_capture)
1428 spec->stream_digital_capture =
1429 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001430 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001431 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001432 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1433 spec->dig_in_nid;
1434 }
1435 }
1436
Takashi Iwaiece8d042011-06-19 16:24:21 +02001437 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001438 codec->num_pcms++;
1439 info++;
1440 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1441 "%s HP", codec->chip_name);
1442 info->name = spec->stream_name_hp;
1443 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1444 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001445 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001446 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001447 return 0;
1448}
1449
1450static void via_free(struct hda_codec *codec)
1451{
1452 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001453
1454 if (!spec)
1455 return;
1456
Takashi Iwai603c4012008-07-30 15:01:44 +02001457 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001458 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001459 kfree(spec->bind_cap_vol);
1460 kfree(spec->bind_cap_sw);
1461 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001462}
1463
Takashi Iwai64be2852011-06-17 16:51:39 +02001464/* mute/unmute outputs */
1465static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1466 hda_nid_t *pins, bool mute)
1467{
1468 int i;
1469 for (i = 0; i < num_pins; i++)
1470 snd_hda_codec_write(codec, pins[i], 0,
1471 AC_VERB_SET_PIN_WIDGET_CONTROL,
1472 mute ? 0 : PIN_OUT);
1473}
1474
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001475/* mute internal speaker if line-out is plugged */
1476static void via_line_automute(struct hda_codec *codec, int present)
1477{
1478 struct via_spec *spec = codec->spec;
1479
1480 if (!spec->autocfg.speaker_outs)
1481 return;
1482 if (!present)
1483 present = snd_hda_jack_detect(codec,
1484 spec->autocfg.line_out_pins[0]);
1485 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1486 spec->autocfg.speaker_pins,
1487 present);
1488}
1489
Harald Welte69e52a82008-09-09 15:57:32 +08001490/* mute internal speaker if HP is plugged */
1491static void via_hp_automute(struct hda_codec *codec)
1492{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001493 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001494 struct via_spec *spec = codec->spec;
1495
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001496 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001497 int nums;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001498 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001499 if (spec->smart51_enabled)
1500 nums = spec->autocfg.line_outs + spec->smart51_nums;
1501 else
1502 nums = spec->autocfg.line_outs;
1503 toggle_output_mutes(codec, nums,
Takashi Iwai64be2852011-06-17 16:51:39 +02001504 spec->autocfg.line_out_pins,
1505 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001506 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001507 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001508}
1509
Harald Welte69e52a82008-09-09 15:57:32 +08001510static void via_gpio_control(struct hda_codec *codec)
1511{
1512 unsigned int gpio_data;
1513 unsigned int vol_counter;
1514 unsigned int vol;
1515 unsigned int master_vol;
1516
1517 struct via_spec *spec = codec->spec;
1518
1519 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1520 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1521
1522 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1523 0xF84, 0) & 0x3F0000) >> 16;
1524
1525 vol = vol_counter & 0x1F;
1526 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1527 AC_VERB_GET_AMP_GAIN_MUTE,
1528 AC_AMP_GET_INPUT);
1529
1530 if (gpio_data == 0x02) {
1531 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001532 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1533 AC_VERB_SET_PIN_WIDGET_CONTROL,
1534 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001535 if (vol_counter & 0x20) {
1536 /* decrease volume */
1537 if (vol > master_vol)
1538 vol = master_vol;
1539 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1540 0, HDA_AMP_VOLMASK,
1541 master_vol-vol);
1542 } else {
1543 /* increase volume */
1544 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1545 HDA_AMP_VOLMASK,
1546 ((master_vol+vol) > 0x2A) ? 0x2A :
1547 (master_vol+vol));
1548 }
1549 } else if (!(gpio_data & 0x02)) {
1550 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001551 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1552 AC_VERB_SET_PIN_WIDGET_CONTROL,
1553 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001554 }
1555}
1556
1557/* unsolicited event for jack sensing */
1558static void via_unsol_event(struct hda_codec *codec,
1559 unsigned int res)
1560{
1561 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001562
Lydia Wanga34df192009-10-10 19:08:01 +08001563 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001564 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001565
1566 res &= ~VIA_JACK_EVENT;
1567
1568 if (res == VIA_HP_EVENT)
1569 via_hp_automute(codec);
1570 else if (res == VIA_GPIO_EVENT)
1571 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001572 else if (res == VIA_LINE_EVENT)
1573 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001574}
1575
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001576#ifdef SND_HDA_NEEDS_RESUME
1577static int via_suspend(struct hda_codec *codec, pm_message_t state)
1578{
1579 struct via_spec *spec = codec->spec;
1580 vt1708_stop_hp_work(spec);
1581 return 0;
1582}
1583#endif
1584
Takashi Iwaicb53c622007-08-10 17:21:45 +02001585#ifdef CONFIG_SND_HDA_POWER_SAVE
1586static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1587{
1588 struct via_spec *spec = codec->spec;
1589 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1590}
1591#endif
1592
Joseph Chanc577b8a2006-11-29 15:29:40 +01001593/*
1594 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001595
1596static int via_init(struct hda_codec *codec);
1597
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001598static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001599 .build_controls = via_build_controls,
1600 .build_pcms = via_build_pcms,
1601 .init = via_init,
1602 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001603 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001604#ifdef SND_HDA_NEEDS_RESUME
1605 .suspend = via_suspend,
1606#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001607#ifdef CONFIG_SND_HDA_POWER_SAVE
1608 .check_power_status = via_check_power_status,
1609#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001610};
1611
Takashi Iwai4a796162011-06-17 17:53:38 +02001612static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001613{
Takashi Iwai4a796162011-06-17 17:53:38 +02001614 struct via_spec *spec = codec->spec;
1615 int i;
1616
1617 for (i = 0; i < spec->multiout.num_dacs; i++) {
1618 if (spec->multiout.dac_nids[i] == dac)
1619 return false;
1620 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001621 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001622 return false;
1623 return true;
1624}
1625
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001626static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001627 hda_nid_t target_dac, struct nid_path *path,
1628 int depth, int wid_type)
1629{
1630 hda_nid_t conn[8];
1631 int i, nums;
1632
1633 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1634 for (i = 0; i < nums; i++) {
1635 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1636 continue;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001637 if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
1638 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001639 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001640 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001641 return false;
1642 for (i = 0; i < nums; i++) {
1643 unsigned int type;
1644 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1645 if (type == AC_WID_AUD_OUT ||
1646 (wid_type != -1 && type != wid_type))
1647 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001648 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001649 path, depth + 1, AC_WID_AUD_SEL))
1650 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001651 }
1652 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001653
1654 found:
1655 path->path[path->depth] = conn[i];
1656 path->idx[path->depth] = i;
1657 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1658 path->multi[path->depth] = 1;
1659 path->depth++;
1660 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001661}
1662
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001663static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1664 hda_nid_t target_dac, struct nid_path *path)
1665{
1666 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1667 path->path[path->depth] = nid;
1668 path->depth++;
1669 return true;
1670 }
1671 return false;
1672}
1673
Takashi Iwai4a796162011-06-17 17:53:38 +02001674static int via_auto_fill_dac_nids(struct hda_codec *codec)
1675{
1676 struct via_spec *spec = codec->spec;
1677 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001678 int i;
1679 hda_nid_t nid;
1680
Joseph Chanc577b8a2006-11-29 15:29:40 +01001681 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001682 spec->multiout.num_dacs = cfg->line_outs;
1683 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001684 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001685 if (!nid)
1686 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001687 if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
1688 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001689 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001690 return 0;
1691}
1692
Takashi Iwai4a796162011-06-17 17:53:38 +02001693static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001694 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001695{
Takashi Iwai4a796162011-06-17 17:53:38 +02001696 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001697 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001698 hda_nid_t dac, pin, sel, nid;
1699 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001700
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001701 dac = check_dac ? path->path[0] : 0;
1702 pin = path->path[path->depth - 1];
1703 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001704
Takashi Iwai8df2a312011-06-21 11:48:29 +02001705 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001706 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001707 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001708 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001709 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1710 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001711 else
1712 nid = 0;
1713 if (nid) {
1714 sprintf(name, "%s Playback Volume", pfx);
1715 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001716 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001717 if (err < 0)
1718 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001719 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001720 }
1721
Takashi Iwai8df2a312011-06-21 11:48:29 +02001722 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001723 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001724 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001725 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001726 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1727 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001728 else
1729 nid = 0;
1730 if (nid) {
1731 sprintf(name, "%s Playback Switch", pfx);
1732 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1733 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1734 if (err < 0)
1735 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001736 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001737 }
1738 return 0;
1739}
1740
Takashi Iwaif4a78282011-06-17 18:46:48 +02001741static void mangle_smart51(struct hda_codec *codec)
1742{
1743 struct via_spec *spec = codec->spec;
1744 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001745 struct auto_pin_cfg_item *ins = cfg->inputs;
1746 int i, j, nums, attr;
1747 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001748
Takashi Iwai0f98c242011-06-21 12:51:33 +02001749 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1750 nums = 0;
1751 for (i = 0; i < cfg->num_inputs; i++) {
1752 unsigned int def;
1753 if (ins[i].type > AUTO_PIN_LINE_IN)
1754 continue;
1755 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1756 if (snd_hda_get_input_pin_attr(def) != attr)
1757 continue;
1758 for (j = 0; j < nums; j++)
1759 if (ins[pins[j]].type < ins[i].type) {
1760 memmove(pins + j + 1, pins + j,
1761 (nums - j - 1) * sizeof(int));
1762 break;
1763 }
1764 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001765 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001766 }
1767 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001768 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001769 for (i = 0; i < nums; i++) {
1770 hda_nid_t pin = ins[pins[i]].pin;
1771 spec->smart51_pins[spec->smart51_nums++] = pin;
1772 cfg->line_out_pins[cfg->line_outs++] = pin;
1773 if (cfg->line_outs == 3)
1774 break;
1775 }
1776 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001777 }
1778}
1779
Takashi Iwai4a796162011-06-17 17:53:38 +02001780/* add playback controls from the parsed DAC table */
1781static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1782{
1783 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001784 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001785 static const char * const chname[4] = {
1786 "Front", "Surround", "C/LFE", "Side"
1787 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001788 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001789 int old_line_outs;
1790
1791 /* check smart51 */
1792 old_line_outs = cfg->line_outs;
1793 if (cfg->line_outs == 1)
1794 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001795
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001796 err = via_auto_fill_dac_nids(codec);
1797 if (err < 0)
1798 return err;
1799
Takashi Iwai4a796162011-06-17 17:53:38 +02001800 for (i = 0; i < cfg->line_outs; i++) {
1801 hda_nid_t pin, dac;
1802 pin = cfg->line_out_pins[i];
1803 dac = spec->multiout.dac_nids[i];
1804 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001805 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001806 if (i == HDA_CLFE) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001807 err = create_ch_ctls(codec, "Center", 1, true,
1808 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001809 if (err < 0)
1810 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001811 err = create_ch_ctls(codec, "LFE", 2, true,
1812 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001813 if (err < 0)
1814 return err;
1815 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001816 const char *pfx = chname[i];
1817 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1818 cfg->line_outs == 1)
1819 pfx = "Speaker";
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001820 err = create_ch_ctls(codec, pfx, 3, true,
1821 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001822 if (err < 0)
1823 return err;
1824 }
1825 }
1826
Takashi Iwai4a796162011-06-17 17:53:38 +02001827 idx = get_connection_index(codec, spec->aa_mix_nid,
1828 spec->multiout.dac_nids[0]);
Lydia Wangc4394f52011-07-04 16:54:15 +08001829 if (idx < 0 && spec->dac_mixer_idx)
1830 idx = spec->dac_mixer_idx;
Takashi Iwai4a796162011-06-17 17:53:38 +02001831 if (idx >= 0) {
1832 /* add control to mixer */
1833 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1834 "PCM Playback Volume",
1835 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1836 idx, HDA_INPUT));
1837 if (err < 0)
1838 return err;
1839 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1840 "PCM Playback Switch",
1841 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1842 idx, HDA_INPUT));
1843 if (err < 0)
1844 return err;
1845 }
1846
Takashi Iwaif4a78282011-06-17 18:46:48 +02001847 cfg->line_outs = old_line_outs;
1848
Joseph Chanc577b8a2006-11-29 15:29:40 +01001849 return 0;
1850}
1851
Takashi Iwai4a796162011-06-17 17:53:38 +02001852static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001853{
Takashi Iwai4a796162011-06-17 17:53:38 +02001854 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001855 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001856 int err;
1857
1858 if (!pin)
1859 return 0;
1860
Takashi Iwai8df2a312011-06-21 11:48:29 +02001861 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001862 spec->hp_dac_nid = spec->hp_path.path[0];
Takashi Iwai25250502011-06-30 17:24:47 +02001863 else if (spec->multiout.dac_nids[HDA_SIDE] &&
1864 parse_output_path(codec, pin,
1865 spec->multiout.dac_nids[HDA_SIDE],
1866 &spec->hp_path)) {
1867 spec->hp_dac_nid = spec->hp_path.path[0];
1868 spec->hp_indep_shared = true;
1869 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001870
Takashi Iwaiece8d042011-06-19 16:24:21 +02001871 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001872 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001873 !spec->hp_dac_nid)
1874 return 0;
1875
Takashi Iwai25250502011-06-30 17:24:47 +02001876 if (spec->hp_dac_nid && !spec->hp_indep_shared)
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001877 path = &spec->hp_path;
1878 else
1879 path = &spec->hp_dep_path;
1880 err = create_ch_ctls(codec, "Headphone", 3, false, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001881 if (err < 0)
1882 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001883 if (spec->hp_dac_nid) {
1884 spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
1885 spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
1886 }
Harald Welte0aa62ae2008-09-09 15:58:27 +08001887
Joseph Chanc577b8a2006-11-29 15:29:40 +01001888 return 0;
1889}
1890
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001891static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1892{
1893 struct via_spec *spec = codec->spec;
1894 hda_nid_t pin, dac;
1895
1896 pin = spec->autocfg.speaker_pins[0];
1897 if (!spec->autocfg.speaker_outs || !pin)
1898 return 0;
1899
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001900 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1901 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001902 spec->multiout.extra_out_nid[0] = dac;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001903 return create_ch_ctls(codec, "Speaker", 3, true,
1904 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001905 }
1906 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001907 &spec->speaker_path))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001908 return create_ch_ctls(codec, "Speaker", 3, false,
1909 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001910
1911 return 0;
1912}
1913
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001914/* look for ADCs */
1915static int via_fill_adcs(struct hda_codec *codec)
1916{
1917 struct via_spec *spec = codec->spec;
1918 hda_nid_t nid = codec->start_nid;
1919 int i;
1920
1921 for (i = 0; i < codec->num_nodes; i++, nid++) {
1922 unsigned int wcaps = get_wcaps(codec, nid);
1923 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1924 continue;
1925 if (wcaps & AC_WCAP_DIGITAL)
1926 continue;
1927 if (!(wcaps & AC_WCAP_CONN_LIST))
1928 continue;
1929 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1930 return -ENOMEM;
1931 spec->adc_nids[spec->num_adc_nids++] = nid;
1932 }
1933 return 0;
1934}
1935
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001936/* input-src control */
1937static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
1938 struct snd_ctl_elem_info *uinfo)
1939{
1940 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1941 struct via_spec *spec = codec->spec;
1942
1943 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1944 uinfo->count = 1;
1945 uinfo->value.enumerated.items = spec->num_inputs;
1946 if (uinfo->value.enumerated.item >= spec->num_inputs)
1947 uinfo->value.enumerated.item = spec->num_inputs - 1;
1948 strcpy(uinfo->value.enumerated.name,
1949 spec->inputs[uinfo->value.enumerated.item].label);
1950 return 0;
1951}
1952
1953static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
1954 struct snd_ctl_elem_value *ucontrol)
1955{
1956 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1957 struct via_spec *spec = codec->spec;
1958 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1959
1960 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
1961 return 0;
1962}
1963
1964static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
1965 struct snd_ctl_elem_value *ucontrol)
1966{
1967 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1968 struct via_spec *spec = codec->spec;
1969 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1970 hda_nid_t mux;
1971 int cur;
1972
1973 cur = ucontrol->value.enumerated.item[0];
1974 if (cur < 0 || cur >= spec->num_inputs)
1975 return -EINVAL;
1976 if (spec->cur_mux[idx] == cur)
1977 return 0;
1978 spec->cur_mux[idx] = cur;
1979 if (spec->dyn_adc_switch) {
1980 int adc_idx = spec->inputs[cur].adc_idx;
1981 mux = spec->mux_nids[adc_idx];
1982 via_dyn_adc_pcm_resetup(codec, cur);
1983 } else {
1984 mux = spec->mux_nids[idx];
1985 if (snd_BUG_ON(!mux))
1986 return -EINVAL;
1987 }
1988
1989 if (mux) {
1990 /* switch to D0 beofre change index */
1991 if (snd_hda_codec_read(codec, mux, 0,
1992 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
1993 snd_hda_codec_write(codec, mux, 0,
1994 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
1995 snd_hda_codec_write(codec, mux, 0,
1996 AC_VERB_SET_CONNECT_SEL,
1997 spec->inputs[cur].mux_idx);
1998 }
1999
2000 /* update jack power state */
2001 set_widgets_power_state(codec);
2002 return 0;
2003}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002004
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002005static const struct snd_kcontrol_new via_input_src_ctl = {
2006 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2007 /* The multiple "Capture Source" controls confuse alsamixer
2008 * So call somewhat different..
2009 */
2010 /* .name = "Capture Source", */
2011 .name = "Input Source",
2012 .info = via_mux_enum_info,
2013 .get = via_mux_enum_get,
2014 .put = via_mux_enum_put,
2015};
2016
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002017static int create_input_src_ctls(struct hda_codec *codec, int count)
2018{
2019 struct via_spec *spec = codec->spec;
2020 struct snd_kcontrol_new *knew;
2021
2022 if (spec->num_inputs <= 1 || !count)
2023 return 0; /* no need for single src */
2024
2025 knew = via_clone_control(spec, &via_input_src_ctl);
2026 if (!knew)
2027 return -ENOMEM;
2028 knew->count = count;
2029 return 0;
2030}
2031
2032/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002033static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2034{
2035 struct hda_amp_list *list;
2036
2037 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2038 return;
2039 list = spec->loopback_list + spec->num_loopbacks;
2040 list->nid = mix;
2041 list->dir = HDA_INPUT;
2042 list->idx = idx;
2043 spec->num_loopbacks++;
2044 spec->loopback.amplist = spec->loopback_list;
2045}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002046
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002047static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002048 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002049{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002050 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002051}
2052
2053/* add the input-route to the given pin */
2054static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002055{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002056 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002057 int c, idx;
2058
2059 spec->inputs[spec->num_inputs].adc_idx = -1;
2060 spec->inputs[spec->num_inputs].pin = pin;
2061 for (c = 0; c < spec->num_adc_nids; c++) {
2062 if (spec->mux_nids[c]) {
2063 idx = get_connection_index(codec, spec->mux_nids[c],
2064 pin);
2065 if (idx < 0)
2066 continue;
2067 spec->inputs[spec->num_inputs].mux_idx = idx;
2068 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002069 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002070 continue;
2071 }
2072 spec->inputs[spec->num_inputs].adc_idx = c;
2073 /* Can primary ADC satisfy all inputs? */
2074 if (!spec->dyn_adc_switch &&
2075 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2076 snd_printd(KERN_INFO
2077 "via: dynamic ADC switching enabled\n");
2078 spec->dyn_adc_switch = 1;
2079 }
2080 return true;
2081 }
2082 return false;
2083}
2084
2085static int get_mux_nids(struct hda_codec *codec);
2086
2087/* parse input-routes; fill ADCs, MUXs and input-src entries */
2088static int parse_analog_inputs(struct hda_codec *codec)
2089{
2090 struct via_spec *spec = codec->spec;
2091 const struct auto_pin_cfg *cfg = &spec->autocfg;
2092 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002093
2094 err = via_fill_adcs(codec);
2095 if (err < 0)
2096 return err;
2097 err = get_mux_nids(codec);
2098 if (err < 0)
2099 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002100
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002101 /* fill all input-routes */
2102 for (i = 0; i < cfg->num_inputs; i++) {
2103 if (add_input_route(codec, cfg->inputs[i].pin))
2104 spec->inputs[spec->num_inputs++].label =
2105 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002106 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002107
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002108 /* check for internal loopback recording */
2109 if (spec->aa_mix_nid &&
2110 add_input_route(codec, spec->aa_mix_nid))
2111 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2112
2113 return 0;
2114}
2115
2116/* create analog-loopback volume/switch controls */
2117static int create_loopback_ctls(struct hda_codec *codec)
2118{
2119 struct via_spec *spec = codec->spec;
2120 const struct auto_pin_cfg *cfg = &spec->autocfg;
2121 const char *prev_label = NULL;
2122 int type_idx = 0;
2123 int i, j, err, idx;
2124
2125 if (!spec->aa_mix_nid)
2126 return 0;
2127
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002128 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002129 hda_nid_t pin = cfg->inputs[i].pin;
2130 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2131
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002132 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002133 type_idx++;
2134 else
2135 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002136 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002137 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2138 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002139 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002140 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002141 if (err < 0)
2142 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002143 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002144 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002145
2146 /* remember the label for smart51 control */
2147 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002148 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002149 spec->smart51_idxs[j] = idx;
2150 spec->smart51_labels[j] = label;
2151 break;
2152 }
2153 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002154 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002155 return 0;
2156}
2157
2158/* create mic-boost controls (if present) */
2159static int create_mic_boost_ctls(struct hda_codec *codec)
2160{
2161 struct via_spec *spec = codec->spec;
2162 const struct auto_pin_cfg *cfg = &spec->autocfg;
2163 int i, err;
2164
2165 for (i = 0; i < cfg->num_inputs; i++) {
2166 hda_nid_t pin = cfg->inputs[i].pin;
2167 unsigned int caps;
2168 const char *label;
2169 char name[32];
2170
2171 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2172 continue;
2173 caps = query_amp_caps(codec, pin, HDA_INPUT);
2174 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2175 continue;
2176 label = hda_get_autocfg_input_label(codec, cfg, i);
2177 snprintf(name, sizeof(name), "%s Boost Volume", label);
2178 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2179 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2180 if (err < 0)
2181 return err;
2182 }
2183 return 0;
2184}
2185
2186/* create capture and input-src controls for multiple streams */
2187static int create_multi_adc_ctls(struct hda_codec *codec)
2188{
2189 struct via_spec *spec = codec->spec;
2190 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002191
2192 /* create capture mixer elements */
2193 for (i = 0; i < spec->num_adc_nids; i++) {
2194 hda_nid_t adc = spec->adc_nids[i];
2195 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2196 "Capture Volume", i,
2197 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2198 HDA_INPUT));
2199 if (err < 0)
2200 return err;
2201 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2202 "Capture Switch", i,
2203 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2204 HDA_INPUT));
2205 if (err < 0)
2206 return err;
2207 }
2208
2209 /* input-source control */
2210 for (i = 0; i < spec->num_adc_nids; i++)
2211 if (!spec->mux_nids[i])
2212 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002213 err = create_input_src_ctls(codec, i);
2214 if (err < 0)
2215 return err;
2216 return 0;
2217}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002218
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002219/* bind capture volume/switch */
2220static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2221 HDA_BIND_VOL("Capture Volume", 0);
2222static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2223 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002224
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002225static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2226 struct hda_ctl_ops *ops)
2227{
2228 struct hda_bind_ctls *ctl;
2229 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002230
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002231 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2232 if (!ctl)
2233 return -ENOMEM;
2234 ctl->ops = ops;
2235 for (i = 0; i < spec->num_adc_nids; i++)
2236 ctl->values[i] =
2237 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2238 *ctl_ret = ctl;
2239 return 0;
2240}
2241
2242/* create capture and input-src controls for dynamic ADC-switch case */
2243static int create_dyn_adc_ctls(struct hda_codec *codec)
2244{
2245 struct via_spec *spec = codec->spec;
2246 struct snd_kcontrol_new *knew;
2247 int err;
2248
2249 /* set up the bind capture ctls */
2250 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2251 if (err < 0)
2252 return err;
2253 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2254 if (err < 0)
2255 return err;
2256
2257 /* create capture mixer elements */
2258 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2259 if (!knew)
2260 return -ENOMEM;
2261 knew->private_value = (long)spec->bind_cap_vol;
2262
2263 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2264 if (!knew)
2265 return -ENOMEM;
2266 knew->private_value = (long)spec->bind_cap_sw;
2267
2268 /* input-source control */
2269 err = create_input_src_ctls(codec, 1);
2270 if (err < 0)
2271 return err;
2272 return 0;
2273}
2274
2275/* parse and create capture-related stuff */
2276static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2277{
2278 struct via_spec *spec = codec->spec;
2279 int err;
2280
2281 err = parse_analog_inputs(codec);
2282 if (err < 0)
2283 return err;
2284 if (spec->dyn_adc_switch)
2285 err = create_dyn_adc_ctls(codec);
2286 else
2287 err = create_multi_adc_ctls(codec);
2288 if (err < 0)
2289 return err;
2290 err = create_loopback_ctls(codec);
2291 if (err < 0)
2292 return err;
2293 err = create_mic_boost_ctls(codec);
2294 if (err < 0)
2295 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002296 return 0;
2297}
2298
Harald Welte76d9b0d2008-09-09 15:50:37 +08002299static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2300{
2301 unsigned int def_conf;
2302 unsigned char seqassoc;
2303
Takashi Iwai2f334f92009-02-20 14:37:42 +01002304 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002305 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2306 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002307 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2308 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2309 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2310 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002311 }
2312
2313 return;
2314}
2315
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002316static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002317 struct snd_ctl_elem_value *ucontrol)
2318{
2319 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2320 struct via_spec *spec = codec->spec;
2321
2322 if (spec->codec_type != VT1708)
2323 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002324 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002325 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002326 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002327 return 0;
2328}
2329
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002330static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002331 struct snd_ctl_elem_value *ucontrol)
2332{
2333 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2334 struct via_spec *spec = codec->spec;
2335 int change;
2336
2337 if (spec->codec_type != VT1708)
2338 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002339 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002340 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002341 == !spec->vt1708_jack_detect;
2342 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002343 mute_aa_path(codec, 1);
2344 notify_aa_path_ctls(codec);
2345 }
2346 return change;
2347}
2348
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002349static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2350 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2351 .name = "Jack Detect",
2352 .count = 1,
2353 .info = snd_ctl_boolean_mono_info,
2354 .get = vt1708_jack_detect_get,
2355 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002356};
2357
Takashi Iwai12daef62011-06-18 17:45:49 +02002358static void fill_dig_outs(struct hda_codec *codec);
2359static void fill_dig_in(struct hda_codec *codec);
2360
2361static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002362{
2363 struct via_spec *spec = codec->spec;
2364 int err;
2365
2366 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2367 if (err < 0)
2368 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002369 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002370 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002371
Takashi Iwai4a796162011-06-17 17:53:38 +02002372 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002373 if (err < 0)
2374 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002375 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002376 if (err < 0)
2377 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002378 err = via_auto_create_speaker_ctls(codec);
2379 if (err < 0)
2380 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002381 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002382 if (err < 0)
2383 return err;
2384
2385 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2386
Takashi Iwai12daef62011-06-18 17:45:49 +02002387 fill_dig_outs(codec);
2388 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002389
Takashi Iwai603c4012008-07-30 15:01:44 +02002390 if (spec->kctls.list)
2391 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002392
Joseph Chanc577b8a2006-11-29 15:29:40 +01002393
Takashi Iwai8df2a312011-06-21 11:48:29 +02002394 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002395 err = via_hp_build(codec);
2396 if (err < 0)
2397 return err;
2398 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002399
Takashi Iwaif4a78282011-06-17 18:46:48 +02002400 err = via_smart51_build(codec);
2401 if (err < 0)
2402 return err;
2403
Takashi Iwai5d417622011-06-20 11:32:27 +02002404 /* assign slave outs */
2405 if (spec->slave_dig_outs[0])
2406 codec->slave_dig_outs = spec->slave_dig_outs;
2407
Joseph Chanc577b8a2006-11-29 15:29:40 +01002408 return 1;
2409}
2410
Takashi Iwai5d417622011-06-20 11:32:27 +02002411static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002412{
Lydia Wang25eaba22009-10-10 19:08:43 +08002413 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002414 if (spec->multiout.dig_out_nid)
2415 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2416 if (spec->slave_dig_outs[0])
2417 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2418}
Lydia Wang25eaba22009-10-10 19:08:43 +08002419
Takashi Iwai5d417622011-06-20 11:32:27 +02002420static void via_auto_init_dig_in(struct hda_codec *codec)
2421{
2422 struct via_spec *spec = codec->spec;
2423 if (!spec->dig_in_nid)
2424 return;
2425 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2426 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2427}
2428
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002429/* initialize the unsolicited events */
2430static void via_auto_init_unsol_event(struct hda_codec *codec)
2431{
2432 struct via_spec *spec = codec->spec;
2433 struct auto_pin_cfg *cfg = &spec->autocfg;
2434 unsigned int ev;
2435 int i;
2436
2437 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2438 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2439 AC_VERB_SET_UNSOLICITED_ENABLE,
2440 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2441
2442 if (cfg->speaker_pins[0])
2443 ev = VIA_LINE_EVENT;
2444 else
2445 ev = 0;
2446 for (i = 0; i < cfg->line_outs; i++) {
2447 if (cfg->line_out_pins[i] &&
2448 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002449 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002450 AC_VERB_SET_UNSOLICITED_ENABLE,
2451 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2452 }
2453
2454 for (i = 0; i < cfg->num_inputs; i++) {
2455 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2456 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2457 AC_VERB_SET_UNSOLICITED_ENABLE,
2458 AC_USRSP_EN | VIA_JACK_EVENT);
2459 }
2460}
2461
Takashi Iwai5d417622011-06-20 11:32:27 +02002462static int via_init(struct hda_codec *codec)
2463{
2464 struct via_spec *spec = codec->spec;
2465 int i;
2466
2467 for (i = 0; i < spec->num_iverbs; i++)
2468 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2469
Joseph Chanc577b8a2006-11-29 15:29:40 +01002470 via_auto_init_multi_out(codec);
2471 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002472 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002473 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002474 via_auto_init_dig_outs(codec);
2475 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002476
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002477 via_auto_init_unsol_event(codec);
2478
2479 via_hp_automute(codec);
2480 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002481
Joseph Chanc577b8a2006-11-29 15:29:40 +01002482 return 0;
2483}
2484
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002485static void vt1708_update_hp_jack_state(struct work_struct *work)
2486{
2487 struct via_spec *spec = container_of(work, struct via_spec,
2488 vt1708_hp_work.work);
2489 if (spec->codec_type != VT1708)
2490 return;
2491 /* if jack state toggled */
2492 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002493 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002494 spec->vt1708_hp_present ^= 1;
2495 via_hp_automute(spec->codec);
2496 }
2497 vt1708_start_hp_work(spec);
2498}
2499
Takashi Iwai337b9d02009-07-07 18:18:59 +02002500static int get_mux_nids(struct hda_codec *codec)
2501{
2502 struct via_spec *spec = codec->spec;
2503 hda_nid_t nid, conn[8];
2504 unsigned int type;
2505 int i, n;
2506
2507 for (i = 0; i < spec->num_adc_nids; i++) {
2508 nid = spec->adc_nids[i];
2509 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002510 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002511 if (type == AC_WID_PIN)
2512 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002513 n = snd_hda_get_connections(codec, nid, conn,
2514 ARRAY_SIZE(conn));
2515 if (n <= 0)
2516 break;
2517 if (n > 1) {
2518 spec->mux_nids[i] = nid;
2519 break;
2520 }
2521 nid = conn[0];
2522 }
2523 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002524 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002525}
2526
Joseph Chanc577b8a2006-11-29 15:29:40 +01002527static int patch_vt1708(struct hda_codec *codec)
2528{
2529 struct via_spec *spec;
2530 int err;
2531
2532 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002533 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002534 if (spec == NULL)
2535 return -ENOMEM;
2536
Takashi Iwai620e2b22011-06-17 17:19:19 +02002537 spec->aa_mix_nid = 0x17;
2538
Takashi Iwai12daef62011-06-18 17:45:49 +02002539 /* Add HP and CD pin config connect bit re-config action */
2540 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2541 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2542
Joseph Chanc577b8a2006-11-29 15:29:40 +01002543 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002544 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002545 if (err < 0) {
2546 via_free(codec);
2547 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002548 }
2549
Takashi Iwai12daef62011-06-18 17:45:49 +02002550 /* add jack detect on/off control */
2551 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2552 return -ENOMEM;
2553
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002554 /* disable 32bit format on VT1708 */
2555 if (codec->vendor_id == 0x11061708)
2556 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002557
Lydia Wange322a362011-06-29 13:52:02 +08002558 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2559
Joseph Chanc577b8a2006-11-29 15:29:40 +01002560 codec->patch_ops = via_patch_ops;
2561
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002562 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002563 return 0;
2564}
2565
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002566static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002567{
2568 struct via_spec *spec;
2569 int err;
2570
2571 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002572 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002573 if (spec == NULL)
2574 return -ENOMEM;
2575
Takashi Iwai620e2b22011-06-17 17:19:19 +02002576 spec->aa_mix_nid = 0x18;
2577
Takashi Iwai12daef62011-06-18 17:45:49 +02002578 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002579 if (err < 0) {
2580 via_free(codec);
2581 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002582 }
2583
Joseph Chanc577b8a2006-11-29 15:29:40 +01002584 codec->patch_ops = via_patch_ops;
2585
Josepch Chanf7278fd2007-12-13 16:40:40 +01002586 return 0;
2587}
2588
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002589static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2590{
2591 struct via_spec *spec = codec->spec;
2592 int imux_is_smixer;
2593 unsigned int parm;
2594 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002595 if ((spec->codec_type != VT1708B_4CH) &&
2596 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002597 is_8ch = 1;
2598
2599 /* SW0 (17h) = stereo mixer */
2600 imux_is_smixer =
2601 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2602 == ((spec->codec_type == VT1708S) ? 5 : 0));
2603 /* inputs */
2604 /* PW 1/2/5 (1ah/1bh/1eh) */
2605 parm = AC_PWRST_D3;
2606 set_pin_power_state(codec, 0x1a, &parm);
2607 set_pin_power_state(codec, 0x1b, &parm);
2608 set_pin_power_state(codec, 0x1e, &parm);
2609 if (imux_is_smixer)
2610 parm = AC_PWRST_D0;
2611 /* SW0 (17h), AIW 0/1 (13h/14h) */
2612 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2613 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2614 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2615
2616 /* outputs */
2617 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2618 parm = AC_PWRST_D3;
2619 set_pin_power_state(codec, 0x19, &parm);
2620 if (spec->smart51_enabled)
2621 set_pin_power_state(codec, 0x1b, &parm);
2622 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2623 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2624
2625 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2626 if (is_8ch) {
2627 parm = AC_PWRST_D3;
2628 set_pin_power_state(codec, 0x22, &parm);
2629 if (spec->smart51_enabled)
2630 set_pin_power_state(codec, 0x1a, &parm);
2631 snd_hda_codec_write(codec, 0x26, 0,
2632 AC_VERB_SET_POWER_STATE, parm);
2633 snd_hda_codec_write(codec, 0x24, 0,
2634 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002635 } else if (codec->vendor_id == 0x11064397) {
2636 /* PW7(23h), SW2(27h), AOW2(25h) */
2637 parm = AC_PWRST_D3;
2638 set_pin_power_state(codec, 0x23, &parm);
2639 if (spec->smart51_enabled)
2640 set_pin_power_state(codec, 0x1a, &parm);
2641 snd_hda_codec_write(codec, 0x27, 0,
2642 AC_VERB_SET_POWER_STATE, parm);
2643 snd_hda_codec_write(codec, 0x25, 0,
2644 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002645 }
2646
2647 /* PW 3/4/7 (1ch/1dh/23h) */
2648 parm = AC_PWRST_D3;
2649 /* force to D0 for internal Speaker */
2650 set_pin_power_state(codec, 0x1c, &parm);
2651 set_pin_power_state(codec, 0x1d, &parm);
2652 if (is_8ch)
2653 set_pin_power_state(codec, 0x23, &parm);
2654
2655 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2656 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2657 imux_is_smixer ? AC_PWRST_D0 : parm);
2658 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2659 if (is_8ch) {
2660 snd_hda_codec_write(codec, 0x25, 0,
2661 AC_VERB_SET_POWER_STATE, parm);
2662 snd_hda_codec_write(codec, 0x27, 0,
2663 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002664 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2665 snd_hda_codec_write(codec, 0x25, 0,
2666 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002667}
2668
Lydia Wang518bf3b2009-10-10 19:07:29 +08002669static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002670static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002671{
2672 struct via_spec *spec;
2673 int err;
2674
Lydia Wang518bf3b2009-10-10 19:07:29 +08002675 if (get_codec_type(codec) == VT1708BCE)
2676 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002677
Josepch Chanf7278fd2007-12-13 16:40:40 +01002678 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002679 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002680 if (spec == NULL)
2681 return -ENOMEM;
2682
Takashi Iwai620e2b22011-06-17 17:19:19 +02002683 spec->aa_mix_nid = 0x16;
2684
Josepch Chanf7278fd2007-12-13 16:40:40 +01002685 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002686 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002687 if (err < 0) {
2688 via_free(codec);
2689 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002690 }
2691
Josepch Chanf7278fd2007-12-13 16:40:40 +01002692 codec->patch_ops = via_patch_ops;
2693
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002694 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2695
Josepch Chanf7278fd2007-12-13 16:40:40 +01002696 return 0;
2697}
2698
Harald Welted949cac2008-09-09 15:56:01 +08002699/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002700static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002701 /* Enable Mic Boost Volume backdoor */
2702 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002703 /* don't bybass mixer */
2704 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002705 { }
2706};
2707
Takashi Iwai9da29272009-05-07 16:31:14 +02002708/* fill out digital output widgets; one for master and one for slave outputs */
2709static void fill_dig_outs(struct hda_codec *codec)
2710{
2711 struct via_spec *spec = codec->spec;
2712 int i;
2713
2714 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2715 hda_nid_t nid;
2716 int conn;
2717
2718 nid = spec->autocfg.dig_out_pins[i];
2719 if (!nid)
2720 continue;
2721 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2722 if (conn < 1)
2723 continue;
2724 if (!spec->multiout.dig_out_nid)
2725 spec->multiout.dig_out_nid = nid;
2726 else {
2727 spec->slave_dig_outs[0] = nid;
2728 break; /* at most two dig outs */
2729 }
2730 }
2731}
2732
Takashi Iwai12daef62011-06-18 17:45:49 +02002733static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002734{
2735 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002736 hda_nid_t dig_nid;
2737 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002738
Takashi Iwai12daef62011-06-18 17:45:49 +02002739 if (!spec->autocfg.dig_in_pin)
2740 return;
Harald Welted949cac2008-09-09 15:56:01 +08002741
Takashi Iwai12daef62011-06-18 17:45:49 +02002742 dig_nid = codec->start_nid;
2743 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2744 unsigned int wcaps = get_wcaps(codec, dig_nid);
2745 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2746 continue;
2747 if (!(wcaps & AC_WCAP_DIGITAL))
2748 continue;
2749 if (!(wcaps & AC_WCAP_CONN_LIST))
2750 continue;
2751 err = get_connection_index(codec, dig_nid,
2752 spec->autocfg.dig_in_pin);
2753 if (err >= 0) {
2754 spec->dig_in_nid = dig_nid;
2755 break;
2756 }
2757 }
Harald Welted949cac2008-09-09 15:56:01 +08002758}
2759
Lydia Wang6369bcf2009-10-10 19:08:31 +08002760static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2761 int offset, int num_steps, int step_size)
2762{
2763 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2764 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2765 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2766 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2767 (0 << AC_AMPCAP_MUTE_SHIFT));
2768}
2769
Harald Welted949cac2008-09-09 15:56:01 +08002770static int patch_vt1708S(struct hda_codec *codec)
2771{
2772 struct via_spec *spec;
2773 int err;
2774
2775 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002776 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002777 if (spec == NULL)
2778 return -ENOMEM;
2779
Takashi Iwai620e2b22011-06-17 17:19:19 +02002780 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002781 override_mic_boost(codec, 0x1a, 0, 3, 40);
2782 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002783
Harald Welted949cac2008-09-09 15:56:01 +08002784 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002785 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002786 if (err < 0) {
2787 via_free(codec);
2788 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002789 }
2790
Takashi Iwai096a8852011-06-20 12:09:02 +02002791 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002792
Harald Welted949cac2008-09-09 15:56:01 +08002793 codec->patch_ops = via_patch_ops;
2794
Lydia Wang518bf3b2009-10-10 19:07:29 +08002795 /* correct names for VT1708BCE */
2796 if (get_codec_type(codec) == VT1708BCE) {
2797 kfree(codec->chip_name);
2798 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2799 snprintf(codec->bus->card->mixername,
2800 sizeof(codec->bus->card->mixername),
2801 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002802 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002803 /* correct names for VT1705 */
2804 if (codec->vendor_id == 0x11064397) {
2805 kfree(codec->chip_name);
2806 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2807 snprintf(codec->bus->card->mixername,
2808 sizeof(codec->bus->card->mixername),
2809 "%s %s", codec->vendor_name, codec->chip_name);
2810 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002811 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002812 return 0;
2813}
2814
2815/* Patch for VT1702 */
2816
Takashi Iwai096a8852011-06-20 12:09:02 +02002817static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002818 /* mixer enable */
2819 {0x1, 0xF88, 0x3},
2820 /* GPIO 0~2 */
2821 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002822 { }
2823};
2824
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002825static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2826{
2827 int imux_is_smixer =
2828 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2829 unsigned int parm;
2830 /* inputs */
2831 /* PW 1/2/5 (14h/15h/18h) */
2832 parm = AC_PWRST_D3;
2833 set_pin_power_state(codec, 0x14, &parm);
2834 set_pin_power_state(codec, 0x15, &parm);
2835 set_pin_power_state(codec, 0x18, &parm);
2836 if (imux_is_smixer)
2837 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2838 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2839 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2840 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2841 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2842 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2843
2844 /* outputs */
2845 /* PW 3/4 (16h/17h) */
2846 parm = AC_PWRST_D3;
2847 set_pin_power_state(codec, 0x17, &parm);
2848 set_pin_power_state(codec, 0x16, &parm);
2849 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2850 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2851 imux_is_smixer ? AC_PWRST_D0 : parm);
2852 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2853 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2854}
2855
Harald Welted949cac2008-09-09 15:56:01 +08002856static int patch_vt1702(struct hda_codec *codec)
2857{
2858 struct via_spec *spec;
2859 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002860
2861 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002862 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002863 if (spec == NULL)
2864 return -ENOMEM;
2865
Takashi Iwai620e2b22011-06-17 17:19:19 +02002866 spec->aa_mix_nid = 0x1a;
2867
Takashi Iwai12daef62011-06-18 17:45:49 +02002868 /* limit AA path volume to 0 dB */
2869 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2870 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2871 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2872 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2873 (1 << AC_AMPCAP_MUTE_SHIFT));
2874
Harald Welted949cac2008-09-09 15:56:01 +08002875 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002876 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002877 if (err < 0) {
2878 via_free(codec);
2879 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002880 }
2881
Takashi Iwai096a8852011-06-20 12:09:02 +02002882 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002883
Harald Welted949cac2008-09-09 15:56:01 +08002884 codec->patch_ops = via_patch_ops;
2885
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002886 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002887 return 0;
2888}
2889
Lydia Wangeb7188c2009-10-10 19:08:34 +08002890/* Patch for VT1718S */
2891
Takashi Iwai096a8852011-06-20 12:09:02 +02002892static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002893 /* Enable MW0 adjust Gain 5 */
2894 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002895 /* Enable Boost Volume backdoor */
2896 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002897
Lydia Wangeb7188c2009-10-10 19:08:34 +08002898 { }
2899};
2900
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002901static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2902{
2903 struct via_spec *spec = codec->spec;
2904 int imux_is_smixer;
2905 unsigned int parm;
2906 /* MUX6 (1eh) = stereo mixer */
2907 imux_is_smixer =
2908 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2909 /* inputs */
2910 /* PW 5/6/7 (29h/2ah/2bh) */
2911 parm = AC_PWRST_D3;
2912 set_pin_power_state(codec, 0x29, &parm);
2913 set_pin_power_state(codec, 0x2a, &parm);
2914 set_pin_power_state(codec, 0x2b, &parm);
2915 if (imux_is_smixer)
2916 parm = AC_PWRST_D0;
2917 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2918 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2919 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2920 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2921 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2922
2923 /* outputs */
2924 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2925 parm = AC_PWRST_D3;
2926 set_pin_power_state(codec, 0x27, &parm);
2927 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2928 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2929
2930 /* PW2 (26h), AOW2 (ah) */
2931 parm = AC_PWRST_D3;
2932 set_pin_power_state(codec, 0x26, &parm);
2933 if (spec->smart51_enabled)
2934 set_pin_power_state(codec, 0x2b, &parm);
2935 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2936
2937 /* PW0 (24h), AOW0 (8h) */
2938 parm = AC_PWRST_D3;
2939 set_pin_power_state(codec, 0x24, &parm);
2940 if (!spec->hp_independent_mode) /* check for redirected HP */
2941 set_pin_power_state(codec, 0x28, &parm);
2942 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2943 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2944 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2945 imux_is_smixer ? AC_PWRST_D0 : parm);
2946
2947 /* PW1 (25h), AOW1 (9h) */
2948 parm = AC_PWRST_D3;
2949 set_pin_power_state(codec, 0x25, &parm);
2950 if (spec->smart51_enabled)
2951 set_pin_power_state(codec, 0x2a, &parm);
2952 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2953
2954 if (spec->hp_independent_mode) {
2955 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2956 parm = AC_PWRST_D3;
2957 set_pin_power_state(codec, 0x28, &parm);
2958 snd_hda_codec_write(codec, 0x1b, 0,
2959 AC_VERB_SET_POWER_STATE, parm);
2960 snd_hda_codec_write(codec, 0x34, 0,
2961 AC_VERB_SET_POWER_STATE, parm);
2962 snd_hda_codec_write(codec, 0xc, 0,
2963 AC_VERB_SET_POWER_STATE, parm);
2964 }
2965}
2966
Lydia Wangeb7188c2009-10-10 19:08:34 +08002967static int patch_vt1718S(struct hda_codec *codec)
2968{
2969 struct via_spec *spec;
2970 int err;
2971
2972 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002973 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002974 if (spec == NULL)
2975 return -ENOMEM;
2976
Takashi Iwai620e2b22011-06-17 17:19:19 +02002977 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002978 override_mic_boost(codec, 0x2b, 0, 3, 40);
2979 override_mic_boost(codec, 0x29, 0, 3, 40);
Lydia Wangc4394f52011-07-04 16:54:15 +08002980 spec->dac_mixer_idx = 5;
Takashi Iwai620e2b22011-06-17 17:19:19 +02002981
Lydia Wangeb7188c2009-10-10 19:08:34 +08002982 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002983 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002984 if (err < 0) {
2985 via_free(codec);
2986 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002987 }
2988
Takashi Iwai096a8852011-06-20 12:09:02 +02002989 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002990
Lydia Wangeb7188c2009-10-10 19:08:34 +08002991 codec->patch_ops = via_patch_ops;
2992
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002993 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2994
Lydia Wangeb7188c2009-10-10 19:08:34 +08002995 return 0;
2996}
Lydia Wangf3db4232009-10-10 19:08:41 +08002997
2998/* Patch for VT1716S */
2999
3000static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3001 struct snd_ctl_elem_info *uinfo)
3002{
3003 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3004 uinfo->count = 1;
3005 uinfo->value.integer.min = 0;
3006 uinfo->value.integer.max = 1;
3007 return 0;
3008}
3009
3010static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3011 struct snd_ctl_elem_value *ucontrol)
3012{
3013 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3014 int index = 0;
3015
3016 index = snd_hda_codec_read(codec, 0x26, 0,
3017 AC_VERB_GET_CONNECT_SEL, 0);
3018 if (index != -1)
3019 *ucontrol->value.integer.value = index;
3020
3021 return 0;
3022}
3023
3024static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3025 struct snd_ctl_elem_value *ucontrol)
3026{
3027 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3028 struct via_spec *spec = codec->spec;
3029 int index = *ucontrol->value.integer.value;
3030
3031 snd_hda_codec_write(codec, 0x26, 0,
3032 AC_VERB_SET_CONNECT_SEL, index);
3033 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003034 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003035 return 1;
3036}
3037
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003038static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003039 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3040 {
3041 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3042 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003043 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003044 .count = 1,
3045 .info = vt1716s_dmic_info,
3046 .get = vt1716s_dmic_get,
3047 .put = vt1716s_dmic_put,
3048 },
3049 {} /* end */
3050};
3051
3052
3053/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003054static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003055 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3056 { } /* end */
3057};
3058
Takashi Iwai096a8852011-06-20 12:09:02 +02003059static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003060 /* Enable Boost Volume backdoor */
3061 {0x1, 0xf8a, 0x80},
3062 /* don't bybass mixer */
3063 {0x1, 0xf88, 0xc0},
3064 /* Enable mono output */
3065 {0x1, 0xf90, 0x08},
3066 { }
3067};
3068
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003069static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3070{
3071 struct via_spec *spec = codec->spec;
3072 int imux_is_smixer;
3073 unsigned int parm;
3074 unsigned int mono_out, present;
3075 /* SW0 (17h) = stereo mixer */
3076 imux_is_smixer =
3077 (snd_hda_codec_read(codec, 0x17, 0,
3078 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3079 /* inputs */
3080 /* PW 1/2/5 (1ah/1bh/1eh) */
3081 parm = AC_PWRST_D3;
3082 set_pin_power_state(codec, 0x1a, &parm);
3083 set_pin_power_state(codec, 0x1b, &parm);
3084 set_pin_power_state(codec, 0x1e, &parm);
3085 if (imux_is_smixer)
3086 parm = AC_PWRST_D0;
3087 /* SW0 (17h), AIW0(13h) */
3088 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3089 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3090
3091 parm = AC_PWRST_D3;
3092 set_pin_power_state(codec, 0x1e, &parm);
3093 /* PW11 (22h) */
3094 if (spec->dmic_enabled)
3095 set_pin_power_state(codec, 0x22, &parm);
3096 else
3097 snd_hda_codec_write(codec, 0x22, 0,
3098 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3099
3100 /* SW2(26h), AIW1(14h) */
3101 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3102 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3103
3104 /* outputs */
3105 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3106 parm = AC_PWRST_D3;
3107 set_pin_power_state(codec, 0x19, &parm);
3108 /* Smart 5.1 PW2(1bh) */
3109 if (spec->smart51_enabled)
3110 set_pin_power_state(codec, 0x1b, &parm);
3111 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3112 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3113
3114 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3115 parm = AC_PWRST_D3;
3116 set_pin_power_state(codec, 0x23, &parm);
3117 /* Smart 5.1 PW1(1ah) */
3118 if (spec->smart51_enabled)
3119 set_pin_power_state(codec, 0x1a, &parm);
3120 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3121
3122 /* Smart 5.1 PW5(1eh) */
3123 if (spec->smart51_enabled)
3124 set_pin_power_state(codec, 0x1e, &parm);
3125 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3126
3127 /* Mono out */
3128 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3129 present = snd_hda_jack_detect(codec, 0x1c);
3130
3131 if (present)
3132 mono_out = 0;
3133 else {
3134 present = snd_hda_jack_detect(codec, 0x1d);
3135 if (!spec->hp_independent_mode && present)
3136 mono_out = 0;
3137 else
3138 mono_out = 1;
3139 }
3140 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3141 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3142 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3143 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3144
3145 /* PW 3/4 (1ch/1dh) */
3146 parm = AC_PWRST_D3;
3147 set_pin_power_state(codec, 0x1c, &parm);
3148 set_pin_power_state(codec, 0x1d, &parm);
3149 /* HP Independent Mode, power on AOW3 */
3150 if (spec->hp_independent_mode)
3151 snd_hda_codec_write(codec, 0x25, 0,
3152 AC_VERB_SET_POWER_STATE, parm);
3153
3154 /* force to D0 for internal Speaker */
3155 /* MW0 (16h), AOW0 (10h) */
3156 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3157 imux_is_smixer ? AC_PWRST_D0 : parm);
3158 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3159 mono_out ? AC_PWRST_D0 : parm);
3160}
3161
Lydia Wangf3db4232009-10-10 19:08:41 +08003162static int patch_vt1716S(struct hda_codec *codec)
3163{
3164 struct via_spec *spec;
3165 int err;
3166
3167 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003168 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003169 if (spec == NULL)
3170 return -ENOMEM;
3171
Takashi Iwai620e2b22011-06-17 17:19:19 +02003172 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003173 override_mic_boost(codec, 0x1a, 0, 3, 40);
3174 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003175
Lydia Wangf3db4232009-10-10 19:08:41 +08003176 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003177 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003178 if (err < 0) {
3179 via_free(codec);
3180 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003181 }
3182
Takashi Iwai096a8852011-06-20 12:09:02 +02003183 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003184
Lydia Wangf3db4232009-10-10 19:08:41 +08003185 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3186 spec->num_mixers++;
3187
3188 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3189
3190 codec->patch_ops = via_patch_ops;
3191
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003192 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003193 return 0;
3194}
Lydia Wang25eaba22009-10-10 19:08:43 +08003195
3196/* for vt2002P */
3197
Takashi Iwai096a8852011-06-20 12:09:02 +02003198static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003199 /* Class-D speaker related verbs */
3200 {0x1, 0xfe0, 0x4},
3201 {0x1, 0xfe9, 0x80},
3202 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003203 /* Enable Boost Volume backdoor */
3204 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003205 /* Enable AOW0 to MW9 */
3206 {0x1, 0xfb8, 0x88},
3207 { }
3208};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003209
Takashi Iwai096a8852011-06-20 12:09:02 +02003210static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003211 /* Enable Boost Volume backdoor */
3212 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003213 /* Enable AOW0 to MW9 */
3214 {0x1, 0xfb8, 0x88},
3215 { }
3216};
Lydia Wang25eaba22009-10-10 19:08:43 +08003217
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003218static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3219{
3220 struct via_spec *spec = codec->spec;
3221 int imux_is_smixer;
3222 unsigned int parm;
3223 unsigned int present;
3224 /* MUX9 (1eh) = stereo mixer */
3225 imux_is_smixer =
3226 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3227 /* inputs */
3228 /* PW 5/6/7 (29h/2ah/2bh) */
3229 parm = AC_PWRST_D3;
3230 set_pin_power_state(codec, 0x29, &parm);
3231 set_pin_power_state(codec, 0x2a, &parm);
3232 set_pin_power_state(codec, 0x2b, &parm);
3233 parm = AC_PWRST_D0;
3234 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3235 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3236 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3237 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3238 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3239
3240 /* outputs */
3241 /* AOW0 (8h)*/
3242 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3243
Lydia Wang118909562011-03-23 17:57:34 +08003244 if (spec->codec_type == VT1802) {
3245 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3246 parm = AC_PWRST_D3;
3247 set_pin_power_state(codec, 0x28, &parm);
3248 snd_hda_codec_write(codec, 0x18, 0,
3249 AC_VERB_SET_POWER_STATE, parm);
3250 snd_hda_codec_write(codec, 0x38, 0,
3251 AC_VERB_SET_POWER_STATE, parm);
3252 } else {
3253 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3254 parm = AC_PWRST_D3;
3255 set_pin_power_state(codec, 0x26, &parm);
3256 snd_hda_codec_write(codec, 0x1c, 0,
3257 AC_VERB_SET_POWER_STATE, parm);
3258 snd_hda_codec_write(codec, 0x37, 0,
3259 AC_VERB_SET_POWER_STATE, parm);
3260 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003261
Lydia Wang118909562011-03-23 17:57:34 +08003262 if (spec->codec_type == VT1802) {
3263 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3264 parm = AC_PWRST_D3;
3265 set_pin_power_state(codec, 0x25, &parm);
3266 snd_hda_codec_write(codec, 0x15, 0,
3267 AC_VERB_SET_POWER_STATE, parm);
3268 snd_hda_codec_write(codec, 0x35, 0,
3269 AC_VERB_SET_POWER_STATE, parm);
3270 } else {
3271 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3272 parm = AC_PWRST_D3;
3273 set_pin_power_state(codec, 0x25, &parm);
3274 snd_hda_codec_write(codec, 0x19, 0,
3275 AC_VERB_SET_POWER_STATE, parm);
3276 snd_hda_codec_write(codec, 0x35, 0,
3277 AC_VERB_SET_POWER_STATE, parm);
3278 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003279
3280 if (spec->hp_independent_mode)
3281 snd_hda_codec_write(codec, 0x9, 0,
3282 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3283
3284 /* Class-D */
3285 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3286 present = snd_hda_jack_detect(codec, 0x25);
3287
3288 parm = AC_PWRST_D3;
3289 set_pin_power_state(codec, 0x24, &parm);
3290 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003291 if (spec->codec_type == VT1802)
3292 snd_hda_codec_write(codec, 0x14, 0,
3293 AC_VERB_SET_POWER_STATE, parm);
3294 else
3295 snd_hda_codec_write(codec, 0x18, 0,
3296 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003297 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3298
3299 /* Mono Out */
3300 present = snd_hda_jack_detect(codec, 0x26);
3301
3302 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003303 if (spec->codec_type == VT1802) {
3304 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3305 snd_hda_codec_write(codec, 0x33, 0,
3306 AC_VERB_SET_POWER_STATE, parm);
3307 snd_hda_codec_write(codec, 0x1c, 0,
3308 AC_VERB_SET_POWER_STATE, parm);
3309 snd_hda_codec_write(codec, 0x3c, 0,
3310 AC_VERB_SET_POWER_STATE, parm);
3311 } else {
3312 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3313 snd_hda_codec_write(codec, 0x31, 0,
3314 AC_VERB_SET_POWER_STATE, parm);
3315 snd_hda_codec_write(codec, 0x17, 0,
3316 AC_VERB_SET_POWER_STATE, parm);
3317 snd_hda_codec_write(codec, 0x3b, 0,
3318 AC_VERB_SET_POWER_STATE, parm);
3319 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003320 /* MW9 (21h) */
3321 if (imux_is_smixer || !is_aa_path_mute(codec))
3322 snd_hda_codec_write(codec, 0x21, 0,
3323 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3324 else
3325 snd_hda_codec_write(codec, 0x21, 0,
3326 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3327}
Lydia Wang25eaba22009-10-10 19:08:43 +08003328
3329/* patch for vt2002P */
3330static int patch_vt2002P(struct hda_codec *codec)
3331{
3332 struct via_spec *spec;
3333 int err;
3334
3335 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003336 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003337 if (spec == NULL)
3338 return -ENOMEM;
3339
Takashi Iwai620e2b22011-06-17 17:19:19 +02003340 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003341 override_mic_boost(codec, 0x2b, 0, 3, 40);
3342 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003343
Lydia Wang25eaba22009-10-10 19:08:43 +08003344 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003345 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003346 if (err < 0) {
3347 via_free(codec);
3348 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003349 }
3350
Lydia Wang118909562011-03-23 17:57:34 +08003351 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003352 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003353 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003354 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003355
Lydia Wang25eaba22009-10-10 19:08:43 +08003356 codec->patch_ops = via_patch_ops;
3357
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003358 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003359 return 0;
3360}
Lydia Wangab6734e2009-10-10 19:08:46 +08003361
3362/* for vt1812 */
3363
Takashi Iwai096a8852011-06-20 12:09:02 +02003364static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003365 /* Enable Boost Volume backdoor */
3366 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003367 /* Enable AOW0 to MW9 */
3368 {0x1, 0xfb8, 0xa8},
3369 { }
3370};
3371
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003372static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3373{
3374 struct via_spec *spec = codec->spec;
3375 int imux_is_smixer =
3376 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3377 unsigned int parm;
3378 unsigned int present;
3379 /* MUX10 (1eh) = stereo mixer */
3380 imux_is_smixer =
3381 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3382 /* inputs */
3383 /* PW 5/6/7 (29h/2ah/2bh) */
3384 parm = AC_PWRST_D3;
3385 set_pin_power_state(codec, 0x29, &parm);
3386 set_pin_power_state(codec, 0x2a, &parm);
3387 set_pin_power_state(codec, 0x2b, &parm);
3388 parm = AC_PWRST_D0;
3389 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3390 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3391 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3392 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3393 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3394
3395 /* outputs */
3396 /* AOW0 (8h)*/
3397 snd_hda_codec_write(codec, 0x8, 0,
3398 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3399
3400 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3401 parm = AC_PWRST_D3;
3402 set_pin_power_state(codec, 0x28, &parm);
3403 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3404 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3405
3406 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3407 parm = AC_PWRST_D3;
3408 set_pin_power_state(codec, 0x25, &parm);
3409 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3410 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3411 if (spec->hp_independent_mode)
3412 snd_hda_codec_write(codec, 0x9, 0,
3413 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3414
3415 /* Internal Speaker */
3416 /* PW0 (24h), MW0(14h), MUX0(34h) */
3417 present = snd_hda_jack_detect(codec, 0x25);
3418
3419 parm = AC_PWRST_D3;
3420 set_pin_power_state(codec, 0x24, &parm);
3421 if (present) {
3422 snd_hda_codec_write(codec, 0x14, 0,
3423 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3424 snd_hda_codec_write(codec, 0x34, 0,
3425 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3426 } else {
3427 snd_hda_codec_write(codec, 0x14, 0,
3428 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3429 snd_hda_codec_write(codec, 0x34, 0,
3430 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3431 }
3432
3433
3434 /* Mono Out */
3435 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3436 present = snd_hda_jack_detect(codec, 0x28);
3437
3438 parm = AC_PWRST_D3;
3439 set_pin_power_state(codec, 0x31, &parm);
3440 if (present) {
3441 snd_hda_codec_write(codec, 0x1c, 0,
3442 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3443 snd_hda_codec_write(codec, 0x3c, 0,
3444 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3445 snd_hda_codec_write(codec, 0x3e, 0,
3446 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3447 } else {
3448 snd_hda_codec_write(codec, 0x1c, 0,
3449 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3450 snd_hda_codec_write(codec, 0x3c, 0,
3451 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3452 snd_hda_codec_write(codec, 0x3e, 0,
3453 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3454 }
3455
3456 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3457 parm = AC_PWRST_D3;
3458 set_pin_power_state(codec, 0x33, &parm);
3459 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3460 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3461
3462}
Lydia Wangab6734e2009-10-10 19:08:46 +08003463
3464/* patch for vt1812 */
3465static int patch_vt1812(struct hda_codec *codec)
3466{
3467 struct via_spec *spec;
3468 int err;
3469
3470 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003471 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003472 if (spec == NULL)
3473 return -ENOMEM;
3474
Takashi Iwai620e2b22011-06-17 17:19:19 +02003475 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003476 override_mic_boost(codec, 0x2b, 0, 3, 40);
3477 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003478
Lydia Wangab6734e2009-10-10 19:08:46 +08003479 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003480 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003481 if (err < 0) {
3482 via_free(codec);
3483 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003484 }
3485
Takashi Iwai096a8852011-06-20 12:09:02 +02003486 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003487
Lydia Wangab6734e2009-10-10 19:08:46 +08003488 codec->patch_ops = via_patch_ops;
3489
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003490 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003491 return 0;
3492}
3493
Joseph Chanc577b8a2006-11-29 15:29:40 +01003494/*
3495 * patch entries
3496 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003497static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003498 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3499 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3500 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3501 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3502 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003503 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003504 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003505 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003506 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003507 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003508 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003509 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003510 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003511 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003512 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003513 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003514 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003515 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003516 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003517 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003518 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003519 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003520 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003521 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003522 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003523 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003524 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003525 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003526 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003527 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003528 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003529 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003530 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003531 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003532 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003533 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003534 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003535 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003536 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003537 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003538 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003539 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003540 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003541 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003542 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003543 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003544 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003545 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003546 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003547 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003548 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003549 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003550 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003551 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003552 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003553 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003554 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003555 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003556 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003557 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003558 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003559 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003560 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003561 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003562 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003563 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003564 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003565 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003566 { .id = 0x11060428, .name = "VT1718S",
3567 .patch = patch_vt1718S},
3568 { .id = 0x11064428, .name = "VT1718S",
3569 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003570 { .id = 0x11060441, .name = "VT2020",
3571 .patch = patch_vt1718S},
3572 { .id = 0x11064441, .name = "VT1828S",
3573 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003574 { .id = 0x11060433, .name = "VT1716S",
3575 .patch = patch_vt1716S},
3576 { .id = 0x1106a721, .name = "VT1716S",
3577 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003578 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3579 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003580 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003581 { .id = 0x11060440, .name = "VT1818S",
3582 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003583 { .id = 0x11060446, .name = "VT1802",
3584 .patch = patch_vt2002P},
3585 { .id = 0x11068446, .name = "VT1802",
3586 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003587 {} /* terminator */
3588};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003589
3590MODULE_ALIAS("snd-hda-codec-id:1106*");
3591
3592static struct hda_codec_preset_list via_list = {
3593 .preset = snd_hda_preset_via,
3594 .owner = THIS_MODULE,
3595};
3596
3597MODULE_LICENSE("GPL");
3598MODULE_DESCRIPTION("VIA HD-audio codec");
3599
3600static int __init patch_via_init(void)
3601{
3602 return snd_hda_add_codec_preset(&via_list);
3603}
3604
3605static void __exit patch_via_exit(void)
3606{
3607 snd_hda_delete_codec_preset(&via_list);
3608}
3609
3610module_init(patch_via_init)
3611module_exit(patch_via_exit)