blob: 89dd29db97e38e873d8d7132f4d4aa1062906959 [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 Iwai09a9ad62011-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 Iwai09a9ad62011-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 Wangbb3c6bf2009-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 Iwai09a9ad62011-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 Iwai09a9ad62011-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 Iwai09a9ad62011-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 Wange5e14682011-07-01 10:55:07 +0800455 if (get_wcaps_type(get_wcaps(codec, src)) == AC_WID_AUD_OUT &&
456 get_wcaps_type(get_wcaps(codec, dst)) == AC_WID_AUD_MIX)
457 continue;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200458 if (have_mute(codec, dst, HDA_INPUT)) {
459 int val = enable ? AMP_IN_UNMUTE(idx) :
460 AMP_IN_MUTE(idx);
461 snd_hda_codec_write(codec, dst, 0,
462 AC_VERB_SET_AMP_GAIN_MUTE, val);
463 }
464 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
465 continue;
466 if (have_mute(codec, src, HDA_OUTPUT)) {
467 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
468 snd_hda_codec_write(codec, src, 0,
469 AC_VERB_SET_AMP_GAIN_MUTE, val);
470 }
471 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200472}
473
474/* set the given pin as output */
475static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
476 int pin_type)
477{
478 if (!pin)
479 return;
480 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
481 pin_type);
482 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
483 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200484 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100485}
486
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200487static void via_auto_init_output(struct hda_codec *codec,
488 struct nid_path *path, int pin_type,
489 bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200490{
491 struct via_spec *spec = codec->spec;
492 unsigned int caps;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200493 hda_nid_t pin, nid;
494 int i, idx;
Takashi Iwai5d417622011-06-20 11:32:27 +0200495
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200496 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200497 return;
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200498 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200499
500 init_output_pin(codec, pin, pin_type);
501 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
502 if (caps & AC_AMPCAP_MUTE) {
503 unsigned int val;
504 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
505 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
506 AMP_OUT_MUTE | val);
507 }
508
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200509 activate_output_path(codec, path, true, force);
510
511 /* initialize the AA-path */
512 if (!spec->aa_mix_nid)
513 return;
Takashi Iwai8e3679d2011-06-21 09:01:36 +0200514 for (i = path->depth - 1; i > 0; i--) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200515 nid = path->path[i];
516 idx = get_connection_index(codec, nid, spec->aa_mix_nid);
517 if (idx >= 0) {
518 if (have_mute(codec, nid, HDA_INPUT))
519 snd_hda_codec_write(codec, nid, 0,
520 AC_VERB_SET_AMP_GAIN_MUTE,
521 AMP_IN_UNMUTE(idx));
522 break;
523 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200524 }
525}
526
Joseph Chanc577b8a2006-11-29 15:29:40 +0100527static void via_auto_init_multi_out(struct hda_codec *codec)
528{
529 struct via_spec *spec = codec->spec;
530 int i;
531
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200532 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200533 via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, true);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100534}
535
536static void via_auto_init_hp_out(struct hda_codec *codec)
537{
538 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100539
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200540 if (!spec->hp_dac_nid) {
541 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
542 return;
543 }
544 if (spec->hp_independent_mode) {
545 activate_output_path(codec, &spec->hp_dep_path, false, false);
546 via_auto_init_output(codec, &spec->hp_path, PIN_HP, true);
547 } else {
548 activate_output_path(codec, &spec->hp_path, false, false);
549 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
550 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100551}
552
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200553static void via_auto_init_speaker_out(struct hda_codec *codec)
554{
555 struct via_spec *spec = codec->spec;
556
557 if (spec->autocfg.speaker_outs)
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200558 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, true);
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200559}
560
Takashi Iwaif4a78282011-06-17 18:46:48 +0200561static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200562
Joseph Chanc577b8a2006-11-29 15:29:40 +0100563static void via_auto_init_analog_input(struct hda_codec *codec)
564{
565 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200566 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200567 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200568 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200569 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100570
Takashi Iwai096a8852011-06-20 12:09:02 +0200571 /* init ADCs */
572 for (i = 0; i < spec->num_adc_nids; i++) {
573 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
574 AC_VERB_SET_AMP_GAIN_MUTE,
575 AMP_IN_UNMUTE(0));
576 }
577
578 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200579 for (i = 0; i < cfg->num_inputs; i++) {
580 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200581 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200582 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100583 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200584 ctl = PIN_VREF50;
585 else
586 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100587 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200588 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100589 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200590
591 /* init input-src */
592 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200593 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
594 if (spec->mux_nids[adc_idx]) {
595 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
596 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
597 AC_VERB_SET_CONNECT_SEL,
598 mux_idx);
599 }
600 if (spec->dyn_adc_switch)
601 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200602 }
603
604 /* init aa-mixer */
605 if (!spec->aa_mix_nid)
606 return;
607 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
608 ARRAY_SIZE(conn));
609 for (i = 0; i < num_conns; i++) {
610 unsigned int caps = get_wcaps(codec, conn[i]);
611 if (get_wcaps_type(caps) == AC_WID_PIN)
612 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
613 AC_VERB_SET_AMP_GAIN_MUTE,
614 AMP_IN_MUTE(i));
615 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100616}
Lydia Wangf5271102009-10-10 19:07:35 +0800617
618static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
619 unsigned int *affected_parm)
620{
621 unsigned parm;
622 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
623 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
624 >> AC_DEFCFG_MISC_SHIFT
625 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800626 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200627 unsigned present = 0;
628
629 no_presence |= spec->no_pin_power_ctl;
630 if (!no_presence)
631 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200632 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800633 || ((no_presence || present)
634 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800635 *affected_parm = AC_PWRST_D0; /* if it's connected */
636 parm = AC_PWRST_D0;
637 } else
638 parm = AC_PWRST_D3;
639
640 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
641}
642
Takashi Iwai24088a52011-06-17 16:59:21 +0200643static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
644 struct snd_ctl_elem_info *uinfo)
645{
646 static const char * const texts[] = {
647 "Disabled", "Enabled"
648 };
649
650 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
651 uinfo->count = 1;
652 uinfo->value.enumerated.items = 2;
653 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
654 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
655 strcpy(uinfo->value.enumerated.name,
656 texts[uinfo->value.enumerated.item]);
657 return 0;
658}
659
660static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
661 struct snd_ctl_elem_value *ucontrol)
662{
663 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
664 struct via_spec *spec = codec->spec;
665 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
666 return 0;
667}
668
669static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
670 struct snd_ctl_elem_value *ucontrol)
671{
672 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
673 struct via_spec *spec = codec->spec;
674 unsigned int val = !ucontrol->value.enumerated.item[0];
675
676 if (val == spec->no_pin_power_ctl)
677 return 0;
678 spec->no_pin_power_ctl = val;
679 set_widgets_power_state(codec);
680 return 1;
681}
682
683static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
684 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
685 .name = "Dynamic Power-Control",
686 .info = via_pin_power_ctl_info,
687 .get = via_pin_power_ctl_get,
688 .put = via_pin_power_ctl_put,
689};
690
691
Harald Welte0aa62ae2008-09-09 15:58:27 +0800692static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
693 struct snd_ctl_elem_info *uinfo)
694{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200695 static const char * const texts[] = { "OFF", "ON" };
696
697 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
698 uinfo->count = 1;
699 uinfo->value.enumerated.items = 2;
700 if (uinfo->value.enumerated.item >= 2)
701 uinfo->value.enumerated.item = 1;
702 strcpy(uinfo->value.enumerated.name,
703 texts[uinfo->value.enumerated.item]);
704 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800705}
706
707static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
708 struct snd_ctl_elem_value *ucontrol)
709{
710 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800711 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800712
Takashi Iwaiece8d042011-06-19 16:24:21 +0200713 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800714 return 0;
715}
716
Harald Welte0aa62ae2008-09-09 15:58:27 +0800717static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
718 struct snd_ctl_elem_value *ucontrol)
719{
720 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
721 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +0200722 int cur;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200723
Takashi Iwai25250502011-06-30 17:24:47 +0200724 /* no independent-hp status change during PCM playback is running */
725 if (spec->num_active_streams)
726 return -EBUSY;
727
728 cur = !!ucontrol->value.enumerated.item[0];
729 if (spec->hp_independent_mode == cur)
730 return 0;
731 spec->hp_independent_mode = cur;
732 if (cur) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200733 activate_output_path(codec, &spec->hp_dep_path, false, false);
734 activate_output_path(codec, &spec->hp_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200735 if (spec->hp_indep_shared)
736 activate_output_path(codec, &spec->out_path[HDA_SIDE],
737 false, false);
Takashi Iwai09a9ad62011-06-21 15:57:44 +0200738 } else {
739 activate_output_path(codec, &spec->hp_path, false, false);
740 activate_output_path(codec, &spec->hp_dep_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200741 if (spec->hp_indep_shared)
742 activate_output_path(codec, &spec->out_path[HDA_SIDE],
743 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200744 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800745
Lydia Wangce0e5a92011-03-22 16:22:37 +0800746 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800747 set_widgets_power_state(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200748 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800749}
750
Takashi Iwaiece8d042011-06-19 16:24:21 +0200751static const struct snd_kcontrol_new via_hp_mixer = {
752 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
753 .name = "Independent HP",
754 .info = via_independent_hp_info,
755 .get = via_independent_hp_get,
756 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800757};
758
Takashi Iwai3d83e572010-04-14 14:36:23 +0200759static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100760{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200761 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100762 struct snd_kcontrol_new *knew;
763 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100764
Takashi Iwaiece8d042011-06-19 16:24:21 +0200765 nid = spec->autocfg.hp_pins[0];
766 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200767 if (knew == NULL)
768 return -ENOMEM;
769
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100770 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100771
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100772 return 0;
773}
774
Lydia Wang1564b282009-10-10 19:07:52 +0800775static void notify_aa_path_ctls(struct hda_codec *codec)
776{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200777 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800778 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800779
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200780 for (i = 0; i < spec->smart51_nums; i++) {
781 struct snd_kcontrol *ctl;
782 struct snd_ctl_elem_id id;
783 memset(&id, 0, sizeof(id));
784 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
785 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800786 ctl = snd_hda_find_mixer_ctl(codec, id.name);
787 if (ctl)
788 snd_ctl_notify(codec->bus->card,
789 SNDRV_CTL_EVENT_MASK_VALUE,
790 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800791 }
792}
793
794static void mute_aa_path(struct hda_codec *codec, int mute)
795{
796 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200797 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800798 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200799
Lydia Wang1564b282009-10-10 19:07:52 +0800800 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200801 for (i = 0; i < spec->smart51_nums; i++) {
802 if (spec->smart51_idxs[i] < 0)
803 continue;
804 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
805 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800806 HDA_AMP_MUTE, val);
807 }
808}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200809
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200810static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
811{
812 struct via_spec *spec = codec->spec;
813 int i;
814
815 for (i = 0; i < spec->smart51_nums; i++)
816 if (spec->smart51_pins[i] == pin)
817 return true;
818 return false;
819}
820
Lydia Wang1564b282009-10-10 19:07:52 +0800821static int via_smart51_get(struct snd_kcontrol *kcontrol,
822 struct snd_ctl_elem_value *ucontrol)
823{
824 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
825 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800826
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200827 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800828 return 0;
829}
830
831static int via_smart51_put(struct snd_kcontrol *kcontrol,
832 struct snd_ctl_elem_value *ucontrol)
833{
834 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
835 struct via_spec *spec = codec->spec;
836 int out_in = *ucontrol->value.integer.value
837 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800838 int i;
839
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200840 for (i = 0; i < spec->smart51_nums; i++) {
841 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200842 unsigned int parm;
843
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200844 parm = snd_hda_codec_read(codec, nid, 0,
845 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
846 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
847 parm |= out_in;
848 snd_hda_codec_write(codec, nid, 0,
849 AC_VERB_SET_PIN_WIDGET_CONTROL,
850 parm);
851 if (out_in == AC_PINCTL_OUT_EN) {
852 mute_aa_path(codec, 1);
853 notify_aa_path_ctls(codec);
854 }
Lydia Wang1564b282009-10-10 19:07:52 +0800855 }
856 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800857 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800858 return 1;
859}
860
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200861static const struct snd_kcontrol_new via_smart51_mixer = {
862 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
863 .name = "Smart 5.1",
864 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200865 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200866 .get = via_smart51_get,
867 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800868};
869
Takashi Iwaif4a78282011-06-17 18:46:48 +0200870static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100871{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200872 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100873
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200874 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800875 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200876 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100877 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100878 return 0;
879}
880
Takashi Iwaiada509e2011-06-20 15:40:19 +0200881/* check AA path's mute status */
882static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800883{
Lydia Wangf5271102009-10-10 19:07:35 +0800884 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200885 const struct hda_amp_list *p;
886 int i, ch, v;
887
888 for (i = 0; i < spec->num_loopbacks; i++) {
889 p = &spec->loopback_list[i];
890 for (ch = 0; ch < 2; ch++) {
891 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
892 p->idx);
893 if (!(v & HDA_AMP_MUTE) && v > 0)
894 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800895 }
896 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200897 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800898}
899
900/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200901static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800902{
903 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200904 bool enable;
905 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800906
Takashi Iwaiada509e2011-06-20 15:40:19 +0200907 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800908
909 /* decide low current mode's verb & parameter */
910 switch (spec->codec_type) {
911 case VT1708B_8CH:
912 case VT1708B_4CH:
913 verb = 0xf70;
914 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
915 break;
916 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800917 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800918 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800919 verb = 0xf73;
920 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
921 break;
922 case VT1702:
923 verb = 0xf73;
924 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
925 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800926 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800927 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800928 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800929 verb = 0xf93;
930 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
931 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800932 default:
933 return; /* other codecs are not supported */
934 }
935 /* send verb */
936 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
937}
938
Joseph Chanc577b8a2006-11-29 15:29:40 +0100939/*
940 * generic initialization of ADC, input mixers and output mixers
941 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200942static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800943 /* power down jack detect function */
944 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +0100945 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100946};
947
Takashi Iwaiada509e2011-06-20 15:40:19 +0200948static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200949{
Takashi Iwaiada509e2011-06-20 15:40:19 +0200950 struct via_spec *spec = codec->spec;
951
952 if (active)
953 spec->num_active_streams++;
954 else
955 spec->num_active_streams--;
956 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200957}
958
Takashi Iwaiece8d042011-06-19 16:24:21 +0200959static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100960 struct hda_codec *codec,
961 struct snd_pcm_substream *substream)
962{
963 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +0200964 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200965 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +0200966
Takashi Iwai25250502011-06-30 17:24:47 +0200967 spec->multiout.hp_nid = 0;
968 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
969 if (!spec->hp_independent_mode) {
970 if (!spec->hp_indep_shared)
971 spec->multiout.hp_nid = spec->hp_dac_nid;
972 } else {
973 if (spec->hp_indep_shared)
974 spec->multiout.num_dacs = cfg->line_outs - 1;
975 }
976 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200977 set_stream_active(codec, true);
978 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
979 hinfo);
980 if (err < 0) {
981 spec->multiout.hp_nid = 0;
982 set_stream_active(codec, false);
983 return err;
984 }
985 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100986}
987
Takashi Iwaiece8d042011-06-19 16:24:21 +0200988static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +0200989 struct hda_codec *codec,
990 struct snd_pcm_substream *substream)
991{
Takashi Iwaiece8d042011-06-19 16:24:21 +0200992 struct via_spec *spec = codec->spec;
993
994 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200995 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +0200996 return 0;
997}
998
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200999static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1000 struct hda_codec *codec,
1001 struct snd_pcm_substream *substream)
1002{
1003 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001004
Takashi Iwaiece8d042011-06-19 16:24:21 +02001005 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001006 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001007 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1008 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001009 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001010 return 0;
1011}
1012
1013static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1014 struct hda_codec *codec,
1015 struct snd_pcm_substream *substream)
1016{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001017 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001018 return 0;
1019}
1020
1021static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1022 struct hda_codec *codec,
1023 unsigned int stream_tag,
1024 unsigned int format,
1025 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001026{
1027 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001028
Takashi Iwaiece8d042011-06-19 16:24:21 +02001029 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1030 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001031 vt1708_start_hp_work(spec);
1032 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001033}
1034
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001035static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1036 struct hda_codec *codec,
1037 unsigned int stream_tag,
1038 unsigned int format,
1039 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001040{
1041 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001042
Takashi Iwaiece8d042011-06-19 16:24:21 +02001043 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1044 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001045 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001046 return 0;
1047}
1048
1049static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1050 struct hda_codec *codec,
1051 struct snd_pcm_substream *substream)
1052{
1053 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001054
Takashi Iwaiece8d042011-06-19 16:24:21 +02001055 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001056 vt1708_stop_hp_work(spec);
1057 return 0;
1058}
1059
1060static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1061 struct hda_codec *codec,
1062 struct snd_pcm_substream *substream)
1063{
1064 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001065
Takashi Iwaiece8d042011-06-19 16:24:21 +02001066 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001067 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001068 return 0;
1069}
1070
Joseph Chanc577b8a2006-11-29 15:29:40 +01001071/*
1072 * Digital out
1073 */
1074static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1075 struct hda_codec *codec,
1076 struct snd_pcm_substream *substream)
1077{
1078 struct via_spec *spec = codec->spec;
1079 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1080}
1081
1082static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1083 struct hda_codec *codec,
1084 struct snd_pcm_substream *substream)
1085{
1086 struct via_spec *spec = codec->spec;
1087 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1088}
1089
Harald Welte5691ec72008-09-15 22:42:26 +08001090static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001091 struct hda_codec *codec,
1092 unsigned int stream_tag,
1093 unsigned int format,
1094 struct snd_pcm_substream *substream)
1095{
1096 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001097 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1098 stream_tag, format, substream);
1099}
Harald Welte5691ec72008-09-15 22:42:26 +08001100
Takashi Iwai9da29272009-05-07 16:31:14 +02001101static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1102 struct hda_codec *codec,
1103 struct snd_pcm_substream *substream)
1104{
1105 struct via_spec *spec = codec->spec;
1106 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001107 return 0;
1108}
1109
Joseph Chanc577b8a2006-11-29 15:29:40 +01001110/*
1111 * Analog capture
1112 */
1113static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1114 struct hda_codec *codec,
1115 unsigned int stream_tag,
1116 unsigned int format,
1117 struct snd_pcm_substream *substream)
1118{
1119 struct via_spec *spec = codec->spec;
1120
1121 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1122 stream_tag, 0, format);
1123 return 0;
1124}
1125
1126static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1127 struct hda_codec *codec,
1128 struct snd_pcm_substream *substream)
1129{
1130 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001131 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001132 return 0;
1133}
1134
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001135/* analog capture with dynamic ADC switching */
1136static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1137 struct hda_codec *codec,
1138 unsigned int stream_tag,
1139 unsigned int format,
1140 struct snd_pcm_substream *substream)
1141{
1142 struct via_spec *spec = codec->spec;
1143 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1144
1145 spec->cur_adc = spec->adc_nids[adc_idx];
1146 spec->cur_adc_stream_tag = stream_tag;
1147 spec->cur_adc_format = format;
1148 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
1149 return 0;
1150}
1151
1152static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1153 struct hda_codec *codec,
1154 struct snd_pcm_substream *substream)
1155{
1156 struct via_spec *spec = codec->spec;
1157
1158 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1159 spec->cur_adc = 0;
1160 return 0;
1161}
1162
1163/* re-setup the stream if running; called from input-src put */
1164static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1165{
1166 struct via_spec *spec = codec->spec;
1167 int adc_idx = spec->inputs[cur].adc_idx;
1168 hda_nid_t adc = spec->adc_nids[adc_idx];
1169
1170 if (spec->cur_adc && spec->cur_adc != adc) {
1171 /* stream is running, let's swap the current ADC */
1172 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1173 spec->cur_adc = adc;
1174 snd_hda_codec_setup_stream(codec, adc,
1175 spec->cur_adc_stream_tag, 0,
1176 spec->cur_adc_format);
1177 return true;
1178 }
1179 return false;
1180}
1181
Takashi Iwai9af74212011-06-18 16:17:45 +02001182static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001183 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001184 .channels_min = 2,
1185 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001186 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001187 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001188 .open = via_playback_multi_pcm_open,
1189 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001190 .prepare = via_playback_multi_pcm_prepare,
1191 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001192 },
1193};
1194
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001195static const struct hda_pcm_stream via_pcm_hp_playback = {
1196 .substreams = 1,
1197 .channels_min = 2,
1198 .channels_max = 2,
1199 /* NID is set in via_build_pcms */
1200 .ops = {
1201 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001202 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001203 .prepare = via_playback_hp_pcm_prepare,
1204 .cleanup = via_playback_hp_pcm_cleanup
1205 },
1206};
1207
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001208static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001209 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001210 .channels_min = 2,
1211 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001212 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001213 /* We got noisy outputs on the right channel on VT1708 when
1214 * 24bit samples are used. Until any workaround is found,
1215 * disable the 24bit format, so far.
1216 */
1217 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1218 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001219 .open = via_playback_multi_pcm_open,
1220 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001221 .prepare = via_playback_multi_pcm_prepare,
1222 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001223 },
1224};
1225
Takashi Iwai9af74212011-06-18 16:17:45 +02001226static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001227 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001228 .channels_min = 2,
1229 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001230 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001231 .ops = {
1232 .prepare = via_capture_pcm_prepare,
1233 .cleanup = via_capture_pcm_cleanup
1234 },
1235};
1236
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001237static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1238 .substreams = 1,
1239 .channels_min = 2,
1240 .channels_max = 2,
1241 /* NID is set in via_build_pcms */
1242 .ops = {
1243 .prepare = via_dyn_adc_capture_pcm_prepare,
1244 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1245 },
1246};
1247
Takashi Iwai9af74212011-06-18 16:17:45 +02001248static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001249 .substreams = 1,
1250 .channels_min = 2,
1251 .channels_max = 2,
1252 /* NID is set in via_build_pcms */
1253 .ops = {
1254 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001255 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001256 .prepare = via_dig_playback_pcm_prepare,
1257 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001258 },
1259};
1260
Takashi Iwai9af74212011-06-18 16:17:45 +02001261static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001262 .substreams = 1,
1263 .channels_min = 2,
1264 .channels_max = 2,
1265};
1266
Takashi Iwai370bafb2011-06-20 12:47:45 +02001267/*
1268 * slave controls for virtual master
1269 */
1270static const char * const via_slave_vols[] = {
1271 "Front Playback Volume",
1272 "Surround Playback Volume",
1273 "Center Playback Volume",
1274 "LFE Playback Volume",
1275 "Side Playback Volume",
1276 "Headphone Playback Volume",
1277 "Speaker Playback Volume",
1278 NULL,
1279};
1280
1281static const char * const via_slave_sws[] = {
1282 "Front Playback Switch",
1283 "Surround Playback Switch",
1284 "Center Playback Switch",
1285 "LFE Playback Switch",
1286 "Side Playback Switch",
1287 "Headphone Playback Switch",
1288 "Speaker Playback Switch",
1289 NULL,
1290};
1291
Joseph Chanc577b8a2006-11-29 15:29:40 +01001292static int via_build_controls(struct hda_codec *codec)
1293{
1294 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001295 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001296 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001297
Takashi Iwai24088a52011-06-17 16:59:21 +02001298 if (spec->set_widgets_power_state)
1299 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1300 return -ENOMEM;
1301
Joseph Chanc577b8a2006-11-29 15:29:40 +01001302 for (i = 0; i < spec->num_mixers; i++) {
1303 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1304 if (err < 0)
1305 return err;
1306 }
1307
1308 if (spec->multiout.dig_out_nid) {
1309 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001310 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001311 spec->multiout.dig_out_nid);
1312 if (err < 0)
1313 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001314 err = snd_hda_create_spdif_share_sw(codec,
1315 &spec->multiout);
1316 if (err < 0)
1317 return err;
1318 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001319 }
1320 if (spec->dig_in_nid) {
1321 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1322 if (err < 0)
1323 return err;
1324 }
Lydia Wang17314372009-10-10 19:07:37 +08001325
Takashi Iwai370bafb2011-06-20 12:47:45 +02001326 /* if we have no master control, let's create it */
1327 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1328 unsigned int vmaster_tlv[4];
1329 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1330 HDA_OUTPUT, vmaster_tlv);
1331 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1332 vmaster_tlv, via_slave_vols);
1333 if (err < 0)
1334 return err;
1335 }
1336 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1337 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1338 NULL, via_slave_sws);
1339 if (err < 0)
1340 return err;
1341 }
1342
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001343 /* assign Capture Source enums to NID */
1344 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1345 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001346 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001347 if (err < 0)
1348 return err;
1349 }
1350
Lydia Wang17314372009-10-10 19:07:37 +08001351 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001352 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001353 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001354
Takashi Iwai603c4012008-07-30 15:01:44 +02001355 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001356 return 0;
1357}
1358
1359static int via_build_pcms(struct hda_codec *codec)
1360{
1361 struct via_spec *spec = codec->spec;
1362 struct hda_pcm *info = spec->pcm_rec;
1363
1364 codec->num_pcms = 1;
1365 codec->pcm_info = info;
1366
Takashi Iwai82673bc2011-06-17 16:24:21 +02001367 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1368 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001369 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001370
1371 if (!spec->stream_analog_playback)
1372 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001373 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001374 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001375 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1376 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001377 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1378 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001379
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001380 if (!spec->stream_analog_capture) {
1381 if (spec->dyn_adc_switch)
1382 spec->stream_analog_capture =
1383 &via_pcm_dyn_adc_analog_capture;
1384 else
1385 spec->stream_analog_capture = &via_pcm_analog_capture;
1386 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001387 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1388 *spec->stream_analog_capture;
1389 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001390 if (!spec->dyn_adc_switch)
1391 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1392 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001393
1394 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1395 codec->num_pcms++;
1396 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001397 snprintf(spec->stream_name_digital,
1398 sizeof(spec->stream_name_digital),
1399 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001400 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001401 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001402 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001403 if (!spec->stream_digital_playback)
1404 spec->stream_digital_playback =
1405 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001406 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001407 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001408 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1409 spec->multiout.dig_out_nid;
1410 }
1411 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001412 if (!spec->stream_digital_capture)
1413 spec->stream_digital_capture =
1414 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001415 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001416 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001417 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1418 spec->dig_in_nid;
1419 }
1420 }
1421
Takashi Iwaiece8d042011-06-19 16:24:21 +02001422 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001423 codec->num_pcms++;
1424 info++;
1425 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1426 "%s HP", codec->chip_name);
1427 info->name = spec->stream_name_hp;
1428 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1429 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001430 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001431 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001432 return 0;
1433}
1434
1435static void via_free(struct hda_codec *codec)
1436{
1437 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001438
1439 if (!spec)
1440 return;
1441
Takashi Iwai603c4012008-07-30 15:01:44 +02001442 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001443 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001444 kfree(spec->bind_cap_vol);
1445 kfree(spec->bind_cap_sw);
1446 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001447}
1448
Takashi Iwai64be2852011-06-17 16:51:39 +02001449/* mute/unmute outputs */
1450static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1451 hda_nid_t *pins, bool mute)
1452{
1453 int i;
1454 for (i = 0; i < num_pins; i++)
1455 snd_hda_codec_write(codec, pins[i], 0,
1456 AC_VERB_SET_PIN_WIDGET_CONTROL,
1457 mute ? 0 : PIN_OUT);
1458}
1459
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001460/* mute internal speaker if line-out is plugged */
1461static void via_line_automute(struct hda_codec *codec, int present)
1462{
1463 struct via_spec *spec = codec->spec;
1464
1465 if (!spec->autocfg.speaker_outs)
1466 return;
1467 if (!present)
1468 present = snd_hda_jack_detect(codec,
1469 spec->autocfg.line_out_pins[0]);
1470 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1471 spec->autocfg.speaker_pins,
1472 present);
1473}
1474
Harald Welte69e52a82008-09-09 15:57:32 +08001475/* mute internal speaker if HP is plugged */
1476static void via_hp_automute(struct hda_codec *codec)
1477{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001478 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001479 struct via_spec *spec = codec->spec;
1480
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001481 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001482 int nums;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001483 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001484 if (spec->smart51_enabled)
1485 nums = spec->autocfg.line_outs + spec->smart51_nums;
1486 else
1487 nums = spec->autocfg.line_outs;
1488 toggle_output_mutes(codec, nums,
Takashi Iwai64be2852011-06-17 16:51:39 +02001489 spec->autocfg.line_out_pins,
1490 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001491 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001492 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001493}
1494
Harald Welte69e52a82008-09-09 15:57:32 +08001495static void via_gpio_control(struct hda_codec *codec)
1496{
1497 unsigned int gpio_data;
1498 unsigned int vol_counter;
1499 unsigned int vol;
1500 unsigned int master_vol;
1501
1502 struct via_spec *spec = codec->spec;
1503
1504 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1505 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1506
1507 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1508 0xF84, 0) & 0x3F0000) >> 16;
1509
1510 vol = vol_counter & 0x1F;
1511 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1512 AC_VERB_GET_AMP_GAIN_MUTE,
1513 AC_AMP_GET_INPUT);
1514
1515 if (gpio_data == 0x02) {
1516 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001517 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1518 AC_VERB_SET_PIN_WIDGET_CONTROL,
1519 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001520 if (vol_counter & 0x20) {
1521 /* decrease volume */
1522 if (vol > master_vol)
1523 vol = master_vol;
1524 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1525 0, HDA_AMP_VOLMASK,
1526 master_vol-vol);
1527 } else {
1528 /* increase volume */
1529 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1530 HDA_AMP_VOLMASK,
1531 ((master_vol+vol) > 0x2A) ? 0x2A :
1532 (master_vol+vol));
1533 }
1534 } else if (!(gpio_data & 0x02)) {
1535 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001536 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1537 AC_VERB_SET_PIN_WIDGET_CONTROL,
1538 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001539 }
1540}
1541
1542/* unsolicited event for jack sensing */
1543static void via_unsol_event(struct hda_codec *codec,
1544 unsigned int res)
1545{
1546 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001547
Lydia Wanga34df192009-10-10 19:08:01 +08001548 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001549 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001550
1551 res &= ~VIA_JACK_EVENT;
1552
1553 if (res == VIA_HP_EVENT)
1554 via_hp_automute(codec);
1555 else if (res == VIA_GPIO_EVENT)
1556 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001557 else if (res == VIA_LINE_EVENT)
1558 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001559}
1560
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001561#ifdef SND_HDA_NEEDS_RESUME
1562static int via_suspend(struct hda_codec *codec, pm_message_t state)
1563{
1564 struct via_spec *spec = codec->spec;
1565 vt1708_stop_hp_work(spec);
1566 return 0;
1567}
1568#endif
1569
Takashi Iwaicb53c622007-08-10 17:21:45 +02001570#ifdef CONFIG_SND_HDA_POWER_SAVE
1571static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1572{
1573 struct via_spec *spec = codec->spec;
1574 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1575}
1576#endif
1577
Joseph Chanc577b8a2006-11-29 15:29:40 +01001578/*
1579 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001580
1581static int via_init(struct hda_codec *codec);
1582
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001583static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001584 .build_controls = via_build_controls,
1585 .build_pcms = via_build_pcms,
1586 .init = via_init,
1587 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001588 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001589#ifdef SND_HDA_NEEDS_RESUME
1590 .suspend = via_suspend,
1591#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001592#ifdef CONFIG_SND_HDA_POWER_SAVE
1593 .check_power_status = via_check_power_status,
1594#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001595};
1596
Takashi Iwai4a796162011-06-17 17:53:38 +02001597static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001598{
Takashi Iwai4a796162011-06-17 17:53:38 +02001599 struct via_spec *spec = codec->spec;
1600 int i;
1601
1602 for (i = 0; i < spec->multiout.num_dacs; i++) {
1603 if (spec->multiout.dac_nids[i] == dac)
1604 return false;
1605 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001606 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001607 return false;
1608 return true;
1609}
1610
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001611static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001612 hda_nid_t target_dac, struct nid_path *path,
1613 int depth, int wid_type)
1614{
1615 hda_nid_t conn[8];
1616 int i, nums;
1617
1618 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1619 for (i = 0; i < nums; i++) {
1620 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1621 continue;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001622 if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
1623 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001624 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001625 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001626 return false;
1627 for (i = 0; i < nums; i++) {
1628 unsigned int type;
1629 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1630 if (type == AC_WID_AUD_OUT ||
1631 (wid_type != -1 && type != wid_type))
1632 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001633 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001634 path, depth + 1, AC_WID_AUD_SEL))
1635 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001636 }
1637 return false;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001638
1639 found:
1640 path->path[path->depth] = conn[i];
1641 path->idx[path->depth] = i;
1642 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1643 path->multi[path->depth] = 1;
1644 path->depth++;
1645 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001646}
1647
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001648static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1649 hda_nid_t target_dac, struct nid_path *path)
1650{
1651 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1652 path->path[path->depth] = nid;
1653 path->depth++;
1654 return true;
1655 }
1656 return false;
1657}
1658
Takashi Iwai4a796162011-06-17 17:53:38 +02001659static int via_auto_fill_dac_nids(struct hda_codec *codec)
1660{
1661 struct via_spec *spec = codec->spec;
1662 const struct auto_pin_cfg *cfg = &spec->autocfg;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001663 int i;
1664 hda_nid_t nid;
1665
Joseph Chanc577b8a2006-11-29 15:29:40 +01001666 spec->multiout.dac_nids = spec->private_dac_nids;
Takashi Iwai4a796162011-06-17 17:53:38 +02001667 spec->multiout.num_dacs = cfg->line_outs;
1668 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001669 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001670 if (!nid)
1671 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001672 if (parse_output_path(codec, nid, 0, &spec->out_path[i]))
1673 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001674 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001675 return 0;
1676}
1677
Takashi Iwai4a796162011-06-17 17:53:38 +02001678static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001679 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001680{
Takashi Iwai4a796162011-06-17 17:53:38 +02001681 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001682 char name[32];
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001683 hda_nid_t dac, pin, sel, nid;
1684 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001685
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001686 dac = check_dac ? path->path[0] : 0;
1687 pin = path->path[path->depth - 1];
1688 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001689
Takashi Iwai8df2a312011-06-21 11:48:29 +02001690 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001691 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001692 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001693 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001694 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1695 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001696 else
1697 nid = 0;
1698 if (nid) {
1699 sprintf(name, "%s Playback Volume", pfx);
1700 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001701 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001702 if (err < 0)
1703 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001704 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001705 }
1706
Takashi Iwai8df2a312011-06-21 11:48:29 +02001707 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001708 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001709 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001710 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001711 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1712 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001713 else
1714 nid = 0;
1715 if (nid) {
1716 sprintf(name, "%s Playback Switch", pfx);
1717 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1718 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1719 if (err < 0)
1720 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001721 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001722 }
1723 return 0;
1724}
1725
Takashi Iwaif4a78282011-06-17 18:46:48 +02001726static void mangle_smart51(struct hda_codec *codec)
1727{
1728 struct via_spec *spec = codec->spec;
1729 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001730 struct auto_pin_cfg_item *ins = cfg->inputs;
1731 int i, j, nums, attr;
1732 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001733
Takashi Iwai0f98c242011-06-21 12:51:33 +02001734 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1735 nums = 0;
1736 for (i = 0; i < cfg->num_inputs; i++) {
1737 unsigned int def;
1738 if (ins[i].type > AUTO_PIN_LINE_IN)
1739 continue;
1740 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1741 if (snd_hda_get_input_pin_attr(def) != attr)
1742 continue;
1743 for (j = 0; j < nums; j++)
1744 if (ins[pins[j]].type < ins[i].type) {
1745 memmove(pins + j + 1, pins + j,
1746 (nums - j - 1) * sizeof(int));
1747 break;
1748 }
1749 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001750 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001751 }
1752 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001753 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001754 for (i = 0; i < nums; i++) {
1755 hda_nid_t pin = ins[pins[i]].pin;
1756 spec->smart51_pins[spec->smart51_nums++] = pin;
1757 cfg->line_out_pins[cfg->line_outs++] = pin;
1758 if (cfg->line_outs == 3)
1759 break;
1760 }
1761 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001762 }
1763}
1764
Takashi Iwai4a796162011-06-17 17:53:38 +02001765/* add playback controls from the parsed DAC table */
1766static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1767{
1768 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001769 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001770 static const char * const chname[4] = {
1771 "Front", "Surround", "C/LFE", "Side"
1772 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001773 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001774 int old_line_outs;
1775
1776 /* check smart51 */
1777 old_line_outs = cfg->line_outs;
1778 if (cfg->line_outs == 1)
1779 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001780
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001781 err = via_auto_fill_dac_nids(codec);
1782 if (err < 0)
1783 return err;
1784
Takashi Iwai4a796162011-06-17 17:53:38 +02001785 for (i = 0; i < cfg->line_outs; i++) {
1786 hda_nid_t pin, dac;
1787 pin = cfg->line_out_pins[i];
1788 dac = spec->multiout.dac_nids[i];
1789 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001790 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001791 if (i == HDA_CLFE) {
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001792 err = create_ch_ctls(codec, "Center", 1, true,
1793 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001794 if (err < 0)
1795 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001796 err = create_ch_ctls(codec, "LFE", 2, true,
1797 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001798 if (err < 0)
1799 return err;
1800 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001801 const char *pfx = chname[i];
1802 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1803 cfg->line_outs == 1)
1804 pfx = "Speaker";
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001805 err = create_ch_ctls(codec, pfx, 3, true,
1806 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001807 if (err < 0)
1808 return err;
1809 }
1810 }
1811
Takashi Iwai4a796162011-06-17 17:53:38 +02001812 idx = get_connection_index(codec, spec->aa_mix_nid,
1813 spec->multiout.dac_nids[0]);
Lydia Wangc4394f52011-07-04 16:54:15 +08001814 if (idx < 0 && spec->dac_mixer_idx)
1815 idx = spec->dac_mixer_idx;
Takashi Iwai4a796162011-06-17 17:53:38 +02001816 if (idx >= 0) {
1817 /* add control to mixer */
1818 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1819 "PCM Playback Volume",
1820 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1821 idx, HDA_INPUT));
1822 if (err < 0)
1823 return err;
1824 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1825 "PCM Playback Switch",
1826 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1827 idx, HDA_INPUT));
1828 if (err < 0)
1829 return err;
1830 }
1831
Takashi Iwaif4a78282011-06-17 18:46:48 +02001832 cfg->line_outs = old_line_outs;
1833
Joseph Chanc577b8a2006-11-29 15:29:40 +01001834 return 0;
1835}
1836
Takashi Iwai4a796162011-06-17 17:53:38 +02001837static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001838{
Takashi Iwai4a796162011-06-17 17:53:38 +02001839 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001840 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001841 int err;
1842
1843 if (!pin)
1844 return 0;
1845
Takashi Iwai8df2a312011-06-21 11:48:29 +02001846 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001847 spec->hp_dac_nid = spec->hp_path.path[0];
Takashi Iwai25250502011-06-30 17:24:47 +02001848 else if (spec->multiout.dac_nids[HDA_SIDE] &&
1849 parse_output_path(codec, pin,
1850 spec->multiout.dac_nids[HDA_SIDE],
1851 &spec->hp_path)) {
1852 spec->hp_dac_nid = spec->hp_path.path[0];
1853 spec->hp_indep_shared = true;
1854 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001855
Takashi Iwaiece8d042011-06-19 16:24:21 +02001856 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001857 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001858 !spec->hp_dac_nid)
1859 return 0;
1860
Takashi Iwai25250502011-06-30 17:24:47 +02001861 if (spec->hp_dac_nid && !spec->hp_indep_shared)
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001862 path = &spec->hp_path;
1863 else
1864 path = &spec->hp_dep_path;
1865 err = create_ch_ctls(codec, "Headphone", 3, false, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001866 if (err < 0)
1867 return err;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001868 if (spec->hp_dac_nid) {
1869 spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
1870 spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
1871 }
Harald Welte0aa62ae2008-09-09 15:58:27 +08001872
Joseph Chanc577b8a2006-11-29 15:29:40 +01001873 return 0;
1874}
1875
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001876static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1877{
1878 struct via_spec *spec = codec->spec;
1879 hda_nid_t pin, dac;
1880
1881 pin = spec->autocfg.speaker_pins[0];
1882 if (!spec->autocfg.speaker_outs || !pin)
1883 return 0;
1884
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001885 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1886 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001887 spec->multiout.extra_out_nid[0] = dac;
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001888 return create_ch_ctls(codec, "Speaker", 3, true,
1889 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001890 }
1891 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001892 &spec->speaker_path))
Takashi Iwai09a9ad62011-06-21 15:57:44 +02001893 return create_ch_ctls(codec, "Speaker", 3, false,
1894 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001895
1896 return 0;
1897}
1898
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001899/* look for ADCs */
1900static int via_fill_adcs(struct hda_codec *codec)
1901{
1902 struct via_spec *spec = codec->spec;
1903 hda_nid_t nid = codec->start_nid;
1904 int i;
1905
1906 for (i = 0; i < codec->num_nodes; i++, nid++) {
1907 unsigned int wcaps = get_wcaps(codec, nid);
1908 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1909 continue;
1910 if (wcaps & AC_WCAP_DIGITAL)
1911 continue;
1912 if (!(wcaps & AC_WCAP_CONN_LIST))
1913 continue;
1914 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1915 return -ENOMEM;
1916 spec->adc_nids[spec->num_adc_nids++] = nid;
1917 }
1918 return 0;
1919}
1920
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001921/* input-src control */
1922static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
1923 struct snd_ctl_elem_info *uinfo)
1924{
1925 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1926 struct via_spec *spec = codec->spec;
1927
1928 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
1929 uinfo->count = 1;
1930 uinfo->value.enumerated.items = spec->num_inputs;
1931 if (uinfo->value.enumerated.item >= spec->num_inputs)
1932 uinfo->value.enumerated.item = spec->num_inputs - 1;
1933 strcpy(uinfo->value.enumerated.name,
1934 spec->inputs[uinfo->value.enumerated.item].label);
1935 return 0;
1936}
1937
1938static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
1939 struct snd_ctl_elem_value *ucontrol)
1940{
1941 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1942 struct via_spec *spec = codec->spec;
1943 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1944
1945 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
1946 return 0;
1947}
1948
1949static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
1950 struct snd_ctl_elem_value *ucontrol)
1951{
1952 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1953 struct via_spec *spec = codec->spec;
1954 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
1955 hda_nid_t mux;
1956 int cur;
1957
1958 cur = ucontrol->value.enumerated.item[0];
1959 if (cur < 0 || cur >= spec->num_inputs)
1960 return -EINVAL;
1961 if (spec->cur_mux[idx] == cur)
1962 return 0;
1963 spec->cur_mux[idx] = cur;
1964 if (spec->dyn_adc_switch) {
1965 int adc_idx = spec->inputs[cur].adc_idx;
1966 mux = spec->mux_nids[adc_idx];
1967 via_dyn_adc_pcm_resetup(codec, cur);
1968 } else {
1969 mux = spec->mux_nids[idx];
1970 if (snd_BUG_ON(!mux))
1971 return -EINVAL;
1972 }
1973
1974 if (mux) {
1975 /* switch to D0 beofre change index */
1976 if (snd_hda_codec_read(codec, mux, 0,
1977 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
1978 snd_hda_codec_write(codec, mux, 0,
1979 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
1980 snd_hda_codec_write(codec, mux, 0,
1981 AC_VERB_SET_CONNECT_SEL,
1982 spec->inputs[cur].mux_idx);
1983 }
1984
1985 /* update jack power state */
1986 set_widgets_power_state(codec);
1987 return 0;
1988}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001989
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02001990static const struct snd_kcontrol_new via_input_src_ctl = {
1991 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1992 /* The multiple "Capture Source" controls confuse alsamixer
1993 * So call somewhat different..
1994 */
1995 /* .name = "Capture Source", */
1996 .name = "Input Source",
1997 .info = via_mux_enum_info,
1998 .get = via_mux_enum_get,
1999 .put = via_mux_enum_put,
2000};
2001
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002002static int create_input_src_ctls(struct hda_codec *codec, int count)
2003{
2004 struct via_spec *spec = codec->spec;
2005 struct snd_kcontrol_new *knew;
2006
2007 if (spec->num_inputs <= 1 || !count)
2008 return 0; /* no need for single src */
2009
2010 knew = via_clone_control(spec, &via_input_src_ctl);
2011 if (!knew)
2012 return -ENOMEM;
2013 knew->count = count;
2014 return 0;
2015}
2016
2017/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002018static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2019{
2020 struct hda_amp_list *list;
2021
2022 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2023 return;
2024 list = spec->loopback_list + spec->num_loopbacks;
2025 list->nid = mix;
2026 list->dir = HDA_INPUT;
2027 list->idx = idx;
2028 spec->num_loopbacks++;
2029 spec->loopback.amplist = spec->loopback_list;
2030}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002031
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002032static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002033 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002034{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002035 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002036}
2037
2038/* add the input-route to the given pin */
2039static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002040{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002041 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002042 int c, idx;
2043
2044 spec->inputs[spec->num_inputs].adc_idx = -1;
2045 spec->inputs[spec->num_inputs].pin = pin;
2046 for (c = 0; c < spec->num_adc_nids; c++) {
2047 if (spec->mux_nids[c]) {
2048 idx = get_connection_index(codec, spec->mux_nids[c],
2049 pin);
2050 if (idx < 0)
2051 continue;
2052 spec->inputs[spec->num_inputs].mux_idx = idx;
2053 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002054 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002055 continue;
2056 }
2057 spec->inputs[spec->num_inputs].adc_idx = c;
2058 /* Can primary ADC satisfy all inputs? */
2059 if (!spec->dyn_adc_switch &&
2060 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2061 snd_printd(KERN_INFO
2062 "via: dynamic ADC switching enabled\n");
2063 spec->dyn_adc_switch = 1;
2064 }
2065 return true;
2066 }
2067 return false;
2068}
2069
2070static int get_mux_nids(struct hda_codec *codec);
2071
2072/* parse input-routes; fill ADCs, MUXs and input-src entries */
2073static int parse_analog_inputs(struct hda_codec *codec)
2074{
2075 struct via_spec *spec = codec->spec;
2076 const struct auto_pin_cfg *cfg = &spec->autocfg;
2077 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002078
2079 err = via_fill_adcs(codec);
2080 if (err < 0)
2081 return err;
2082 err = get_mux_nids(codec);
2083 if (err < 0)
2084 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002085
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002086 /* fill all input-routes */
2087 for (i = 0; i < cfg->num_inputs; i++) {
2088 if (add_input_route(codec, cfg->inputs[i].pin))
2089 spec->inputs[spec->num_inputs++].label =
2090 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002091 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002092
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002093 /* check for internal loopback recording */
2094 if (spec->aa_mix_nid &&
2095 add_input_route(codec, spec->aa_mix_nid))
2096 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2097
2098 return 0;
2099}
2100
2101/* create analog-loopback volume/switch controls */
2102static int create_loopback_ctls(struct hda_codec *codec)
2103{
2104 struct via_spec *spec = codec->spec;
2105 const struct auto_pin_cfg *cfg = &spec->autocfg;
2106 const char *prev_label = NULL;
2107 int type_idx = 0;
2108 int i, j, err, idx;
2109
2110 if (!spec->aa_mix_nid)
2111 return 0;
2112
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002113 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002114 hda_nid_t pin = cfg->inputs[i].pin;
2115 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2116
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002117 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002118 type_idx++;
2119 else
2120 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002121 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002122 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2123 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002124 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002125 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002126 if (err < 0)
2127 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002128 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002129 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002130
2131 /* remember the label for smart51 control */
2132 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002133 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002134 spec->smart51_idxs[j] = idx;
2135 spec->smart51_labels[j] = label;
2136 break;
2137 }
2138 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002139 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002140 return 0;
2141}
2142
2143/* create mic-boost controls (if present) */
2144static int create_mic_boost_ctls(struct hda_codec *codec)
2145{
2146 struct via_spec *spec = codec->spec;
2147 const struct auto_pin_cfg *cfg = &spec->autocfg;
2148 int i, err;
2149
2150 for (i = 0; i < cfg->num_inputs; i++) {
2151 hda_nid_t pin = cfg->inputs[i].pin;
2152 unsigned int caps;
2153 const char *label;
2154 char name[32];
2155
2156 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2157 continue;
2158 caps = query_amp_caps(codec, pin, HDA_INPUT);
2159 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2160 continue;
2161 label = hda_get_autocfg_input_label(codec, cfg, i);
2162 snprintf(name, sizeof(name), "%s Boost Volume", label);
2163 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2164 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2165 if (err < 0)
2166 return err;
2167 }
2168 return 0;
2169}
2170
2171/* create capture and input-src controls for multiple streams */
2172static int create_multi_adc_ctls(struct hda_codec *codec)
2173{
2174 struct via_spec *spec = codec->spec;
2175 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002176
2177 /* create capture mixer elements */
2178 for (i = 0; i < spec->num_adc_nids; i++) {
2179 hda_nid_t adc = spec->adc_nids[i];
2180 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2181 "Capture Volume", i,
2182 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2183 HDA_INPUT));
2184 if (err < 0)
2185 return err;
2186 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2187 "Capture Switch", i,
2188 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2189 HDA_INPUT));
2190 if (err < 0)
2191 return err;
2192 }
2193
2194 /* input-source control */
2195 for (i = 0; i < spec->num_adc_nids; i++)
2196 if (!spec->mux_nids[i])
2197 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002198 err = create_input_src_ctls(codec, i);
2199 if (err < 0)
2200 return err;
2201 return 0;
2202}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002203
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002204/* bind capture volume/switch */
2205static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2206 HDA_BIND_VOL("Capture Volume", 0);
2207static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2208 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002209
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002210static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2211 struct hda_ctl_ops *ops)
2212{
2213 struct hda_bind_ctls *ctl;
2214 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002215
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002216 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2217 if (!ctl)
2218 return -ENOMEM;
2219 ctl->ops = ops;
2220 for (i = 0; i < spec->num_adc_nids; i++)
2221 ctl->values[i] =
2222 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2223 *ctl_ret = ctl;
2224 return 0;
2225}
2226
2227/* create capture and input-src controls for dynamic ADC-switch case */
2228static int create_dyn_adc_ctls(struct hda_codec *codec)
2229{
2230 struct via_spec *spec = codec->spec;
2231 struct snd_kcontrol_new *knew;
2232 int err;
2233
2234 /* set up the bind capture ctls */
2235 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2236 if (err < 0)
2237 return err;
2238 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2239 if (err < 0)
2240 return err;
2241
2242 /* create capture mixer elements */
2243 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2244 if (!knew)
2245 return -ENOMEM;
2246 knew->private_value = (long)spec->bind_cap_vol;
2247
2248 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2249 if (!knew)
2250 return -ENOMEM;
2251 knew->private_value = (long)spec->bind_cap_sw;
2252
2253 /* input-source control */
2254 err = create_input_src_ctls(codec, 1);
2255 if (err < 0)
2256 return err;
2257 return 0;
2258}
2259
2260/* parse and create capture-related stuff */
2261static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2262{
2263 struct via_spec *spec = codec->spec;
2264 int err;
2265
2266 err = parse_analog_inputs(codec);
2267 if (err < 0)
2268 return err;
2269 if (spec->dyn_adc_switch)
2270 err = create_dyn_adc_ctls(codec);
2271 else
2272 err = create_multi_adc_ctls(codec);
2273 if (err < 0)
2274 return err;
2275 err = create_loopback_ctls(codec);
2276 if (err < 0)
2277 return err;
2278 err = create_mic_boost_ctls(codec);
2279 if (err < 0)
2280 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002281 return 0;
2282}
2283
Harald Welte76d9b0d2008-09-09 15:50:37 +08002284static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2285{
2286 unsigned int def_conf;
2287 unsigned char seqassoc;
2288
Takashi Iwai2f334f92009-02-20 14:37:42 +01002289 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002290 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2291 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002292 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2293 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2294 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2295 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002296 }
2297
2298 return;
2299}
2300
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002301static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002302 struct snd_ctl_elem_value *ucontrol)
2303{
2304 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2305 struct via_spec *spec = codec->spec;
2306
2307 if (spec->codec_type != VT1708)
2308 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002309 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002310 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002311 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002312 return 0;
2313}
2314
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002315static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002316 struct snd_ctl_elem_value *ucontrol)
2317{
2318 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2319 struct via_spec *spec = codec->spec;
2320 int change;
2321
2322 if (spec->codec_type != VT1708)
2323 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002324 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002325 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002326 == !spec->vt1708_jack_detect;
2327 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002328 mute_aa_path(codec, 1);
2329 notify_aa_path_ctls(codec);
2330 }
2331 return change;
2332}
2333
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002334static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2335 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2336 .name = "Jack Detect",
2337 .count = 1,
2338 .info = snd_ctl_boolean_mono_info,
2339 .get = vt1708_jack_detect_get,
2340 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002341};
2342
Takashi Iwai12daef62011-06-18 17:45:49 +02002343static void fill_dig_outs(struct hda_codec *codec);
2344static void fill_dig_in(struct hda_codec *codec);
2345
2346static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002347{
2348 struct via_spec *spec = codec->spec;
2349 int err;
2350
2351 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2352 if (err < 0)
2353 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002354 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002355 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002356
Takashi Iwai4a796162011-06-17 17:53:38 +02002357 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002358 if (err < 0)
2359 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002360 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002361 if (err < 0)
2362 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002363 err = via_auto_create_speaker_ctls(codec);
2364 if (err < 0)
2365 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002366 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002367 if (err < 0)
2368 return err;
2369
2370 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2371
Takashi Iwai12daef62011-06-18 17:45:49 +02002372 fill_dig_outs(codec);
2373 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002374
Takashi Iwai603c4012008-07-30 15:01:44 +02002375 if (spec->kctls.list)
2376 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002377
Joseph Chanc577b8a2006-11-29 15:29:40 +01002378
Takashi Iwai8df2a312011-06-21 11:48:29 +02002379 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002380 err = via_hp_build(codec);
2381 if (err < 0)
2382 return err;
2383 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002384
Takashi Iwaif4a78282011-06-17 18:46:48 +02002385 err = via_smart51_build(codec);
2386 if (err < 0)
2387 return err;
2388
Takashi Iwai5d417622011-06-20 11:32:27 +02002389 /* assign slave outs */
2390 if (spec->slave_dig_outs[0])
2391 codec->slave_dig_outs = spec->slave_dig_outs;
2392
Joseph Chanc577b8a2006-11-29 15:29:40 +01002393 return 1;
2394}
2395
Takashi Iwai5d417622011-06-20 11:32:27 +02002396static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002397{
Lydia Wang25eaba22009-10-10 19:08:43 +08002398 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002399 if (spec->multiout.dig_out_nid)
2400 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2401 if (spec->slave_dig_outs[0])
2402 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2403}
Lydia Wang25eaba22009-10-10 19:08:43 +08002404
Takashi Iwai5d417622011-06-20 11:32:27 +02002405static void via_auto_init_dig_in(struct hda_codec *codec)
2406{
2407 struct via_spec *spec = codec->spec;
2408 if (!spec->dig_in_nid)
2409 return;
2410 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2411 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2412}
2413
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002414/* initialize the unsolicited events */
2415static void via_auto_init_unsol_event(struct hda_codec *codec)
2416{
2417 struct via_spec *spec = codec->spec;
2418 struct auto_pin_cfg *cfg = &spec->autocfg;
2419 unsigned int ev;
2420 int i;
2421
2422 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2423 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2424 AC_VERB_SET_UNSOLICITED_ENABLE,
2425 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2426
2427 if (cfg->speaker_pins[0])
2428 ev = VIA_LINE_EVENT;
2429 else
2430 ev = 0;
2431 for (i = 0; i < cfg->line_outs; i++) {
2432 if (cfg->line_out_pins[i] &&
2433 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002434 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002435 AC_VERB_SET_UNSOLICITED_ENABLE,
2436 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2437 }
2438
2439 for (i = 0; i < cfg->num_inputs; i++) {
2440 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2441 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2442 AC_VERB_SET_UNSOLICITED_ENABLE,
2443 AC_USRSP_EN | VIA_JACK_EVENT);
2444 }
2445}
2446
Takashi Iwai5d417622011-06-20 11:32:27 +02002447static int via_init(struct hda_codec *codec)
2448{
2449 struct via_spec *spec = codec->spec;
2450 int i;
2451
2452 for (i = 0; i < spec->num_iverbs; i++)
2453 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2454
Joseph Chanc577b8a2006-11-29 15:29:40 +01002455 via_auto_init_multi_out(codec);
2456 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002457 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002458 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002459 via_auto_init_dig_outs(codec);
2460 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002461
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002462 via_auto_init_unsol_event(codec);
2463
2464 via_hp_automute(codec);
2465 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002466
Joseph Chanc577b8a2006-11-29 15:29:40 +01002467 return 0;
2468}
2469
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002470static void vt1708_update_hp_jack_state(struct work_struct *work)
2471{
2472 struct via_spec *spec = container_of(work, struct via_spec,
2473 vt1708_hp_work.work);
2474 if (spec->codec_type != VT1708)
2475 return;
2476 /* if jack state toggled */
2477 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002478 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002479 spec->vt1708_hp_present ^= 1;
2480 via_hp_automute(spec->codec);
2481 }
2482 vt1708_start_hp_work(spec);
2483}
2484
Takashi Iwai337b9d02009-07-07 18:18:59 +02002485static int get_mux_nids(struct hda_codec *codec)
2486{
2487 struct via_spec *spec = codec->spec;
2488 hda_nid_t nid, conn[8];
2489 unsigned int type;
2490 int i, n;
2491
2492 for (i = 0; i < spec->num_adc_nids; i++) {
2493 nid = spec->adc_nids[i];
2494 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002495 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002496 if (type == AC_WID_PIN)
2497 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002498 n = snd_hda_get_connections(codec, nid, conn,
2499 ARRAY_SIZE(conn));
2500 if (n <= 0)
2501 break;
2502 if (n > 1) {
2503 spec->mux_nids[i] = nid;
2504 break;
2505 }
2506 nid = conn[0];
2507 }
2508 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002509 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002510}
2511
Joseph Chanc577b8a2006-11-29 15:29:40 +01002512static int patch_vt1708(struct hda_codec *codec)
2513{
2514 struct via_spec *spec;
2515 int err;
2516
2517 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002518 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002519 if (spec == NULL)
2520 return -ENOMEM;
2521
Takashi Iwai620e2b22011-06-17 17:19:19 +02002522 spec->aa_mix_nid = 0x17;
2523
Takashi Iwai12daef62011-06-18 17:45:49 +02002524 /* Add HP and CD pin config connect bit re-config action */
2525 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2526 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2527
Joseph Chanc577b8a2006-11-29 15:29:40 +01002528 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002529 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002530 if (err < 0) {
2531 via_free(codec);
2532 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002533 }
2534
Takashi Iwai12daef62011-06-18 17:45:49 +02002535 /* add jack detect on/off control */
2536 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2537 return -ENOMEM;
2538
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002539 /* disable 32bit format on VT1708 */
2540 if (codec->vendor_id == 0x11061708)
2541 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002542
Lydia Wange322a362011-06-29 13:52:02 +08002543 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2544
Joseph Chanc577b8a2006-11-29 15:29:40 +01002545 codec->patch_ops = via_patch_ops;
2546
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002547 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002548 return 0;
2549}
2550
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002551static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002552{
2553 struct via_spec *spec;
2554 int err;
2555
2556 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002557 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002558 if (spec == NULL)
2559 return -ENOMEM;
2560
Takashi Iwai620e2b22011-06-17 17:19:19 +02002561 spec->aa_mix_nid = 0x18;
2562
Takashi Iwai12daef62011-06-18 17:45:49 +02002563 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002564 if (err < 0) {
2565 via_free(codec);
2566 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002567 }
2568
Joseph Chanc577b8a2006-11-29 15:29:40 +01002569 codec->patch_ops = via_patch_ops;
2570
Josepch Chanf7278fd2007-12-13 16:40:40 +01002571 return 0;
2572}
2573
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002574static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2575{
2576 struct via_spec *spec = codec->spec;
2577 int imux_is_smixer;
2578 unsigned int parm;
2579 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002580 if ((spec->codec_type != VT1708B_4CH) &&
2581 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002582 is_8ch = 1;
2583
2584 /* SW0 (17h) = stereo mixer */
2585 imux_is_smixer =
2586 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2587 == ((spec->codec_type == VT1708S) ? 5 : 0));
2588 /* inputs */
2589 /* PW 1/2/5 (1ah/1bh/1eh) */
2590 parm = AC_PWRST_D3;
2591 set_pin_power_state(codec, 0x1a, &parm);
2592 set_pin_power_state(codec, 0x1b, &parm);
2593 set_pin_power_state(codec, 0x1e, &parm);
2594 if (imux_is_smixer)
2595 parm = AC_PWRST_D0;
2596 /* SW0 (17h), AIW 0/1 (13h/14h) */
2597 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2598 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2599 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2600
2601 /* outputs */
2602 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2603 parm = AC_PWRST_D3;
2604 set_pin_power_state(codec, 0x19, &parm);
2605 if (spec->smart51_enabled)
2606 set_pin_power_state(codec, 0x1b, &parm);
2607 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2608 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2609
2610 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2611 if (is_8ch) {
2612 parm = AC_PWRST_D3;
2613 set_pin_power_state(codec, 0x22, &parm);
2614 if (spec->smart51_enabled)
2615 set_pin_power_state(codec, 0x1a, &parm);
2616 snd_hda_codec_write(codec, 0x26, 0,
2617 AC_VERB_SET_POWER_STATE, parm);
2618 snd_hda_codec_write(codec, 0x24, 0,
2619 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002620 } else if (codec->vendor_id == 0x11064397) {
2621 /* PW7(23h), SW2(27h), AOW2(25h) */
2622 parm = AC_PWRST_D3;
2623 set_pin_power_state(codec, 0x23, &parm);
2624 if (spec->smart51_enabled)
2625 set_pin_power_state(codec, 0x1a, &parm);
2626 snd_hda_codec_write(codec, 0x27, 0,
2627 AC_VERB_SET_POWER_STATE, parm);
2628 snd_hda_codec_write(codec, 0x25, 0,
2629 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002630 }
2631
2632 /* PW 3/4/7 (1ch/1dh/23h) */
2633 parm = AC_PWRST_D3;
2634 /* force to D0 for internal Speaker */
2635 set_pin_power_state(codec, 0x1c, &parm);
2636 set_pin_power_state(codec, 0x1d, &parm);
2637 if (is_8ch)
2638 set_pin_power_state(codec, 0x23, &parm);
2639
2640 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2641 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2642 imux_is_smixer ? AC_PWRST_D0 : parm);
2643 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2644 if (is_8ch) {
2645 snd_hda_codec_write(codec, 0x25, 0,
2646 AC_VERB_SET_POWER_STATE, parm);
2647 snd_hda_codec_write(codec, 0x27, 0,
2648 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002649 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2650 snd_hda_codec_write(codec, 0x25, 0,
2651 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002652}
2653
Lydia Wang518bf3b2009-10-10 19:07:29 +08002654static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002655static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002656{
2657 struct via_spec *spec;
2658 int err;
2659
Lydia Wang518bf3b2009-10-10 19:07:29 +08002660 if (get_codec_type(codec) == VT1708BCE)
2661 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002662
Josepch Chanf7278fd2007-12-13 16:40:40 +01002663 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002664 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002665 if (spec == NULL)
2666 return -ENOMEM;
2667
Takashi Iwai620e2b22011-06-17 17:19:19 +02002668 spec->aa_mix_nid = 0x16;
2669
Josepch Chanf7278fd2007-12-13 16:40:40 +01002670 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002671 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002672 if (err < 0) {
2673 via_free(codec);
2674 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002675 }
2676
Josepch Chanf7278fd2007-12-13 16:40:40 +01002677 codec->patch_ops = via_patch_ops;
2678
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002679 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2680
Josepch Chanf7278fd2007-12-13 16:40:40 +01002681 return 0;
2682}
2683
Harald Welted949cac2008-09-09 15:56:01 +08002684/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002685static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002686 /* Enable Mic Boost Volume backdoor */
2687 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002688 /* don't bybass mixer */
2689 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002690 { }
2691};
2692
Takashi Iwai9da29272009-05-07 16:31:14 +02002693/* fill out digital output widgets; one for master and one for slave outputs */
2694static void fill_dig_outs(struct hda_codec *codec)
2695{
2696 struct via_spec *spec = codec->spec;
2697 int i;
2698
2699 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2700 hda_nid_t nid;
2701 int conn;
2702
2703 nid = spec->autocfg.dig_out_pins[i];
2704 if (!nid)
2705 continue;
2706 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2707 if (conn < 1)
2708 continue;
2709 if (!spec->multiout.dig_out_nid)
2710 spec->multiout.dig_out_nid = nid;
2711 else {
2712 spec->slave_dig_outs[0] = nid;
2713 break; /* at most two dig outs */
2714 }
2715 }
2716}
2717
Takashi Iwai12daef62011-06-18 17:45:49 +02002718static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002719{
2720 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002721 hda_nid_t dig_nid;
2722 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002723
Takashi Iwai12daef62011-06-18 17:45:49 +02002724 if (!spec->autocfg.dig_in_pin)
2725 return;
Harald Welted949cac2008-09-09 15:56:01 +08002726
Takashi Iwai12daef62011-06-18 17:45:49 +02002727 dig_nid = codec->start_nid;
2728 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2729 unsigned int wcaps = get_wcaps(codec, dig_nid);
2730 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2731 continue;
2732 if (!(wcaps & AC_WCAP_DIGITAL))
2733 continue;
2734 if (!(wcaps & AC_WCAP_CONN_LIST))
2735 continue;
2736 err = get_connection_index(codec, dig_nid,
2737 spec->autocfg.dig_in_pin);
2738 if (err >= 0) {
2739 spec->dig_in_nid = dig_nid;
2740 break;
2741 }
2742 }
Harald Welted949cac2008-09-09 15:56:01 +08002743}
2744
Lydia Wang6369bcf2009-10-10 19:08:31 +08002745static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2746 int offset, int num_steps, int step_size)
2747{
2748 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2749 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2750 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2751 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2752 (0 << AC_AMPCAP_MUTE_SHIFT));
2753}
2754
Harald Welted949cac2008-09-09 15:56:01 +08002755static int patch_vt1708S(struct hda_codec *codec)
2756{
2757 struct via_spec *spec;
2758 int err;
2759
2760 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002761 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002762 if (spec == NULL)
2763 return -ENOMEM;
2764
Takashi Iwai620e2b22011-06-17 17:19:19 +02002765 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002766 override_mic_boost(codec, 0x1a, 0, 3, 40);
2767 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002768
Harald Welted949cac2008-09-09 15:56:01 +08002769 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002770 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002771 if (err < 0) {
2772 via_free(codec);
2773 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002774 }
2775
Takashi Iwai096a8852011-06-20 12:09:02 +02002776 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002777
Harald Welted949cac2008-09-09 15:56:01 +08002778 codec->patch_ops = via_patch_ops;
2779
Lydia Wang518bf3b2009-10-10 19:07:29 +08002780 /* correct names for VT1708BCE */
2781 if (get_codec_type(codec) == VT1708BCE) {
2782 kfree(codec->chip_name);
2783 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2784 snprintf(codec->bus->card->mixername,
2785 sizeof(codec->bus->card->mixername),
2786 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002787 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002788 /* correct names for VT1705 */
2789 if (codec->vendor_id == 0x11064397) {
2790 kfree(codec->chip_name);
2791 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2792 snprintf(codec->bus->card->mixername,
2793 sizeof(codec->bus->card->mixername),
2794 "%s %s", codec->vendor_name, codec->chip_name);
2795 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002796 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002797 return 0;
2798}
2799
2800/* Patch for VT1702 */
2801
Takashi Iwai096a8852011-06-20 12:09:02 +02002802static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002803 /* mixer enable */
2804 {0x1, 0xF88, 0x3},
2805 /* GPIO 0~2 */
2806 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002807 { }
2808};
2809
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002810static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2811{
2812 int imux_is_smixer =
2813 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2814 unsigned int parm;
2815 /* inputs */
2816 /* PW 1/2/5 (14h/15h/18h) */
2817 parm = AC_PWRST_D3;
2818 set_pin_power_state(codec, 0x14, &parm);
2819 set_pin_power_state(codec, 0x15, &parm);
2820 set_pin_power_state(codec, 0x18, &parm);
2821 if (imux_is_smixer)
2822 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2823 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2824 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2825 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2826 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2827 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2828
2829 /* outputs */
2830 /* PW 3/4 (16h/17h) */
2831 parm = AC_PWRST_D3;
2832 set_pin_power_state(codec, 0x17, &parm);
2833 set_pin_power_state(codec, 0x16, &parm);
2834 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2835 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2836 imux_is_smixer ? AC_PWRST_D0 : parm);
2837 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2838 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2839}
2840
Harald Welted949cac2008-09-09 15:56:01 +08002841static int patch_vt1702(struct hda_codec *codec)
2842{
2843 struct via_spec *spec;
2844 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002845
2846 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002847 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002848 if (spec == NULL)
2849 return -ENOMEM;
2850
Takashi Iwai620e2b22011-06-17 17:19:19 +02002851 spec->aa_mix_nid = 0x1a;
2852
Takashi Iwai12daef62011-06-18 17:45:49 +02002853 /* limit AA path volume to 0 dB */
2854 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2855 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2856 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2857 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2858 (1 << AC_AMPCAP_MUTE_SHIFT));
2859
Harald Welted949cac2008-09-09 15:56:01 +08002860 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002861 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002862 if (err < 0) {
2863 via_free(codec);
2864 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002865 }
2866
Takashi Iwai096a8852011-06-20 12:09:02 +02002867 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002868
Harald Welted949cac2008-09-09 15:56:01 +08002869 codec->patch_ops = via_patch_ops;
2870
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002871 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002872 return 0;
2873}
2874
Lydia Wangeb7188c2009-10-10 19:08:34 +08002875/* Patch for VT1718S */
2876
Takashi Iwai096a8852011-06-20 12:09:02 +02002877static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002878 /* Enable MW0 adjust Gain 5 */
2879 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002880 /* Enable Boost Volume backdoor */
2881 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002882
Lydia Wangeb7188c2009-10-10 19:08:34 +08002883 { }
2884};
2885
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002886static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2887{
2888 struct via_spec *spec = codec->spec;
2889 int imux_is_smixer;
2890 unsigned int parm;
2891 /* MUX6 (1eh) = stereo mixer */
2892 imux_is_smixer =
2893 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2894 /* inputs */
2895 /* PW 5/6/7 (29h/2ah/2bh) */
2896 parm = AC_PWRST_D3;
2897 set_pin_power_state(codec, 0x29, &parm);
2898 set_pin_power_state(codec, 0x2a, &parm);
2899 set_pin_power_state(codec, 0x2b, &parm);
2900 if (imux_is_smixer)
2901 parm = AC_PWRST_D0;
2902 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2903 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2904 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2905 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2906 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2907
2908 /* outputs */
2909 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2910 parm = AC_PWRST_D3;
2911 set_pin_power_state(codec, 0x27, &parm);
2912 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2913 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2914
2915 /* PW2 (26h), AOW2 (ah) */
2916 parm = AC_PWRST_D3;
2917 set_pin_power_state(codec, 0x26, &parm);
2918 if (spec->smart51_enabled)
2919 set_pin_power_state(codec, 0x2b, &parm);
2920 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2921
2922 /* PW0 (24h), AOW0 (8h) */
2923 parm = AC_PWRST_D3;
2924 set_pin_power_state(codec, 0x24, &parm);
2925 if (!spec->hp_independent_mode) /* check for redirected HP */
2926 set_pin_power_state(codec, 0x28, &parm);
2927 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
2928 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
2929 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
2930 imux_is_smixer ? AC_PWRST_D0 : parm);
2931
2932 /* PW1 (25h), AOW1 (9h) */
2933 parm = AC_PWRST_D3;
2934 set_pin_power_state(codec, 0x25, &parm);
2935 if (spec->smart51_enabled)
2936 set_pin_power_state(codec, 0x2a, &parm);
2937 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
2938
2939 if (spec->hp_independent_mode) {
2940 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
2941 parm = AC_PWRST_D3;
2942 set_pin_power_state(codec, 0x28, &parm);
2943 snd_hda_codec_write(codec, 0x1b, 0,
2944 AC_VERB_SET_POWER_STATE, parm);
2945 snd_hda_codec_write(codec, 0x34, 0,
2946 AC_VERB_SET_POWER_STATE, parm);
2947 snd_hda_codec_write(codec, 0xc, 0,
2948 AC_VERB_SET_POWER_STATE, parm);
2949 }
2950}
2951
Lydia Wangeb7188c2009-10-10 19:08:34 +08002952static int patch_vt1718S(struct hda_codec *codec)
2953{
2954 struct via_spec *spec;
2955 int err;
2956
2957 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002958 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002959 if (spec == NULL)
2960 return -ENOMEM;
2961
Takashi Iwai620e2b22011-06-17 17:19:19 +02002962 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002963 override_mic_boost(codec, 0x2b, 0, 3, 40);
2964 override_mic_boost(codec, 0x29, 0, 3, 40);
Lydia Wangc4394f52011-07-04 16:54:15 +08002965 spec->dac_mixer_idx = 5;
Takashi Iwai620e2b22011-06-17 17:19:19 +02002966
Lydia Wangeb7188c2009-10-10 19:08:34 +08002967 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002968 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08002969 if (err < 0) {
2970 via_free(codec);
2971 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002972 }
2973
Takashi Iwai096a8852011-06-20 12:09:02 +02002974 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08002975
Lydia Wangeb7188c2009-10-10 19:08:34 +08002976 codec->patch_ops = via_patch_ops;
2977
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002978 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
2979
Lydia Wangeb7188c2009-10-10 19:08:34 +08002980 return 0;
2981}
Lydia Wangf3db4232009-10-10 19:08:41 +08002982
2983/* Patch for VT1716S */
2984
2985static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
2986 struct snd_ctl_elem_info *uinfo)
2987{
2988 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2989 uinfo->count = 1;
2990 uinfo->value.integer.min = 0;
2991 uinfo->value.integer.max = 1;
2992 return 0;
2993}
2994
2995static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
2996 struct snd_ctl_elem_value *ucontrol)
2997{
2998 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2999 int index = 0;
3000
3001 index = snd_hda_codec_read(codec, 0x26, 0,
3002 AC_VERB_GET_CONNECT_SEL, 0);
3003 if (index != -1)
3004 *ucontrol->value.integer.value = index;
3005
3006 return 0;
3007}
3008
3009static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3010 struct snd_ctl_elem_value *ucontrol)
3011{
3012 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3013 struct via_spec *spec = codec->spec;
3014 int index = *ucontrol->value.integer.value;
3015
3016 snd_hda_codec_write(codec, 0x26, 0,
3017 AC_VERB_SET_CONNECT_SEL, index);
3018 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003019 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003020 return 1;
3021}
3022
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003023static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003024 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3025 {
3026 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3027 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003028 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003029 .count = 1,
3030 .info = vt1716s_dmic_info,
3031 .get = vt1716s_dmic_get,
3032 .put = vt1716s_dmic_put,
3033 },
3034 {} /* end */
3035};
3036
3037
3038/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003039static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003040 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3041 { } /* end */
3042};
3043
Takashi Iwai096a8852011-06-20 12:09:02 +02003044static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003045 /* Enable Boost Volume backdoor */
3046 {0x1, 0xf8a, 0x80},
3047 /* don't bybass mixer */
3048 {0x1, 0xf88, 0xc0},
3049 /* Enable mono output */
3050 {0x1, 0xf90, 0x08},
3051 { }
3052};
3053
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003054static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3055{
3056 struct via_spec *spec = codec->spec;
3057 int imux_is_smixer;
3058 unsigned int parm;
3059 unsigned int mono_out, present;
3060 /* SW0 (17h) = stereo mixer */
3061 imux_is_smixer =
3062 (snd_hda_codec_read(codec, 0x17, 0,
3063 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3064 /* inputs */
3065 /* PW 1/2/5 (1ah/1bh/1eh) */
3066 parm = AC_PWRST_D3;
3067 set_pin_power_state(codec, 0x1a, &parm);
3068 set_pin_power_state(codec, 0x1b, &parm);
3069 set_pin_power_state(codec, 0x1e, &parm);
3070 if (imux_is_smixer)
3071 parm = AC_PWRST_D0;
3072 /* SW0 (17h), AIW0(13h) */
3073 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3074 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3075
3076 parm = AC_PWRST_D3;
3077 set_pin_power_state(codec, 0x1e, &parm);
3078 /* PW11 (22h) */
3079 if (spec->dmic_enabled)
3080 set_pin_power_state(codec, 0x22, &parm);
3081 else
3082 snd_hda_codec_write(codec, 0x22, 0,
3083 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3084
3085 /* SW2(26h), AIW1(14h) */
3086 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3087 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3088
3089 /* outputs */
3090 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3091 parm = AC_PWRST_D3;
3092 set_pin_power_state(codec, 0x19, &parm);
3093 /* Smart 5.1 PW2(1bh) */
3094 if (spec->smart51_enabled)
3095 set_pin_power_state(codec, 0x1b, &parm);
3096 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3097 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3098
3099 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3100 parm = AC_PWRST_D3;
3101 set_pin_power_state(codec, 0x23, &parm);
3102 /* Smart 5.1 PW1(1ah) */
3103 if (spec->smart51_enabled)
3104 set_pin_power_state(codec, 0x1a, &parm);
3105 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3106
3107 /* Smart 5.1 PW5(1eh) */
3108 if (spec->smart51_enabled)
3109 set_pin_power_state(codec, 0x1e, &parm);
3110 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3111
3112 /* Mono out */
3113 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3114 present = snd_hda_jack_detect(codec, 0x1c);
3115
3116 if (present)
3117 mono_out = 0;
3118 else {
3119 present = snd_hda_jack_detect(codec, 0x1d);
3120 if (!spec->hp_independent_mode && present)
3121 mono_out = 0;
3122 else
3123 mono_out = 1;
3124 }
3125 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3126 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3127 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3128 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3129
3130 /* PW 3/4 (1ch/1dh) */
3131 parm = AC_PWRST_D3;
3132 set_pin_power_state(codec, 0x1c, &parm);
3133 set_pin_power_state(codec, 0x1d, &parm);
3134 /* HP Independent Mode, power on AOW3 */
3135 if (spec->hp_independent_mode)
3136 snd_hda_codec_write(codec, 0x25, 0,
3137 AC_VERB_SET_POWER_STATE, parm);
3138
3139 /* force to D0 for internal Speaker */
3140 /* MW0 (16h), AOW0 (10h) */
3141 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3142 imux_is_smixer ? AC_PWRST_D0 : parm);
3143 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3144 mono_out ? AC_PWRST_D0 : parm);
3145}
3146
Lydia Wangf3db4232009-10-10 19:08:41 +08003147static int patch_vt1716S(struct hda_codec *codec)
3148{
3149 struct via_spec *spec;
3150 int err;
3151
3152 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003153 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003154 if (spec == NULL)
3155 return -ENOMEM;
3156
Takashi Iwai620e2b22011-06-17 17:19:19 +02003157 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003158 override_mic_boost(codec, 0x1a, 0, 3, 40);
3159 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003160
Lydia Wangf3db4232009-10-10 19:08:41 +08003161 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003162 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003163 if (err < 0) {
3164 via_free(codec);
3165 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003166 }
3167
Takashi Iwai096a8852011-06-20 12:09:02 +02003168 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003169
Lydia Wangf3db4232009-10-10 19:08:41 +08003170 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3171 spec->num_mixers++;
3172
3173 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3174
3175 codec->patch_ops = via_patch_ops;
3176
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003177 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003178 return 0;
3179}
Lydia Wang25eaba22009-10-10 19:08:43 +08003180
3181/* for vt2002P */
3182
Takashi Iwai096a8852011-06-20 12:09:02 +02003183static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003184 /* Class-D speaker related verbs */
3185 {0x1, 0xfe0, 0x4},
3186 {0x1, 0xfe9, 0x80},
3187 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003188 /* Enable Boost Volume backdoor */
3189 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003190 /* Enable AOW0 to MW9 */
3191 {0x1, 0xfb8, 0x88},
3192 { }
3193};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003194
Takashi Iwai096a8852011-06-20 12:09:02 +02003195static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003196 /* Enable Boost Volume backdoor */
3197 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003198 /* Enable AOW0 to MW9 */
3199 {0x1, 0xfb8, 0x88},
3200 { }
3201};
Lydia Wang25eaba22009-10-10 19:08:43 +08003202
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003203static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3204{
3205 struct via_spec *spec = codec->spec;
3206 int imux_is_smixer;
3207 unsigned int parm;
3208 unsigned int present;
3209 /* MUX9 (1eh) = stereo mixer */
3210 imux_is_smixer =
3211 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3212 /* inputs */
3213 /* PW 5/6/7 (29h/2ah/2bh) */
3214 parm = AC_PWRST_D3;
3215 set_pin_power_state(codec, 0x29, &parm);
3216 set_pin_power_state(codec, 0x2a, &parm);
3217 set_pin_power_state(codec, 0x2b, &parm);
3218 parm = AC_PWRST_D0;
3219 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3220 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3221 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3222 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3223 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3224
3225 /* outputs */
3226 /* AOW0 (8h)*/
3227 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3228
Lydia Wang118909562011-03-23 17:57:34 +08003229 if (spec->codec_type == VT1802) {
3230 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3231 parm = AC_PWRST_D3;
3232 set_pin_power_state(codec, 0x28, &parm);
3233 snd_hda_codec_write(codec, 0x18, 0,
3234 AC_VERB_SET_POWER_STATE, parm);
3235 snd_hda_codec_write(codec, 0x38, 0,
3236 AC_VERB_SET_POWER_STATE, parm);
3237 } else {
3238 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3239 parm = AC_PWRST_D3;
3240 set_pin_power_state(codec, 0x26, &parm);
3241 snd_hda_codec_write(codec, 0x1c, 0,
3242 AC_VERB_SET_POWER_STATE, parm);
3243 snd_hda_codec_write(codec, 0x37, 0,
3244 AC_VERB_SET_POWER_STATE, parm);
3245 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003246
Lydia Wang118909562011-03-23 17:57:34 +08003247 if (spec->codec_type == VT1802) {
3248 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3249 parm = AC_PWRST_D3;
3250 set_pin_power_state(codec, 0x25, &parm);
3251 snd_hda_codec_write(codec, 0x15, 0,
3252 AC_VERB_SET_POWER_STATE, parm);
3253 snd_hda_codec_write(codec, 0x35, 0,
3254 AC_VERB_SET_POWER_STATE, parm);
3255 } else {
3256 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3257 parm = AC_PWRST_D3;
3258 set_pin_power_state(codec, 0x25, &parm);
3259 snd_hda_codec_write(codec, 0x19, 0,
3260 AC_VERB_SET_POWER_STATE, parm);
3261 snd_hda_codec_write(codec, 0x35, 0,
3262 AC_VERB_SET_POWER_STATE, parm);
3263 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003264
3265 if (spec->hp_independent_mode)
3266 snd_hda_codec_write(codec, 0x9, 0,
3267 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3268
3269 /* Class-D */
3270 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3271 present = snd_hda_jack_detect(codec, 0x25);
3272
3273 parm = AC_PWRST_D3;
3274 set_pin_power_state(codec, 0x24, &parm);
3275 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003276 if (spec->codec_type == VT1802)
3277 snd_hda_codec_write(codec, 0x14, 0,
3278 AC_VERB_SET_POWER_STATE, parm);
3279 else
3280 snd_hda_codec_write(codec, 0x18, 0,
3281 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003282 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3283
3284 /* Mono Out */
3285 present = snd_hda_jack_detect(codec, 0x26);
3286
3287 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003288 if (spec->codec_type == VT1802) {
3289 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3290 snd_hda_codec_write(codec, 0x33, 0,
3291 AC_VERB_SET_POWER_STATE, parm);
3292 snd_hda_codec_write(codec, 0x1c, 0,
3293 AC_VERB_SET_POWER_STATE, parm);
3294 snd_hda_codec_write(codec, 0x3c, 0,
3295 AC_VERB_SET_POWER_STATE, parm);
3296 } else {
3297 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3298 snd_hda_codec_write(codec, 0x31, 0,
3299 AC_VERB_SET_POWER_STATE, parm);
3300 snd_hda_codec_write(codec, 0x17, 0,
3301 AC_VERB_SET_POWER_STATE, parm);
3302 snd_hda_codec_write(codec, 0x3b, 0,
3303 AC_VERB_SET_POWER_STATE, parm);
3304 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003305 /* MW9 (21h) */
3306 if (imux_is_smixer || !is_aa_path_mute(codec))
3307 snd_hda_codec_write(codec, 0x21, 0,
3308 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3309 else
3310 snd_hda_codec_write(codec, 0x21, 0,
3311 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3312}
Lydia Wang25eaba22009-10-10 19:08:43 +08003313
3314/* patch for vt2002P */
3315static int patch_vt2002P(struct hda_codec *codec)
3316{
3317 struct via_spec *spec;
3318 int err;
3319
3320 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003321 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003322 if (spec == NULL)
3323 return -ENOMEM;
3324
Takashi Iwai620e2b22011-06-17 17:19:19 +02003325 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003326 override_mic_boost(codec, 0x2b, 0, 3, 40);
3327 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003328
Lydia Wang25eaba22009-10-10 19:08:43 +08003329 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003330 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003331 if (err < 0) {
3332 via_free(codec);
3333 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003334 }
3335
Lydia Wang118909562011-03-23 17:57:34 +08003336 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003337 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003338 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003339 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003340
Lydia Wang25eaba22009-10-10 19:08:43 +08003341 codec->patch_ops = via_patch_ops;
3342
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003343 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003344 return 0;
3345}
Lydia Wangab6734e2009-10-10 19:08:46 +08003346
3347/* for vt1812 */
3348
Takashi Iwai096a8852011-06-20 12:09:02 +02003349static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003350 /* Enable Boost Volume backdoor */
3351 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003352 /* Enable AOW0 to MW9 */
3353 {0x1, 0xfb8, 0xa8},
3354 { }
3355};
3356
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003357static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3358{
3359 struct via_spec *spec = codec->spec;
3360 int imux_is_smixer =
3361 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3362 unsigned int parm;
3363 unsigned int present;
3364 /* MUX10 (1eh) = stereo mixer */
3365 imux_is_smixer =
3366 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3367 /* inputs */
3368 /* PW 5/6/7 (29h/2ah/2bh) */
3369 parm = AC_PWRST_D3;
3370 set_pin_power_state(codec, 0x29, &parm);
3371 set_pin_power_state(codec, 0x2a, &parm);
3372 set_pin_power_state(codec, 0x2b, &parm);
3373 parm = AC_PWRST_D0;
3374 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3375 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3376 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3377 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3378 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3379
3380 /* outputs */
3381 /* AOW0 (8h)*/
3382 snd_hda_codec_write(codec, 0x8, 0,
3383 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3384
3385 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3386 parm = AC_PWRST_D3;
3387 set_pin_power_state(codec, 0x28, &parm);
3388 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3389 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3390
3391 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3392 parm = AC_PWRST_D3;
3393 set_pin_power_state(codec, 0x25, &parm);
3394 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3395 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3396 if (spec->hp_independent_mode)
3397 snd_hda_codec_write(codec, 0x9, 0,
3398 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3399
3400 /* Internal Speaker */
3401 /* PW0 (24h), MW0(14h), MUX0(34h) */
3402 present = snd_hda_jack_detect(codec, 0x25);
3403
3404 parm = AC_PWRST_D3;
3405 set_pin_power_state(codec, 0x24, &parm);
3406 if (present) {
3407 snd_hda_codec_write(codec, 0x14, 0,
3408 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3409 snd_hda_codec_write(codec, 0x34, 0,
3410 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3411 } else {
3412 snd_hda_codec_write(codec, 0x14, 0,
3413 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3414 snd_hda_codec_write(codec, 0x34, 0,
3415 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3416 }
3417
3418
3419 /* Mono Out */
3420 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3421 present = snd_hda_jack_detect(codec, 0x28);
3422
3423 parm = AC_PWRST_D3;
3424 set_pin_power_state(codec, 0x31, &parm);
3425 if (present) {
3426 snd_hda_codec_write(codec, 0x1c, 0,
3427 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3428 snd_hda_codec_write(codec, 0x3c, 0,
3429 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3430 snd_hda_codec_write(codec, 0x3e, 0,
3431 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3432 } else {
3433 snd_hda_codec_write(codec, 0x1c, 0,
3434 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3435 snd_hda_codec_write(codec, 0x3c, 0,
3436 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3437 snd_hda_codec_write(codec, 0x3e, 0,
3438 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3439 }
3440
3441 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3442 parm = AC_PWRST_D3;
3443 set_pin_power_state(codec, 0x33, &parm);
3444 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3445 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3446
3447}
Lydia Wangab6734e2009-10-10 19:08:46 +08003448
3449/* patch for vt1812 */
3450static int patch_vt1812(struct hda_codec *codec)
3451{
3452 struct via_spec *spec;
3453 int err;
3454
3455 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003456 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003457 if (spec == NULL)
3458 return -ENOMEM;
3459
Takashi Iwai620e2b22011-06-17 17:19:19 +02003460 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003461 override_mic_boost(codec, 0x2b, 0, 3, 40);
3462 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003463
Lydia Wangab6734e2009-10-10 19:08:46 +08003464 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003465 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003466 if (err < 0) {
3467 via_free(codec);
3468 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003469 }
3470
Takashi Iwai096a8852011-06-20 12:09:02 +02003471 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003472
Lydia Wangab6734e2009-10-10 19:08:46 +08003473 codec->patch_ops = via_patch_ops;
3474
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003475 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003476 return 0;
3477}
3478
Joseph Chanc577b8a2006-11-29 15:29:40 +01003479/*
3480 * patch entries
3481 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003482static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003483 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3484 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3485 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3486 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3487 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003488 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003489 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003490 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003491 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003492 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003493 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003494 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003495 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003496 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003497 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003498 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003499 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003500 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003501 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003502 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003503 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003504 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003505 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003506 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003507 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003508 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003509 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003510 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003511 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003512 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003513 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003514 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003515 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003516 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003517 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003518 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003519 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003520 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003521 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003522 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003523 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003524 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003525 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003526 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003527 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003528 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003529 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003530 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003531 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003532 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003533 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003534 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003535 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003536 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003537 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003538 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003539 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003540 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003541 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003542 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003543 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003544 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003545 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003546 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003547 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003548 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003549 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003550 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003551 { .id = 0x11060428, .name = "VT1718S",
3552 .patch = patch_vt1718S},
3553 { .id = 0x11064428, .name = "VT1718S",
3554 .patch = patch_vt1718S},
Lydia Wangbb3c6bf2009-10-10 19:08:39 +08003555 { .id = 0x11060441, .name = "VT2020",
3556 .patch = patch_vt1718S},
3557 { .id = 0x11064441, .name = "VT1828S",
3558 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003559 { .id = 0x11060433, .name = "VT1716S",
3560 .patch = patch_vt1716S},
3561 { .id = 0x1106a721, .name = "VT1716S",
3562 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003563 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3564 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003565 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003566 { .id = 0x11060440, .name = "VT1818S",
3567 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003568 { .id = 0x11060446, .name = "VT1802",
3569 .patch = patch_vt2002P},
3570 { .id = 0x11068446, .name = "VT1802",
3571 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003572 {} /* terminator */
3573};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003574
3575MODULE_ALIAS("snd-hda-codec-id:1106*");
3576
3577static struct hda_codec_preset_list via_list = {
3578 .preset = snd_hda_preset_via,
3579 .owner = THIS_MODULE,
3580};
3581
3582MODULE_LICENSE("GPL");
3583MODULE_DESCRIPTION("VIA HD-audio codec");
3584
3585static int __init patch_via_init(void)
3586{
3587 return snd_hda_add_codec_preset(&via_list);
3588}
3589
3590static void __exit patch_via_exit(void)
3591{
3592 snd_hda_delete_codec_preset(&via_list);
3593}
3594
3595module_init(patch_via_init)
3596module_exit(patch_via_exit)