blob: 77df2bedfb81f6f2f7c2d84d67b41ffc407d2165 [file] [log] [blame]
Joseph Chanc577b8a2006-11-29 15:29:40 +01001/*
2 * Universal Interface for Intel High Definition Audio Codec
3 *
Lydia Wang8e865972009-10-10 19:08:52 +08004 * HD audio interface patch for VIA VT17xx/VT18xx/VT20xx codec
Joseph Chanc577b8a2006-11-29 15:29:40 +01005 *
Lydia Wang8e865972009-10-10 19:08:52 +08006 * (C) 2006-2009 VIA Technology, Inc.
7 * (C) 2006-2008 Takashi Iwai <tiwai@suse.de>
Joseph Chanc577b8a2006-11-29 15:29:40 +01008 *
9 * This driver is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This driver is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24/* * * * * * * * * * * * * * Release History * * * * * * * * * * * * * * * * */
Lydia Wang377ff312009-10-10 19:08:55 +080025/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010026/* 2006-03-03 Lydia Wang Create the basic patch to support VT1708 codec */
Lydia Wang377ff312009-10-10 19:08:55 +080027/* 2006-03-14 Lydia Wang Modify hard code for some pin widget nid */
28/* 2006-08-02 Lydia Wang Add support to VT1709 codec */
Joseph Chanc577b8a2006-11-29 15:29:40 +010029/* 2006-09-08 Lydia Wang Fix internal loopback recording source select bug */
Lydia Wang377ff312009-10-10 19:08:55 +080030/* 2007-09-12 Lydia Wang Add EAPD enable during driver initialization */
31/* 2007-09-17 Lydia Wang Add VT1708B codec support */
Harald Welte76d9b0d2008-09-09 15:50:37 +080032/* 2007-11-14 Lydia Wang Add VT1708A codec HP and CD pin connect config */
Harald Weltefb4cb772008-09-09 15:53:36 +080033/* 2008-02-03 Lydia Wang Fix Rear channels and Back channels inverse issue */
Lydia Wang377ff312009-10-10 19:08:55 +080034/* 2008-03-06 Lydia Wang Add VT1702 codec and VT1708S codec support */
35/* 2008-04-09 Lydia Wang Add mute front speaker when HP plugin */
36/* 2008-04-09 Lydia Wang Add Independent HP feature */
Harald Welte98aa34c2008-09-09 16:02:09 +080037/* 2008-05-28 Lydia Wang Add second S/PDIF Out support for VT1702 */
Lydia Wang377ff312009-10-10 19:08:55 +080038/* 2008-09-15 Logan Li Add VT1708S Mic Boost workaround/backdoor */
Lydia Wang8e865972009-10-10 19:08:52 +080039/* 2009-02-16 Logan Li Add support for VT1718S */
40/* 2009-03-13 Logan Li Add support for VT1716S */
41/* 2009-04-14 Lydai Wang Add support for VT1828S and VT2020 */
42/* 2009-07-08 Lydia Wang Add support for VT2002P */
43/* 2009-07-21 Lydia Wang Add support for VT1812 */
Lydia Wang36dd5c42009-10-20 13:18:04 +080044/* 2009-09-19 Lydia Wang Add support for VT1818S */
Lydia Wang377ff312009-10-10 19:08:55 +080045/* */
Joseph Chanc577b8a2006-11-29 15:29:40 +010046/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
47
48
Joseph Chanc577b8a2006-11-29 15:29:40 +010049#include <linux/init.h>
50#include <linux/delay.h>
51#include <linux/slab.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010052#include <sound/core.h>
Harald Welte0aa62ae2008-09-09 15:58:27 +080053#include <sound/asoundef.h>
Joseph Chanc577b8a2006-11-29 15:29:40 +010054#include "hda_codec.h"
55#include "hda_local.h"
Joseph Chanc577b8a2006-11-29 15:29:40 +010056
Joseph Chanc577b8a2006-11-29 15:29:40 +010057/* Pin Widget NID */
Harald Welted949cac2008-09-09 15:56:01 +080058#define VT1708_HP_PIN_NID 0x20
59#define VT1708_CD_PIN_NID 0x24
Joseph Chanc577b8a2006-11-29 15:29:40 +010060
Harald Welted7426322008-09-15 22:43:23 +080061enum VIA_HDA_CODEC {
62 UNKNOWN = -1,
63 VT1708,
64 VT1709_10CH,
65 VT1709_6CH,
66 VT1708B_8CH,
67 VT1708B_4CH,
68 VT1708S,
Lydia Wang518bf3b2009-10-10 19:07:29 +080069 VT1708BCE,
Harald Welted7426322008-09-15 22:43:23 +080070 VT1702,
Lydia Wangeb7188c2009-10-10 19:08:34 +080071 VT1718S,
Lydia Wangf3db4232009-10-10 19:08:41 +080072 VT1716S,
Lydia Wang25eaba22009-10-10 19:08:43 +080073 VT2002P,
Lydia Wangab6734e2009-10-10 19:08:46 +080074 VT1812,
Lydia Wang118909562011-03-23 17:57:34 +080075 VT1802,
Harald Welted7426322008-09-15 22:43:23 +080076 CODEC_TYPES,
77};
78
Lydia Wang118909562011-03-23 17:57:34 +080079#define VT2002P_COMPATIBLE(spec) \
80 ((spec)->codec_type == VT2002P ||\
81 (spec)->codec_type == VT1812 ||\
82 (spec)->codec_type == VT1802)
83
Takashi Iwai8e3679d2011-06-21 09:01:36 +020084#define MAX_NID_PATH_DEPTH 5
85
Takashi Iwai09a9ad692011-06-21 15:57:44 +020086/* output-path: DAC -> ... -> pin
87 * idx[] contains the source index number of the next widget;
88 * e.g. idx[0] is the index of the DAC selected by path[1] widget
89 * multi[] indicates whether it's a selector widget with multi-connectors
90 * (i.e. the connection selection is mandatory)
91 * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
92 */
Takashi Iwai4a796162011-06-17 17:53:38 +020093struct nid_path {
94 int depth;
Takashi Iwai8e3679d2011-06-21 09:01:36 +020095 hda_nid_t path[MAX_NID_PATH_DEPTH];
Takashi Iwai09a9ad692011-06-21 15:57:44 +020096 unsigned char idx[MAX_NID_PATH_DEPTH];
97 unsigned char multi[MAX_NID_PATH_DEPTH];
98 unsigned int vol_ctl;
99 unsigned int mute_ctl;
Takashi Iwai4a796162011-06-17 17:53:38 +0200100};
101
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200102/* input-path */
103struct via_input {
104 hda_nid_t pin; /* input-pin or aa-mix */
105 int adc_idx; /* ADC index to be used */
106 int mux_idx; /* MUX index (if any) */
107 const char *label; /* input-source label */
108};
109
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200110#define VIA_MAX_ADCS 3
111
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800112struct via_spec {
113 /* codec parameterization */
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200114 const struct snd_kcontrol_new *mixers[6];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800115 unsigned int num_mixers;
116
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200117 const struct hda_verb *init_verbs[5];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800118 unsigned int num_iverbs;
119
Takashi Iwai82673bc2011-06-17 16:24:21 +0200120 char stream_name_analog[32];
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200121 char stream_name_hp[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200122 const struct hda_pcm_stream *stream_analog_playback;
123 const struct hda_pcm_stream *stream_analog_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800124
Takashi Iwai82673bc2011-06-17 16:24:21 +0200125 char stream_name_digital[32];
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200126 const struct hda_pcm_stream *stream_digital_playback;
127 const struct hda_pcm_stream *stream_digital_capture;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800128
129 /* playback */
130 struct hda_multi_out multiout;
131 hda_nid_t slave_dig_outs[2];
Takashi Iwaiece8d042011-06-19 16:24:21 +0200132 hda_nid_t hp_dac_nid;
Takashi Iwai25250502011-06-30 17:24:47 +0200133 bool hp_indep_shared; /* indep HP-DAC is shared with side ch */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200134 int num_active_streams;
Lydia Wangc4394f52011-07-04 16:54:15 +0800135 int dac_mixer_idx;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800136
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200137 struct nid_path out_path[HDA_SIDE + 1];
Takashi Iwai4a796162011-06-17 17:53:38 +0200138 struct nid_path hp_path;
139 struct nid_path hp_dep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200140 struct nid_path speaker_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200141
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800142 /* capture */
143 unsigned int num_adc_nids;
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200144 hda_nid_t adc_nids[VIA_MAX_ADCS];
145 hda_nid_t mux_nids[VIA_MAX_ADCS];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200146 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800147 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800148
149 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200150 bool dyn_adc_switch;
151 int num_inputs;
152 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200153 unsigned int cur_mux[VIA_MAX_ADCS];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800154
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200155 /* dynamic ADC switching */
156 hda_nid_t cur_adc;
157 unsigned int cur_adc_stream_tag;
158 unsigned int cur_adc_format;
159
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800160 /* PCM information */
161 struct hda_pcm pcm_rec[3];
162
163 /* dynamic controls, init_verbs and input_mux */
164 struct auto_pin_cfg autocfg;
165 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800166 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
167
168 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800169 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800170 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200171 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800172 enum VIA_HDA_CODEC codec_type;
173
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200174 /* smart51 setup */
175 unsigned int smart51_nums;
176 hda_nid_t smart51_pins[2];
177 int smart51_idxs[2];
178 const char *smart51_labels[2];
179 unsigned int smart51_enabled;
180
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800181 /* work to check hp jack state */
182 struct hda_codec *codec;
183 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200184 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800185 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800186
187 void (*set_widgets_power_state)(struct hda_codec *codec);
188
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800189 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200190 int num_loopbacks;
191 struct hda_amp_list loopback_list[8];
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200192
193 /* bind capture-volume */
194 struct hda_bind_ctls *bind_cap_vol;
195 struct hda_bind_ctls *bind_cap_sw;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800196};
197
Lydia Wang0341ccd2011-03-22 16:25:03 +0800198static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100199static struct via_spec * via_new_spec(struct hda_codec *codec)
200{
201 struct via_spec *spec;
202
203 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
204 if (spec == NULL)
205 return NULL;
206
207 codec->spec = spec;
208 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800209 spec->codec_type = get_codec_type(codec);
210 /* VT1708BCE & VT1708S are almost same */
211 if (spec->codec_type == VT1708BCE)
212 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100213 return spec;
214}
215
Lydia Wang744ff5f2009-10-10 19:07:26 +0800216static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800217{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800218 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800219 u16 ven_id = vendor_id >> 16;
220 u16 dev_id = vendor_id & 0xffff;
221 enum VIA_HDA_CODEC codec_type;
222
223 /* get codec type */
224 if (ven_id != 0x1106)
225 codec_type = UNKNOWN;
226 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
227 codec_type = VT1708;
228 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
229 codec_type = VT1709_10CH;
230 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
231 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800232 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800233 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800234 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
235 codec_type = VT1708BCE;
236 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800237 codec_type = VT1708B_4CH;
238 else if ((dev_id & 0xfff) == 0x397
239 && (dev_id >> 12) < 8)
240 codec_type = VT1708S;
241 else if ((dev_id & 0xfff) == 0x398
242 && (dev_id >> 12) < 8)
243 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800244 else if ((dev_id & 0xfff) == 0x428
245 && (dev_id >> 12) < 8)
246 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800247 else if (dev_id == 0x0433 || dev_id == 0xa721)
248 codec_type = VT1716S;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +0800249 else if (dev_id == 0x0441 || dev_id == 0x4441)
250 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800251 else if (dev_id == 0x0438 || dev_id == 0x4438)
252 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800253 else if (dev_id == 0x0448)
254 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800255 else if (dev_id == 0x0440)
256 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800257 else if ((dev_id & 0xfff) == 0x446)
258 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800259 else
260 codec_type = UNKNOWN;
261 return codec_type;
262};
263
Lydia Wangec7e7e42011-03-24 12:43:44 +0800264#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800265#define VIA_HP_EVENT 0x01
266#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200267#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800268
Joseph Chanc577b8a2006-11-29 15:29:40 +0100269enum {
270 VIA_CTL_WIDGET_VOL,
271 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800272 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100273};
274
Takashi Iwaiada509e2011-06-20 15:40:19 +0200275static void analog_low_current_mode(struct hda_codec *codec);
276static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800277
278static void vt1708_start_hp_work(struct via_spec *spec)
279{
280 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
281 return;
282 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200283 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800284 if (!delayed_work_pending(&spec->vt1708_hp_work))
285 schedule_delayed_work(&spec->vt1708_hp_work,
286 msecs_to_jiffies(100));
287}
288
289static void vt1708_stop_hp_work(struct via_spec *spec)
290{
291 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
292 return;
293 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
294 && !is_aa_path_mute(spec->codec))
295 return;
296 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200297 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100298 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800299}
Lydia Wangf5271102009-10-10 19:07:35 +0800300
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800301static void set_widgets_power_state(struct hda_codec *codec)
302{
303 struct via_spec *spec = codec->spec;
304 if (spec->set_widgets_power_state)
305 spec->set_widgets_power_state(codec);
306}
Lydia Wang25eaba22009-10-10 19:08:43 +0800307
Lydia Wangf5271102009-10-10 19:07:35 +0800308static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
309 struct snd_ctl_elem_value *ucontrol)
310{
311 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
312 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
313
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800314 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200315 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800316 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
317 if (is_aa_path_mute(codec))
318 vt1708_start_hp_work(codec->spec);
319 else
320 vt1708_stop_hp_work(codec->spec);
321 }
Lydia Wangf5271102009-10-10 19:07:35 +0800322 return change;
323}
324
325/* modify .put = snd_hda_mixer_amp_switch_put */
326#define ANALOG_INPUT_MUTE \
327 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
328 .name = NULL, \
329 .index = 0, \
330 .info = snd_hda_mixer_amp_switch_info, \
331 .get = snd_hda_mixer_amp_switch_get, \
332 .put = analog_input_switch_put, \
333 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
334
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200335static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100336 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
337 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800338 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100339};
340
Lydia Wangab6734e2009-10-10 19:08:46 +0800341
Joseph Chanc577b8a2006-11-29 15:29:40 +0100342/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200343static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
344 const struct snd_kcontrol_new *tmpl,
345 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100346{
347 struct snd_kcontrol_new *knew;
348
Takashi Iwai603c4012008-07-30 15:01:44 +0200349 snd_array_init(&spec->kctls, sizeof(*knew), 32);
350 knew = snd_array_new(&spec->kctls);
351 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200352 return NULL;
353 *knew = *tmpl;
354 if (!name)
355 name = tmpl->name;
356 if (name) {
357 knew->name = kstrdup(name, GFP_KERNEL);
358 if (!knew->name)
359 return NULL;
360 }
361 return knew;
362}
363
364static int __via_add_control(struct via_spec *spec, int type, const char *name,
365 int idx, unsigned long val)
366{
367 struct snd_kcontrol_new *knew;
368
369 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
370 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100371 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200372 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100373 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100374 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100375 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100376 return 0;
377}
378
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200379#define via_add_control(spec, type, name, val) \
380 __via_add_control(spec, type, name, 0, val)
381
Takashi Iwai291c9e32011-06-17 16:15:26 +0200382#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100383
Takashi Iwai603c4012008-07-30 15:01:44 +0200384static void via_free_kctls(struct hda_codec *codec)
385{
386 struct via_spec *spec = codec->spec;
387
388 if (spec->kctls.list) {
389 struct snd_kcontrol_new *kctl = spec->kctls.list;
390 int i;
391 for (i = 0; i < spec->kctls.used; i++)
392 kfree(kctl[i].name);
393 }
394 snd_array_free(&spec->kctls);
395}
396
Joseph Chanc577b8a2006-11-29 15:29:40 +0100397/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800398static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200399 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100400{
401 char name[32];
402 int err;
403
404 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200405 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100406 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
407 if (err < 0)
408 return err;
409 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200410 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100411 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
412 if (err < 0)
413 return err;
414 return 0;
415}
416
Takashi Iwai5d417622011-06-20 11:32:27 +0200417#define get_connection_index(codec, mux, nid) \
Takashi Iwai8d087c72011-06-28 12:45:47 +0200418 snd_hda_get_conn_index(codec, mux, nid, 0)
Takashi Iwai5d417622011-06-20 11:32:27 +0200419
Takashi Iwai8df2a312011-06-21 11:48:29 +0200420static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
421 unsigned int mask)
422{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200423 unsigned int caps;
424 if (!nid)
425 return false;
426 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200427 if (dir == HDA_INPUT)
428 caps &= AC_WCAP_IN_AMP;
429 else
430 caps &= AC_WCAP_OUT_AMP;
431 if (!caps)
432 return false;
433 if (query_amp_caps(codec, nid, dir) & mask)
434 return true;
435 return false;
436}
437
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200438#define have_mute(codec, nid, dir) \
439 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200440
Lydia Wangd69607b2011-07-08 14:02:52 +0800441static bool is_node_in_path(struct nid_path *path, hda_nid_t nid)
442{
443 int i;
444 if (!nid)
445 return false;
446 for (i = 0; i < path->depth; i++) {
447 if (path->path[i] == nid)
448 return true;
449 }
450 return false;
451}
452
453/* enable/disable the output-route mixers */
454static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
455 hda_nid_t mix_nid, int aa_mix_idx, bool enable)
456{
457 int i, num, val;
458 bool hp_path, front_path;
459 struct via_spec *spec = codec->spec;
460
461 if (!path)
462 return;
463 num = snd_hda_get_conn_list(codec, mix_nid, NULL);
464 hp_path = is_node_in_path(path, spec->hp_dac_nid);
465 front_path = is_node_in_path(path, spec->multiout.dac_nids[0]);
466
467 for (i = 0; i < num; i++) {
468 if (i == aa_mix_idx) {
469 if (hp_path)
470 val = enable ? AMP_IN_MUTE(i) :
471 AMP_IN_UNMUTE(i);
472 else if (front_path)
473 val = AMP_IN_UNMUTE(i);
474 else
475 val = AMP_IN_MUTE(i);
476 } else {
477 if (hp_path)
478 val = enable ? AMP_IN_UNMUTE(i) :
479 AMP_IN_MUTE(i);
480 else if (front_path)
481 val = AMP_IN_MUTE(i);
482 else
483 val = AMP_IN_UNMUTE(i);
484 }
485 snd_hda_codec_write(codec, mix_nid, 0,
486 AC_VERB_SET_AMP_GAIN_MUTE, val);
487 }
488}
489
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200490/* enable/disable the output-route */
491static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
492 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200493{
Lydia Wangd69607b2011-07-08 14:02:52 +0800494 int i, val;
495 struct via_spec *spec = codec->spec;
496 hda_nid_t aa_mix_nid = spec->aa_mix_nid;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200497 for (i = 0; i < path->depth; i++) {
498 hda_nid_t src, dst;
499 int idx = path->idx[i];
500 src = path->path[i];
501 if (i < path->depth - 1)
502 dst = path->path[i + 1];
503 else
504 dst = 0;
505 if (enable && path->multi[i])
506 snd_hda_codec_write(codec, dst, 0,
507 AC_VERB_SET_CONNECT_SEL, idx);
Lydia Wangb89596a2011-07-04 17:01:33 +0800508 if (!force
509 && get_wcaps_type(get_wcaps(codec, src)) == AC_WID_AUD_OUT
510 && get_wcaps_type(get_wcaps(codec, dst)) == AC_WID_AUD_MIX)
Lydia Wange5e14682011-07-01 10:55:07 +0800511 continue;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200512 if (have_mute(codec, dst, HDA_INPUT)) {
Lydia Wangd69607b2011-07-08 14:02:52 +0800513 if (dst == aa_mix_nid) {
514 val = enable ? AMP_IN_UNMUTE(idx) :
515 AMP_IN_MUTE(idx);
516 snd_hda_codec_write(codec, dst, 0,
517 AC_VERB_SET_AMP_GAIN_MUTE, val);
518 } else {
519 idx = get_connection_index(codec, dst,
520 aa_mix_nid);
521 if (idx >= 0) {
522 activate_output_mix(codec, path,
523 dst, idx, enable);
524 }
525 }
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200526 }
527 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
528 continue;
529 if (have_mute(codec, src, HDA_OUTPUT)) {
530 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
531 snd_hda_codec_write(codec, src, 0,
532 AC_VERB_SET_AMP_GAIN_MUTE, val);
533 }
534 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200535}
536
537/* set the given pin as output */
538static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
539 int pin_type)
540{
541 if (!pin)
542 return;
543 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
544 pin_type);
545 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
546 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200547 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100548}
549
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200550static void via_auto_init_output(struct hda_codec *codec,
551 struct nid_path *path, int pin_type,
Takashi Iwaibac4b922011-07-04 17:35:51 +0200552 bool with_aa_mix, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200553{
554 struct via_spec *spec = codec->spec;
555 unsigned int caps;
Lydia Wangd69607b2011-07-08 14:02:52 +0800556 hda_nid_t pin;
Takashi Iwai5d417622011-06-20 11:32:27 +0200557
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200558 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200559 return;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200560 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200561
562 init_output_pin(codec, pin, pin_type);
563 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
564 if (caps & AC_AMPCAP_MUTE) {
565 unsigned int val;
566 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
567 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
568 AMP_OUT_MUTE | val);
569 }
570
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200571 /* initialize the AA-path */
572 if (!spec->aa_mix_nid)
573 return;
Lydia Wangd69607b2011-07-08 14:02:52 +0800574 activate_output_path(codec, path, true, force);
Takashi Iwai5d417622011-06-20 11:32:27 +0200575}
576
Joseph Chanc577b8a2006-11-29 15:29:40 +0100577static void via_auto_init_multi_out(struct hda_codec *codec)
578{
579 struct via_spec *spec = codec->spec;
580 int i;
581
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200582 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
Takashi Iwaibac4b922011-07-04 17:35:51 +0200583 /* enable aa-mute only for the front channel */
584 via_auto_init_output(codec, &spec->out_path[i], PIN_OUT,
585 i == 0, true);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100586}
587
588static void via_auto_init_hp_out(struct hda_codec *codec)
589{
590 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100591
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200592 if (!spec->hp_dac_nid) {
Takashi Iwaibac4b922011-07-04 17:35:51 +0200593 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP,
594 true, true);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200595 return;
596 }
597 if (spec->hp_independent_mode) {
598 activate_output_path(codec, &spec->hp_dep_path, false, false);
Takashi Iwaibac4b922011-07-04 17:35:51 +0200599 via_auto_init_output(codec, &spec->hp_path, PIN_HP,
600 true, true);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200601 } else {
602 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwaibac4b922011-07-04 17:35:51 +0200603 via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP,
604 true, true);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200605 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100606}
607
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200608static void via_auto_init_speaker_out(struct hda_codec *codec)
609{
610 struct via_spec *spec = codec->spec;
611
612 if (spec->autocfg.speaker_outs)
Takashi Iwaibac4b922011-07-04 17:35:51 +0200613 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT,
614 true, true);
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200615}
616
Takashi Iwaif4a78282011-06-17 18:46:48 +0200617static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200618static void via_hp_automute(struct hda_codec *codec);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200619
Joseph Chanc577b8a2006-11-29 15:29:40 +0100620static void via_auto_init_analog_input(struct hda_codec *codec)
621{
622 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200623 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200624 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200625 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200626 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100627
Takashi Iwai096a8852011-06-20 12:09:02 +0200628 /* init ADCs */
629 for (i = 0; i < spec->num_adc_nids; i++) {
630 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
631 AC_VERB_SET_AMP_GAIN_MUTE,
632 AMP_IN_UNMUTE(0));
633 }
634
635 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200636 for (i = 0; i < cfg->num_inputs; i++) {
637 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200638 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200639 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100640 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200641 ctl = PIN_VREF50;
642 else
643 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100644 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200645 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100646 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200647
648 /* init input-src */
649 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200650 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
651 if (spec->mux_nids[adc_idx]) {
652 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
653 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
654 AC_VERB_SET_CONNECT_SEL,
655 mux_idx);
656 }
657 if (spec->dyn_adc_switch)
658 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200659 }
660
661 /* init aa-mixer */
662 if (!spec->aa_mix_nid)
663 return;
664 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
665 ARRAY_SIZE(conn));
666 for (i = 0; i < num_conns; i++) {
667 unsigned int caps = get_wcaps(codec, conn[i]);
668 if (get_wcaps_type(caps) == AC_WID_PIN)
669 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
670 AC_VERB_SET_AMP_GAIN_MUTE,
671 AMP_IN_MUTE(i));
672 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100673}
Lydia Wangf5271102009-10-10 19:07:35 +0800674
675static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
676 unsigned int *affected_parm)
677{
678 unsigned parm;
679 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
680 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
681 >> AC_DEFCFG_MISC_SHIFT
682 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800683 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200684 unsigned present = 0;
685
686 no_presence |= spec->no_pin_power_ctl;
687 if (!no_presence)
688 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200689 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800690 || ((no_presence || present)
691 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800692 *affected_parm = AC_PWRST_D0; /* if it's connected */
693 parm = AC_PWRST_D0;
694 } else
695 parm = AC_PWRST_D3;
696
697 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
698}
699
Takashi Iwai24088a52011-06-17 16:59:21 +0200700static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
701 struct snd_ctl_elem_info *uinfo)
702{
703 static const char * const texts[] = {
704 "Disabled", "Enabled"
705 };
706
707 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
708 uinfo->count = 1;
709 uinfo->value.enumerated.items = 2;
710 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
711 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
712 strcpy(uinfo->value.enumerated.name,
713 texts[uinfo->value.enumerated.item]);
714 return 0;
715}
716
717static int via_pin_power_ctl_get(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;
722 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
723 return 0;
724}
725
726static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
727 struct snd_ctl_elem_value *ucontrol)
728{
729 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
730 struct via_spec *spec = codec->spec;
731 unsigned int val = !ucontrol->value.enumerated.item[0];
732
733 if (val == spec->no_pin_power_ctl)
734 return 0;
735 spec->no_pin_power_ctl = val;
736 set_widgets_power_state(codec);
737 return 1;
738}
739
740static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
741 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
742 .name = "Dynamic Power-Control",
743 .info = via_pin_power_ctl_info,
744 .get = via_pin_power_ctl_get,
745 .put = via_pin_power_ctl_put,
746};
747
748
Harald Welte0aa62ae2008-09-09 15:58:27 +0800749static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
750 struct snd_ctl_elem_info *uinfo)
751{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200752 static const char * const texts[] = { "OFF", "ON" };
753
754 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
755 uinfo->count = 1;
756 uinfo->value.enumerated.items = 2;
757 if (uinfo->value.enumerated.item >= 2)
758 uinfo->value.enumerated.item = 1;
759 strcpy(uinfo->value.enumerated.name,
760 texts[uinfo->value.enumerated.item]);
761 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800762}
763
764static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
765 struct snd_ctl_elem_value *ucontrol)
766{
767 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800768 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800769
Takashi Iwaiece8d042011-06-19 16:24:21 +0200770 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800771 return 0;
772}
773
Harald Welte0aa62ae2008-09-09 15:58:27 +0800774static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
775 struct snd_ctl_elem_value *ucontrol)
776{
777 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
778 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +0200779 int cur;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200780
Takashi Iwai25250502011-06-30 17:24:47 +0200781 /* no independent-hp status change during PCM playback is running */
782 if (spec->num_active_streams)
783 return -EBUSY;
784
785 cur = !!ucontrol->value.enumerated.item[0];
786 if (spec->hp_independent_mode == cur)
787 return 0;
788 spec->hp_independent_mode = cur;
789 if (cur) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200790 activate_output_path(codec, &spec->hp_dep_path, false, false);
791 activate_output_path(codec, &spec->hp_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200792 if (spec->hp_indep_shared)
793 activate_output_path(codec, &spec->out_path[HDA_SIDE],
794 false, false);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200795 } else {
796 activate_output_path(codec, &spec->hp_path, false, false);
797 activate_output_path(codec, &spec->hp_dep_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200798 if (spec->hp_indep_shared)
799 activate_output_path(codec, &spec->out_path[HDA_SIDE],
800 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200801 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800802
Lydia Wangce0e5a92011-03-22 16:22:37 +0800803 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800804 set_widgets_power_state(codec);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200805 via_hp_automute(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200806 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800807}
808
Takashi Iwaiece8d042011-06-19 16:24:21 +0200809static const struct snd_kcontrol_new via_hp_mixer = {
810 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
811 .name = "Independent HP",
812 .info = via_independent_hp_info,
813 .get = via_independent_hp_get,
814 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800815};
816
Takashi Iwai3d83e572010-04-14 14:36:23 +0200817static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100818{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200819 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100820 struct snd_kcontrol_new *knew;
821 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100822
Takashi Iwaiece8d042011-06-19 16:24:21 +0200823 nid = spec->autocfg.hp_pins[0];
824 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200825 if (knew == NULL)
826 return -ENOMEM;
827
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100828 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100829
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100830 return 0;
831}
832
Lydia Wang1564b282009-10-10 19:07:52 +0800833static void notify_aa_path_ctls(struct hda_codec *codec)
834{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200835 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800836 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800837
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200838 for (i = 0; i < spec->smart51_nums; i++) {
839 struct snd_kcontrol *ctl;
840 struct snd_ctl_elem_id id;
841 memset(&id, 0, sizeof(id));
842 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
843 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800844 ctl = snd_hda_find_mixer_ctl(codec, id.name);
845 if (ctl)
846 snd_ctl_notify(codec->bus->card,
847 SNDRV_CTL_EVENT_MASK_VALUE,
848 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800849 }
850}
851
852static void mute_aa_path(struct hda_codec *codec, int mute)
853{
854 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200855 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800856 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200857
Lydia Wang1564b282009-10-10 19:07:52 +0800858 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200859 for (i = 0; i < spec->smart51_nums; i++) {
860 if (spec->smart51_idxs[i] < 0)
861 continue;
862 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
863 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800864 HDA_AMP_MUTE, val);
865 }
866}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200867
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200868static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
869{
870 struct via_spec *spec = codec->spec;
871 int i;
872
873 for (i = 0; i < spec->smart51_nums; i++)
874 if (spec->smart51_pins[i] == pin)
875 return true;
876 return false;
877}
878
Lydia Wang1564b282009-10-10 19:07:52 +0800879static int via_smart51_get(struct snd_kcontrol *kcontrol,
880 struct snd_ctl_elem_value *ucontrol)
881{
882 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
883 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800884
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200885 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800886 return 0;
887}
888
889static int via_smart51_put(struct snd_kcontrol *kcontrol,
890 struct snd_ctl_elem_value *ucontrol)
891{
892 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
893 struct via_spec *spec = codec->spec;
894 int out_in = *ucontrol->value.integer.value
895 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800896 int i;
897
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200898 for (i = 0; i < spec->smart51_nums; i++) {
899 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200900 unsigned int parm;
901
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200902 parm = snd_hda_codec_read(codec, nid, 0,
903 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
904 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
905 parm |= out_in;
906 snd_hda_codec_write(codec, nid, 0,
907 AC_VERB_SET_PIN_WIDGET_CONTROL,
908 parm);
909 if (out_in == AC_PINCTL_OUT_EN) {
910 mute_aa_path(codec, 1);
911 notify_aa_path_ctls(codec);
912 }
Lydia Wang1564b282009-10-10 19:07:52 +0800913 }
914 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800915 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800916 return 1;
917}
918
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200919static const struct snd_kcontrol_new via_smart51_mixer = {
920 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
921 .name = "Smart 5.1",
922 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200923 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200924 .get = via_smart51_get,
925 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800926};
927
Takashi Iwaif4a78282011-06-17 18:46:48 +0200928static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100929{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200930 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100931
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200932 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800933 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200934 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100935 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100936 return 0;
937}
938
Takashi Iwaiada509e2011-06-20 15:40:19 +0200939/* check AA path's mute status */
940static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800941{
Lydia Wangf5271102009-10-10 19:07:35 +0800942 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200943 const struct hda_amp_list *p;
944 int i, ch, v;
945
946 for (i = 0; i < spec->num_loopbacks; i++) {
947 p = &spec->loopback_list[i];
948 for (ch = 0; ch < 2; ch++) {
949 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
950 p->idx);
951 if (!(v & HDA_AMP_MUTE) && v > 0)
952 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800953 }
954 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200955 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800956}
957
958/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200959static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800960{
961 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200962 bool enable;
963 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800964
Takashi Iwaiada509e2011-06-20 15:40:19 +0200965 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800966
967 /* decide low current mode's verb & parameter */
968 switch (spec->codec_type) {
969 case VT1708B_8CH:
970 case VT1708B_4CH:
971 verb = 0xf70;
972 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
973 break;
974 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800975 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800976 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800977 verb = 0xf73;
978 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
979 break;
980 case VT1702:
981 verb = 0xf73;
982 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
983 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800984 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800985 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800986 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800987 verb = 0xf93;
988 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
989 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800990 default:
991 return; /* other codecs are not supported */
992 }
993 /* send verb */
994 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
995}
996
Joseph Chanc577b8a2006-11-29 15:29:40 +0100997/*
998 * generic initialization of ADC, input mixers and output mixers
999 */
Takashi Iwai096a8852011-06-20 12:09:02 +02001000static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +08001001 /* power down jack detect function */
1002 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001003 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001004};
1005
Takashi Iwaiada509e2011-06-20 15:40:19 +02001006static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001007{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001008 struct via_spec *spec = codec->spec;
1009
1010 if (active)
1011 spec->num_active_streams++;
1012 else
1013 spec->num_active_streams--;
1014 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001015}
1016
Takashi Iwaiece8d042011-06-19 16:24:21 +02001017static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001018 struct hda_codec *codec,
1019 struct snd_pcm_substream *substream)
1020{
1021 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001022 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001023 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001024
Takashi Iwai25250502011-06-30 17:24:47 +02001025 spec->multiout.hp_nid = 0;
1026 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
1027 if (!spec->hp_independent_mode) {
1028 if (!spec->hp_indep_shared)
1029 spec->multiout.hp_nid = spec->hp_dac_nid;
1030 } else {
1031 if (spec->hp_indep_shared)
1032 spec->multiout.num_dacs = cfg->line_outs - 1;
1033 }
1034 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001035 set_stream_active(codec, true);
1036 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1037 hinfo);
1038 if (err < 0) {
1039 spec->multiout.hp_nid = 0;
1040 set_stream_active(codec, false);
1041 return err;
1042 }
1043 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001044}
1045
Takashi Iwaiece8d042011-06-19 16:24:21 +02001046static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001047 struct hda_codec *codec,
1048 struct snd_pcm_substream *substream)
1049{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001050 struct via_spec *spec = codec->spec;
1051
1052 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001053 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001054 return 0;
1055}
1056
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001057static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1058 struct hda_codec *codec,
1059 struct snd_pcm_substream *substream)
1060{
1061 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001062
Takashi Iwaiece8d042011-06-19 16:24:21 +02001063 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001064 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001065 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1066 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001067 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001068 return 0;
1069}
1070
1071static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1072 struct hda_codec *codec,
1073 struct snd_pcm_substream *substream)
1074{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001075 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001076 return 0;
1077}
1078
1079static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1080 struct hda_codec *codec,
1081 unsigned int stream_tag,
1082 unsigned int format,
1083 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001084{
1085 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001086
Takashi Iwaiece8d042011-06-19 16:24:21 +02001087 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1088 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001089 vt1708_start_hp_work(spec);
1090 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001091}
1092
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001093static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1094 struct hda_codec *codec,
1095 unsigned int stream_tag,
1096 unsigned int format,
1097 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001098{
1099 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001100
Takashi Iwaiece8d042011-06-19 16:24:21 +02001101 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1102 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001103 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001104 return 0;
1105}
1106
1107static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1108 struct hda_codec *codec,
1109 struct snd_pcm_substream *substream)
1110{
1111 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001112
Takashi Iwaiece8d042011-06-19 16:24:21 +02001113 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001114 vt1708_stop_hp_work(spec);
1115 return 0;
1116}
1117
1118static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1119 struct hda_codec *codec,
1120 struct snd_pcm_substream *substream)
1121{
1122 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001123
Takashi Iwaiece8d042011-06-19 16:24:21 +02001124 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001125 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001126 return 0;
1127}
1128
Joseph Chanc577b8a2006-11-29 15:29:40 +01001129/*
1130 * Digital out
1131 */
1132static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1133 struct hda_codec *codec,
1134 struct snd_pcm_substream *substream)
1135{
1136 struct via_spec *spec = codec->spec;
1137 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1138}
1139
1140static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1141 struct hda_codec *codec,
1142 struct snd_pcm_substream *substream)
1143{
1144 struct via_spec *spec = codec->spec;
1145 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1146}
1147
Harald Welte5691ec72008-09-15 22:42:26 +08001148static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001149 struct hda_codec *codec,
1150 unsigned int stream_tag,
1151 unsigned int format,
1152 struct snd_pcm_substream *substream)
1153{
1154 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001155 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1156 stream_tag, format, substream);
1157}
Harald Welte5691ec72008-09-15 22:42:26 +08001158
Takashi Iwai9da29272009-05-07 16:31:14 +02001159static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1160 struct hda_codec *codec,
1161 struct snd_pcm_substream *substream)
1162{
1163 struct via_spec *spec = codec->spec;
1164 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001165 return 0;
1166}
1167
Joseph Chanc577b8a2006-11-29 15:29:40 +01001168/*
1169 * Analog capture
1170 */
1171static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1172 struct hda_codec *codec,
1173 unsigned int stream_tag,
1174 unsigned int format,
1175 struct snd_pcm_substream *substream)
1176{
1177 struct via_spec *spec = codec->spec;
1178
1179 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1180 stream_tag, 0, format);
1181 return 0;
1182}
1183
1184static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1185 struct hda_codec *codec,
1186 struct snd_pcm_substream *substream)
1187{
1188 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001189 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001190 return 0;
1191}
1192
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001193/* analog capture with dynamic ADC switching */
1194static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1195 struct hda_codec *codec,
1196 unsigned int stream_tag,
1197 unsigned int format,
1198 struct snd_pcm_substream *substream)
1199{
1200 struct via_spec *spec = codec->spec;
1201 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1202
1203 spec->cur_adc = spec->adc_nids[adc_idx];
1204 spec->cur_adc_stream_tag = stream_tag;
1205 spec->cur_adc_format = format;
1206 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
1207 return 0;
1208}
1209
1210static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1211 struct hda_codec *codec,
1212 struct snd_pcm_substream *substream)
1213{
1214 struct via_spec *spec = codec->spec;
1215
1216 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1217 spec->cur_adc = 0;
1218 return 0;
1219}
1220
1221/* re-setup the stream if running; called from input-src put */
1222static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1223{
1224 struct via_spec *spec = codec->spec;
1225 int adc_idx = spec->inputs[cur].adc_idx;
1226 hda_nid_t adc = spec->adc_nids[adc_idx];
1227
1228 if (spec->cur_adc && spec->cur_adc != adc) {
1229 /* stream is running, let's swap the current ADC */
1230 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1231 spec->cur_adc = adc;
1232 snd_hda_codec_setup_stream(codec, adc,
1233 spec->cur_adc_stream_tag, 0,
1234 spec->cur_adc_format);
1235 return true;
1236 }
1237 return false;
1238}
1239
Takashi Iwai9af74212011-06-18 16:17:45 +02001240static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001241 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001242 .channels_min = 2,
1243 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001244 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001245 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001246 .open = via_playback_multi_pcm_open,
1247 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001248 .prepare = via_playback_multi_pcm_prepare,
1249 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001250 },
1251};
1252
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001253static const struct hda_pcm_stream via_pcm_hp_playback = {
1254 .substreams = 1,
1255 .channels_min = 2,
1256 .channels_max = 2,
1257 /* NID is set in via_build_pcms */
1258 .ops = {
1259 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001260 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001261 .prepare = via_playback_hp_pcm_prepare,
1262 .cleanup = via_playback_hp_pcm_cleanup
1263 },
1264};
1265
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001266static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001267 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001268 .channels_min = 2,
1269 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001270 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001271 /* We got noisy outputs on the right channel on VT1708 when
1272 * 24bit samples are used. Until any workaround is found,
1273 * disable the 24bit format, so far.
1274 */
1275 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1276 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001277 .open = via_playback_multi_pcm_open,
1278 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001279 .prepare = via_playback_multi_pcm_prepare,
1280 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001281 },
1282};
1283
Takashi Iwai9af74212011-06-18 16:17:45 +02001284static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001285 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001286 .channels_min = 2,
1287 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001288 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001289 .ops = {
1290 .prepare = via_capture_pcm_prepare,
1291 .cleanup = via_capture_pcm_cleanup
1292 },
1293};
1294
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001295static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1296 .substreams = 1,
1297 .channels_min = 2,
1298 .channels_max = 2,
1299 /* NID is set in via_build_pcms */
1300 .ops = {
1301 .prepare = via_dyn_adc_capture_pcm_prepare,
1302 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1303 },
1304};
1305
Takashi Iwai9af74212011-06-18 16:17:45 +02001306static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001307 .substreams = 1,
1308 .channels_min = 2,
1309 .channels_max = 2,
1310 /* NID is set in via_build_pcms */
1311 .ops = {
1312 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001313 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001314 .prepare = via_dig_playback_pcm_prepare,
1315 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001316 },
1317};
1318
Takashi Iwai9af74212011-06-18 16:17:45 +02001319static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001320 .substreams = 1,
1321 .channels_min = 2,
1322 .channels_max = 2,
1323};
1324
Takashi Iwai370bafb2011-06-20 12:47:45 +02001325/*
1326 * slave controls for virtual master
1327 */
1328static const char * const via_slave_vols[] = {
1329 "Front Playback Volume",
1330 "Surround Playback Volume",
1331 "Center Playback Volume",
1332 "LFE Playback Volume",
1333 "Side Playback Volume",
1334 "Headphone Playback Volume",
1335 "Speaker Playback Volume",
1336 NULL,
1337};
1338
1339static const char * const via_slave_sws[] = {
1340 "Front Playback Switch",
1341 "Surround Playback Switch",
1342 "Center Playback Switch",
1343 "LFE Playback Switch",
1344 "Side Playback Switch",
1345 "Headphone Playback Switch",
1346 "Speaker Playback Switch",
1347 NULL,
1348};
1349
Joseph Chanc577b8a2006-11-29 15:29:40 +01001350static int via_build_controls(struct hda_codec *codec)
1351{
1352 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001353 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001354 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001355
Takashi Iwai24088a52011-06-17 16:59:21 +02001356 if (spec->set_widgets_power_state)
1357 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1358 return -ENOMEM;
1359
Joseph Chanc577b8a2006-11-29 15:29:40 +01001360 for (i = 0; i < spec->num_mixers; i++) {
1361 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1362 if (err < 0)
1363 return err;
1364 }
1365
1366 if (spec->multiout.dig_out_nid) {
1367 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001368 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001369 spec->multiout.dig_out_nid);
1370 if (err < 0)
1371 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001372 err = snd_hda_create_spdif_share_sw(codec,
1373 &spec->multiout);
1374 if (err < 0)
1375 return err;
1376 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001377 }
1378 if (spec->dig_in_nid) {
1379 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1380 if (err < 0)
1381 return err;
1382 }
Lydia Wang17314372009-10-10 19:07:37 +08001383
Takashi Iwai370bafb2011-06-20 12:47:45 +02001384 /* if we have no master control, let's create it */
1385 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1386 unsigned int vmaster_tlv[4];
1387 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1388 HDA_OUTPUT, vmaster_tlv);
1389 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1390 vmaster_tlv, via_slave_vols);
1391 if (err < 0)
1392 return err;
1393 }
1394 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1395 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1396 NULL, via_slave_sws);
1397 if (err < 0)
1398 return err;
1399 }
1400
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001401 /* assign Capture Source enums to NID */
1402 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1403 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001404 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001405 if (err < 0)
1406 return err;
1407 }
1408
Lydia Wang17314372009-10-10 19:07:37 +08001409 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001410 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001411 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001412
Takashi Iwai603c4012008-07-30 15:01:44 +02001413 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001414 return 0;
1415}
1416
1417static int via_build_pcms(struct hda_codec *codec)
1418{
1419 struct via_spec *spec = codec->spec;
1420 struct hda_pcm *info = spec->pcm_rec;
1421
1422 codec->num_pcms = 1;
1423 codec->pcm_info = info;
1424
Takashi Iwai82673bc2011-06-17 16:24:21 +02001425 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1426 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001427 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001428
1429 if (!spec->stream_analog_playback)
1430 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001431 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001432 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001433 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1434 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001435 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1436 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001437
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001438 if (!spec->stream_analog_capture) {
1439 if (spec->dyn_adc_switch)
1440 spec->stream_analog_capture =
1441 &via_pcm_dyn_adc_analog_capture;
1442 else
1443 spec->stream_analog_capture = &via_pcm_analog_capture;
1444 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001445 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1446 *spec->stream_analog_capture;
1447 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001448 if (!spec->dyn_adc_switch)
1449 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1450 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001451
1452 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1453 codec->num_pcms++;
1454 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001455 snprintf(spec->stream_name_digital,
1456 sizeof(spec->stream_name_digital),
1457 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001458 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001459 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001460 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001461 if (!spec->stream_digital_playback)
1462 spec->stream_digital_playback =
1463 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001464 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001465 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001466 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1467 spec->multiout.dig_out_nid;
1468 }
1469 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001470 if (!spec->stream_digital_capture)
1471 spec->stream_digital_capture =
1472 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001473 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001474 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001475 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1476 spec->dig_in_nid;
1477 }
1478 }
1479
Takashi Iwaiece8d042011-06-19 16:24:21 +02001480 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001481 codec->num_pcms++;
1482 info++;
1483 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1484 "%s HP", codec->chip_name);
1485 info->name = spec->stream_name_hp;
1486 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1487 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001488 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001489 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001490 return 0;
1491}
1492
1493static void via_free(struct hda_codec *codec)
1494{
1495 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001496
1497 if (!spec)
1498 return;
1499
Takashi Iwai603c4012008-07-30 15:01:44 +02001500 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001501 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001502 kfree(spec->bind_cap_vol);
1503 kfree(spec->bind_cap_sw);
1504 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001505}
1506
Takashi Iwai64be2852011-06-17 16:51:39 +02001507/* mute/unmute outputs */
1508static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1509 hda_nid_t *pins, bool mute)
1510{
1511 int i;
Takashi Iwai94994732011-07-11 11:36:44 +02001512 for (i = 0; i < num_pins; i++) {
1513 unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
1514 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1515 if (parm & AC_PINCTL_IN_EN)
1516 continue;
1517 if (mute)
1518 parm &= ~AC_PINCTL_OUT_EN;
1519 else
1520 parm |= AC_PINCTL_OUT_EN;
Takashi Iwai64be2852011-06-17 16:51:39 +02001521 snd_hda_codec_write(codec, pins[i], 0,
Takashi Iwai94994732011-07-11 11:36:44 +02001522 AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
1523 }
Takashi Iwai64be2852011-06-17 16:51:39 +02001524}
1525
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001526/* mute internal speaker if line-out is plugged */
1527static void via_line_automute(struct hda_codec *codec, int present)
1528{
1529 struct via_spec *spec = codec->spec;
1530
1531 if (!spec->autocfg.speaker_outs)
1532 return;
1533 if (!present)
1534 present = snd_hda_jack_detect(codec,
1535 spec->autocfg.line_out_pins[0]);
1536 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1537 spec->autocfg.speaker_pins,
1538 present);
1539}
1540
Harald Welte69e52a82008-09-09 15:57:32 +08001541/* mute internal speaker if HP is plugged */
1542static void via_hp_automute(struct hda_codec *codec)
1543{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001544 int present = 0;
Takashi Iwai6e969d92011-07-11 11:28:13 +02001545 int nums;
Harald Welte69e52a82008-09-09 15:57:32 +08001546 struct via_spec *spec = codec->spec;
1547
Takashi Iwai6e969d92011-07-11 11:28:13 +02001548 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0])
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001549 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai6e969d92011-07-11 11:28:13 +02001550
1551 if (spec->smart51_enabled)
1552 nums = spec->autocfg.line_outs + spec->smart51_nums;
1553 else
1554 nums = spec->autocfg.line_outs;
1555 toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
1556
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001557 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001558}
1559
Harald Welte69e52a82008-09-09 15:57:32 +08001560static void via_gpio_control(struct hda_codec *codec)
1561{
1562 unsigned int gpio_data;
1563 unsigned int vol_counter;
1564 unsigned int vol;
1565 unsigned int master_vol;
1566
1567 struct via_spec *spec = codec->spec;
1568
1569 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1570 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1571
1572 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1573 0xF84, 0) & 0x3F0000) >> 16;
1574
1575 vol = vol_counter & 0x1F;
1576 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1577 AC_VERB_GET_AMP_GAIN_MUTE,
1578 AC_AMP_GET_INPUT);
1579
1580 if (gpio_data == 0x02) {
1581 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001582 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1583 AC_VERB_SET_PIN_WIDGET_CONTROL,
1584 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001585 if (vol_counter & 0x20) {
1586 /* decrease volume */
1587 if (vol > master_vol)
1588 vol = master_vol;
1589 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1590 0, HDA_AMP_VOLMASK,
1591 master_vol-vol);
1592 } else {
1593 /* increase volume */
1594 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1595 HDA_AMP_VOLMASK,
1596 ((master_vol+vol) > 0x2A) ? 0x2A :
1597 (master_vol+vol));
1598 }
1599 } else if (!(gpio_data & 0x02)) {
1600 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001601 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1602 AC_VERB_SET_PIN_WIDGET_CONTROL,
1603 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001604 }
1605}
1606
1607/* unsolicited event for jack sensing */
1608static void via_unsol_event(struct hda_codec *codec,
1609 unsigned int res)
1610{
1611 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001612
Lydia Wanga34df192009-10-10 19:08:01 +08001613 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001614 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001615
1616 res &= ~VIA_JACK_EVENT;
1617
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001618 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001619 via_hp_automute(codec);
1620 else if (res == VIA_GPIO_EVENT)
1621 via_gpio_control(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001622}
1623
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001624#ifdef SND_HDA_NEEDS_RESUME
1625static int via_suspend(struct hda_codec *codec, pm_message_t state)
1626{
1627 struct via_spec *spec = codec->spec;
1628 vt1708_stop_hp_work(spec);
1629 return 0;
1630}
1631#endif
1632
Takashi Iwaicb53c622007-08-10 17:21:45 +02001633#ifdef CONFIG_SND_HDA_POWER_SAVE
1634static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1635{
1636 struct via_spec *spec = codec->spec;
1637 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1638}
1639#endif
1640
Joseph Chanc577b8a2006-11-29 15:29:40 +01001641/*
1642 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001643
1644static int via_init(struct hda_codec *codec);
1645
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001646static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001647 .build_controls = via_build_controls,
1648 .build_pcms = via_build_pcms,
1649 .init = via_init,
1650 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001651 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001652#ifdef SND_HDA_NEEDS_RESUME
1653 .suspend = via_suspend,
1654#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001655#ifdef CONFIG_SND_HDA_POWER_SAVE
1656 .check_power_status = via_check_power_status,
1657#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001658};
1659
Takashi Iwai4a796162011-06-17 17:53:38 +02001660static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001661{
Takashi Iwai4a796162011-06-17 17:53:38 +02001662 struct via_spec *spec = codec->spec;
1663 int i;
1664
1665 for (i = 0; i < spec->multiout.num_dacs; i++) {
1666 if (spec->multiout.dac_nids[i] == dac)
1667 return false;
1668 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001669 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001670 return false;
1671 return true;
1672}
1673
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001674static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001675 hda_nid_t target_dac, struct nid_path *path,
1676 int depth, int wid_type)
1677{
1678 hda_nid_t conn[8];
1679 int i, nums;
1680
1681 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1682 for (i = 0; i < nums; i++) {
1683 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1684 continue;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001685 if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
1686 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001687 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001688 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001689 return false;
1690 for (i = 0; i < nums; i++) {
1691 unsigned int type;
1692 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1693 if (type == AC_WID_AUD_OUT ||
1694 (wid_type != -1 && type != wid_type))
1695 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001696 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001697 path, depth + 1, AC_WID_AUD_SEL))
1698 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001699 }
1700 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001701
1702 found:
1703 path->path[path->depth] = conn[i];
1704 path->idx[path->depth] = i;
1705 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1706 path->multi[path->depth] = 1;
1707 path->depth++;
1708 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001709}
1710
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001711static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1712 hda_nid_t target_dac, struct nid_path *path)
1713{
1714 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1715 path->path[path->depth] = nid;
1716 path->depth++;
1717 return true;
1718 }
1719 return false;
1720}
1721
Takashi Iwai4a796162011-06-17 17:53:38 +02001722static int via_auto_fill_dac_nids(struct hda_codec *codec)
1723{
1724 struct via_spec *spec = codec->spec;
1725 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001726 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001727 hda_nid_t nid;
1728
Joseph Chanc577b8a2006-11-29 15:29:40 +01001729 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001730 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001731 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001732 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001733 if (!nid)
1734 continue;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001735 if (parse_output_path(codec, nid, 0, &spec->out_path[i])) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001736 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Lydia Wang5c9a5612011-07-08 14:03:43 +08001737 dac_num++;
1738 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001739 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001740 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001741 return 0;
1742}
1743
Takashi Iwai4a796162011-06-17 17:53:38 +02001744static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001745 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001746{
Takashi Iwai4a796162011-06-17 17:53:38 +02001747 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001748 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001749 hda_nid_t dac, pin, sel, nid;
1750 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001751
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001752 dac = check_dac ? path->path[0] : 0;
1753 pin = path->path[path->depth - 1];
1754 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001755
Takashi Iwai8df2a312011-06-21 11:48:29 +02001756 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001757 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001758 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001759 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001760 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1761 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001762 else
1763 nid = 0;
1764 if (nid) {
1765 sprintf(name, "%s Playback Volume", pfx);
1766 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001767 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001768 if (err < 0)
1769 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001770 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001771 }
1772
Takashi Iwai8df2a312011-06-21 11:48:29 +02001773 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001774 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001775 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001776 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001777 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1778 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001779 else
1780 nid = 0;
1781 if (nid) {
1782 sprintf(name, "%s Playback Switch", pfx);
1783 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1784 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1785 if (err < 0)
1786 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001787 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001788 }
1789 return 0;
1790}
1791
Takashi Iwaif4a78282011-06-17 18:46:48 +02001792static void mangle_smart51(struct hda_codec *codec)
1793{
1794 struct via_spec *spec = codec->spec;
1795 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001796 struct auto_pin_cfg_item *ins = cfg->inputs;
1797 int i, j, nums, attr;
1798 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001799
Takashi Iwai0f98c242011-06-21 12:51:33 +02001800 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1801 nums = 0;
1802 for (i = 0; i < cfg->num_inputs; i++) {
1803 unsigned int def;
1804 if (ins[i].type > AUTO_PIN_LINE_IN)
1805 continue;
1806 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1807 if (snd_hda_get_input_pin_attr(def) != attr)
1808 continue;
1809 for (j = 0; j < nums; j++)
1810 if (ins[pins[j]].type < ins[i].type) {
1811 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001812 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001813 break;
1814 }
1815 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001816 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001817 }
1818 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001819 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001820 for (i = 0; i < nums; i++) {
1821 hda_nid_t pin = ins[pins[i]].pin;
1822 spec->smart51_pins[spec->smart51_nums++] = pin;
1823 cfg->line_out_pins[cfg->line_outs++] = pin;
1824 if (cfg->line_outs == 3)
1825 break;
1826 }
1827 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001828 }
1829}
1830
Takashi Iwai4a796162011-06-17 17:53:38 +02001831/* add playback controls from the parsed DAC table */
1832static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1833{
1834 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001835 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001836 static const char * const chname[4] = {
1837 "Front", "Surround", "C/LFE", "Side"
1838 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001839 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001840 int old_line_outs;
1841
1842 /* check smart51 */
1843 old_line_outs = cfg->line_outs;
1844 if (cfg->line_outs == 1)
1845 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001846
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001847 err = via_auto_fill_dac_nids(codec);
1848 if (err < 0)
1849 return err;
1850
Lydia Wang5c9a5612011-07-08 14:03:43 +08001851 if (spec->multiout.num_dacs < 3) {
1852 spec->smart51_nums = 0;
1853 cfg->line_outs = old_line_outs;
1854 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001855 for (i = 0; i < cfg->line_outs; i++) {
1856 hda_nid_t pin, dac;
1857 pin = cfg->line_out_pins[i];
1858 dac = spec->multiout.dac_nids[i];
1859 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001860 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001861 if (i == HDA_CLFE) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001862 err = create_ch_ctls(codec, "Center", 1, true,
1863 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001864 if (err < 0)
1865 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001866 err = create_ch_ctls(codec, "LFE", 2, true,
1867 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001868 if (err < 0)
1869 return err;
1870 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001871 const char *pfx = chname[i];
1872 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1873 cfg->line_outs == 1)
1874 pfx = "Speaker";
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001875 err = create_ch_ctls(codec, pfx, 3, true,
1876 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001877 if (err < 0)
1878 return err;
1879 }
1880 }
1881
Takashi Iwai4a796162011-06-17 17:53:38 +02001882 idx = get_connection_index(codec, spec->aa_mix_nid,
1883 spec->multiout.dac_nids[0]);
Lydia Wangc4394f52011-07-04 16:54:15 +08001884 if (idx < 0 && spec->dac_mixer_idx)
1885 idx = spec->dac_mixer_idx;
Takashi Iwai4a796162011-06-17 17:53:38 +02001886 if (idx >= 0) {
1887 /* add control to mixer */
1888 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1889 "PCM Playback Volume",
1890 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1891 idx, HDA_INPUT));
1892 if (err < 0)
1893 return err;
1894 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1895 "PCM Playback Switch",
1896 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1897 idx, HDA_INPUT));
1898 if (err < 0)
1899 return err;
1900 }
1901
Takashi Iwaif4a78282011-06-17 18:46:48 +02001902 cfg->line_outs = old_line_outs;
1903
Joseph Chanc577b8a2006-11-29 15:29:40 +01001904 return 0;
1905}
1906
Takashi Iwai4a796162011-06-17 17:53:38 +02001907static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001908{
Takashi Iwai4a796162011-06-17 17:53:38 +02001909 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001910 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001911 bool check_dac;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001912 int err;
1913
1914 if (!pin)
1915 return 0;
1916
Takashi Iwai8df2a312011-06-21 11:48:29 +02001917 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001918 spec->hp_dac_nid = spec->hp_path.path[0];
Takashi Iwai25250502011-06-30 17:24:47 +02001919 else if (spec->multiout.dac_nids[HDA_SIDE] &&
1920 parse_output_path(codec, pin,
1921 spec->multiout.dac_nids[HDA_SIDE],
1922 &spec->hp_path)) {
1923 spec->hp_dac_nid = spec->hp_path.path[0];
1924 spec->hp_indep_shared = true;
Lydia Wanga2a870c2011-07-08 14:04:33 +08001925 } else if (spec->multiout.dac_nids[HDA_CLFE] &&
1926 parse_output_path(codec, pin,
1927 spec->multiout.dac_nids[HDA_CLFE],
1928 &spec->hp_path)) {
1929 spec->hp_dac_nid = spec->hp_path.path[0];
1930 spec->hp_indep_shared = true;
Takashi Iwai25250502011-06-30 17:24:47 +02001931 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001932
Takashi Iwaiece8d042011-06-19 16:24:21 +02001933 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001934 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001935 !spec->hp_dac_nid)
1936 return 0;
1937
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001938 if (spec->hp_dac_nid && !spec->hp_indep_shared) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001939 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001940 check_dac = true;
1941 } else {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001942 path = &spec->hp_dep_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001943 check_dac = false;
1944 }
1945 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001946 if (err < 0)
1947 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001948 if (spec->hp_dac_nid) {
1949 spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
1950 spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
1951 }
Harald Welte0aa62ae2008-09-09 15:58:27 +08001952
Joseph Chanc577b8a2006-11-29 15:29:40 +01001953 return 0;
1954}
1955
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001956static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1957{
1958 struct via_spec *spec = codec->spec;
1959 hda_nid_t pin, dac;
1960
1961 pin = spec->autocfg.speaker_pins[0];
1962 if (!spec->autocfg.speaker_outs || !pin)
1963 return 0;
1964
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001965 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1966 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001967 spec->multiout.extra_out_nid[0] = dac;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001968 return create_ch_ctls(codec, "Speaker", 3, true,
1969 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001970 }
1971 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001972 &spec->speaker_path))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001973 return create_ch_ctls(codec, "Speaker", 3, false,
1974 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001975
1976 return 0;
1977}
1978
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001979/* look for ADCs */
1980static int via_fill_adcs(struct hda_codec *codec)
1981{
1982 struct via_spec *spec = codec->spec;
1983 hda_nid_t nid = codec->start_nid;
1984 int i;
1985
1986 for (i = 0; i < codec->num_nodes; i++, nid++) {
1987 unsigned int wcaps = get_wcaps(codec, nid);
1988 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1989 continue;
1990 if (wcaps & AC_WCAP_DIGITAL)
1991 continue;
1992 if (!(wcaps & AC_WCAP_CONN_LIST))
1993 continue;
1994 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1995 return -ENOMEM;
1996 spec->adc_nids[spec->num_adc_nids++] = nid;
1997 }
1998 return 0;
1999}
2000
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002001/* input-src control */
2002static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2003 struct snd_ctl_elem_info *uinfo)
2004{
2005 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2006 struct via_spec *spec = codec->spec;
2007
2008 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2009 uinfo->count = 1;
2010 uinfo->value.enumerated.items = spec->num_inputs;
2011 if (uinfo->value.enumerated.item >= spec->num_inputs)
2012 uinfo->value.enumerated.item = spec->num_inputs - 1;
2013 strcpy(uinfo->value.enumerated.name,
2014 spec->inputs[uinfo->value.enumerated.item].label);
2015 return 0;
2016}
2017
2018static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2019 struct snd_ctl_elem_value *ucontrol)
2020{
2021 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2022 struct via_spec *spec = codec->spec;
2023 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2024
2025 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2026 return 0;
2027}
2028
2029static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2030 struct snd_ctl_elem_value *ucontrol)
2031{
2032 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2033 struct via_spec *spec = codec->spec;
2034 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2035 hda_nid_t mux;
2036 int cur;
2037
2038 cur = ucontrol->value.enumerated.item[0];
2039 if (cur < 0 || cur >= spec->num_inputs)
2040 return -EINVAL;
2041 if (spec->cur_mux[idx] == cur)
2042 return 0;
2043 spec->cur_mux[idx] = cur;
2044 if (spec->dyn_adc_switch) {
2045 int adc_idx = spec->inputs[cur].adc_idx;
2046 mux = spec->mux_nids[adc_idx];
2047 via_dyn_adc_pcm_resetup(codec, cur);
2048 } else {
2049 mux = spec->mux_nids[idx];
2050 if (snd_BUG_ON(!mux))
2051 return -EINVAL;
2052 }
2053
2054 if (mux) {
2055 /* switch to D0 beofre change index */
2056 if (snd_hda_codec_read(codec, mux, 0,
2057 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2058 snd_hda_codec_write(codec, mux, 0,
2059 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2060 snd_hda_codec_write(codec, mux, 0,
2061 AC_VERB_SET_CONNECT_SEL,
2062 spec->inputs[cur].mux_idx);
2063 }
2064
2065 /* update jack power state */
2066 set_widgets_power_state(codec);
2067 return 0;
2068}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002069
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002070static const struct snd_kcontrol_new via_input_src_ctl = {
2071 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2072 /* The multiple "Capture Source" controls confuse alsamixer
2073 * So call somewhat different..
2074 */
2075 /* .name = "Capture Source", */
2076 .name = "Input Source",
2077 .info = via_mux_enum_info,
2078 .get = via_mux_enum_get,
2079 .put = via_mux_enum_put,
2080};
2081
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002082static int create_input_src_ctls(struct hda_codec *codec, int count)
2083{
2084 struct via_spec *spec = codec->spec;
2085 struct snd_kcontrol_new *knew;
2086
2087 if (spec->num_inputs <= 1 || !count)
2088 return 0; /* no need for single src */
2089
2090 knew = via_clone_control(spec, &via_input_src_ctl);
2091 if (!knew)
2092 return -ENOMEM;
2093 knew->count = count;
2094 return 0;
2095}
2096
2097/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002098static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2099{
2100 struct hda_amp_list *list;
2101
2102 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2103 return;
2104 list = spec->loopback_list + spec->num_loopbacks;
2105 list->nid = mix;
2106 list->dir = HDA_INPUT;
2107 list->idx = idx;
2108 spec->num_loopbacks++;
2109 spec->loopback.amplist = spec->loopback_list;
2110}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002111
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002112static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002113 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002114{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002115 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002116}
2117
2118/* add the input-route to the given pin */
2119static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002120{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002121 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002122 int c, idx;
2123
2124 spec->inputs[spec->num_inputs].adc_idx = -1;
2125 spec->inputs[spec->num_inputs].pin = pin;
2126 for (c = 0; c < spec->num_adc_nids; c++) {
2127 if (spec->mux_nids[c]) {
2128 idx = get_connection_index(codec, spec->mux_nids[c],
2129 pin);
2130 if (idx < 0)
2131 continue;
2132 spec->inputs[spec->num_inputs].mux_idx = idx;
2133 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002134 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002135 continue;
2136 }
2137 spec->inputs[spec->num_inputs].adc_idx = c;
2138 /* Can primary ADC satisfy all inputs? */
2139 if (!spec->dyn_adc_switch &&
2140 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2141 snd_printd(KERN_INFO
2142 "via: dynamic ADC switching enabled\n");
2143 spec->dyn_adc_switch = 1;
2144 }
2145 return true;
2146 }
2147 return false;
2148}
2149
2150static int get_mux_nids(struct hda_codec *codec);
2151
2152/* parse input-routes; fill ADCs, MUXs and input-src entries */
2153static int parse_analog_inputs(struct hda_codec *codec)
2154{
2155 struct via_spec *spec = codec->spec;
2156 const struct auto_pin_cfg *cfg = &spec->autocfg;
2157 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002158
2159 err = via_fill_adcs(codec);
2160 if (err < 0)
2161 return err;
2162 err = get_mux_nids(codec);
2163 if (err < 0)
2164 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002165
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002166 /* fill all input-routes */
2167 for (i = 0; i < cfg->num_inputs; i++) {
2168 if (add_input_route(codec, cfg->inputs[i].pin))
2169 spec->inputs[spec->num_inputs++].label =
2170 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002171 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002172
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002173 /* check for internal loopback recording */
2174 if (spec->aa_mix_nid &&
2175 add_input_route(codec, spec->aa_mix_nid))
2176 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2177
2178 return 0;
2179}
2180
2181/* create analog-loopback volume/switch controls */
2182static int create_loopback_ctls(struct hda_codec *codec)
2183{
2184 struct via_spec *spec = codec->spec;
2185 const struct auto_pin_cfg *cfg = &spec->autocfg;
2186 const char *prev_label = NULL;
2187 int type_idx = 0;
2188 int i, j, err, idx;
2189
2190 if (!spec->aa_mix_nid)
2191 return 0;
2192
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002193 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002194 hda_nid_t pin = cfg->inputs[i].pin;
2195 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2196
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002197 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002198 type_idx++;
2199 else
2200 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002201 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002202 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2203 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002204 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002205 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002206 if (err < 0)
2207 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002208 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002209 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002210
2211 /* remember the label for smart51 control */
2212 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002213 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002214 spec->smart51_idxs[j] = idx;
2215 spec->smart51_labels[j] = label;
2216 break;
2217 }
2218 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002219 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002220 return 0;
2221}
2222
2223/* create mic-boost controls (if present) */
2224static int create_mic_boost_ctls(struct hda_codec *codec)
2225{
2226 struct via_spec *spec = codec->spec;
2227 const struct auto_pin_cfg *cfg = &spec->autocfg;
2228 int i, err;
2229
2230 for (i = 0; i < cfg->num_inputs; i++) {
2231 hda_nid_t pin = cfg->inputs[i].pin;
2232 unsigned int caps;
2233 const char *label;
2234 char name[32];
2235
2236 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2237 continue;
2238 caps = query_amp_caps(codec, pin, HDA_INPUT);
2239 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2240 continue;
2241 label = hda_get_autocfg_input_label(codec, cfg, i);
2242 snprintf(name, sizeof(name), "%s Boost Volume", label);
2243 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2244 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2245 if (err < 0)
2246 return err;
2247 }
2248 return 0;
2249}
2250
2251/* create capture and input-src controls for multiple streams */
2252static int create_multi_adc_ctls(struct hda_codec *codec)
2253{
2254 struct via_spec *spec = codec->spec;
2255 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002256
2257 /* create capture mixer elements */
2258 for (i = 0; i < spec->num_adc_nids; i++) {
2259 hda_nid_t adc = spec->adc_nids[i];
2260 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2261 "Capture Volume", i,
2262 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2263 HDA_INPUT));
2264 if (err < 0)
2265 return err;
2266 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2267 "Capture Switch", i,
2268 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2269 HDA_INPUT));
2270 if (err < 0)
2271 return err;
2272 }
2273
2274 /* input-source control */
2275 for (i = 0; i < spec->num_adc_nids; i++)
2276 if (!spec->mux_nids[i])
2277 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002278 err = create_input_src_ctls(codec, i);
2279 if (err < 0)
2280 return err;
2281 return 0;
2282}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002283
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002284/* bind capture volume/switch */
2285static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2286 HDA_BIND_VOL("Capture Volume", 0);
2287static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2288 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002289
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002290static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2291 struct hda_ctl_ops *ops)
2292{
2293 struct hda_bind_ctls *ctl;
2294 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002295
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002296 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2297 if (!ctl)
2298 return -ENOMEM;
2299 ctl->ops = ops;
2300 for (i = 0; i < spec->num_adc_nids; i++)
2301 ctl->values[i] =
2302 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2303 *ctl_ret = ctl;
2304 return 0;
2305}
2306
2307/* create capture and input-src controls for dynamic ADC-switch case */
2308static int create_dyn_adc_ctls(struct hda_codec *codec)
2309{
2310 struct via_spec *spec = codec->spec;
2311 struct snd_kcontrol_new *knew;
2312 int err;
2313
2314 /* set up the bind capture ctls */
2315 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2316 if (err < 0)
2317 return err;
2318 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2319 if (err < 0)
2320 return err;
2321
2322 /* create capture mixer elements */
2323 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2324 if (!knew)
2325 return -ENOMEM;
2326 knew->private_value = (long)spec->bind_cap_vol;
2327
2328 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2329 if (!knew)
2330 return -ENOMEM;
2331 knew->private_value = (long)spec->bind_cap_sw;
2332
2333 /* input-source control */
2334 err = create_input_src_ctls(codec, 1);
2335 if (err < 0)
2336 return err;
2337 return 0;
2338}
2339
2340/* parse and create capture-related stuff */
2341static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2342{
2343 struct via_spec *spec = codec->spec;
2344 int err;
2345
2346 err = parse_analog_inputs(codec);
2347 if (err < 0)
2348 return err;
2349 if (spec->dyn_adc_switch)
2350 err = create_dyn_adc_ctls(codec);
2351 else
2352 err = create_multi_adc_ctls(codec);
2353 if (err < 0)
2354 return err;
2355 err = create_loopback_ctls(codec);
2356 if (err < 0)
2357 return err;
2358 err = create_mic_boost_ctls(codec);
2359 if (err < 0)
2360 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002361 return 0;
2362}
2363
Harald Welte76d9b0d2008-09-09 15:50:37 +08002364static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2365{
2366 unsigned int def_conf;
2367 unsigned char seqassoc;
2368
Takashi Iwai2f334f92009-02-20 14:37:42 +01002369 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002370 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2371 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002372 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2373 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2374 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2375 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002376 }
2377
2378 return;
2379}
2380
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002381static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002382 struct snd_ctl_elem_value *ucontrol)
2383{
2384 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2385 struct via_spec *spec = codec->spec;
2386
2387 if (spec->codec_type != VT1708)
2388 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002389 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002390 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002391 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002392 return 0;
2393}
2394
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002395static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002396 struct snd_ctl_elem_value *ucontrol)
2397{
2398 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2399 struct via_spec *spec = codec->spec;
2400 int change;
2401
2402 if (spec->codec_type != VT1708)
2403 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002404 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002405 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002406 == !spec->vt1708_jack_detect;
2407 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002408 mute_aa_path(codec, 1);
2409 notify_aa_path_ctls(codec);
2410 }
2411 return change;
2412}
2413
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002414static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2415 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2416 .name = "Jack Detect",
2417 .count = 1,
2418 .info = snd_ctl_boolean_mono_info,
2419 .get = vt1708_jack_detect_get,
2420 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002421};
2422
Takashi Iwai12daef62011-06-18 17:45:49 +02002423static void fill_dig_outs(struct hda_codec *codec);
2424static void fill_dig_in(struct hda_codec *codec);
2425
2426static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002427{
2428 struct via_spec *spec = codec->spec;
2429 int err;
2430
2431 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2432 if (err < 0)
2433 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002434 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002435 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002436
Takashi Iwai4a796162011-06-17 17:53:38 +02002437 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002438 if (err < 0)
2439 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002440 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002441 if (err < 0)
2442 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002443 err = via_auto_create_speaker_ctls(codec);
2444 if (err < 0)
2445 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002446 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002447 if (err < 0)
2448 return err;
2449
2450 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2451
Takashi Iwai12daef62011-06-18 17:45:49 +02002452 fill_dig_outs(codec);
2453 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002454
Takashi Iwai603c4012008-07-30 15:01:44 +02002455 if (spec->kctls.list)
2456 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002457
Joseph Chanc577b8a2006-11-29 15:29:40 +01002458
Takashi Iwai8df2a312011-06-21 11:48:29 +02002459 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002460 err = via_hp_build(codec);
2461 if (err < 0)
2462 return err;
2463 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002464
Takashi Iwaif4a78282011-06-17 18:46:48 +02002465 err = via_smart51_build(codec);
2466 if (err < 0)
2467 return err;
2468
Takashi Iwai5d417622011-06-20 11:32:27 +02002469 /* assign slave outs */
2470 if (spec->slave_dig_outs[0])
2471 codec->slave_dig_outs = spec->slave_dig_outs;
2472
Joseph Chanc577b8a2006-11-29 15:29:40 +01002473 return 1;
2474}
2475
Takashi Iwai5d417622011-06-20 11:32:27 +02002476static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002477{
Lydia Wang25eaba22009-10-10 19:08:43 +08002478 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002479 if (spec->multiout.dig_out_nid)
2480 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2481 if (spec->slave_dig_outs[0])
2482 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2483}
Lydia Wang25eaba22009-10-10 19:08:43 +08002484
Takashi Iwai5d417622011-06-20 11:32:27 +02002485static void via_auto_init_dig_in(struct hda_codec *codec)
2486{
2487 struct via_spec *spec = codec->spec;
2488 if (!spec->dig_in_nid)
2489 return;
2490 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2491 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2492}
2493
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002494/* initialize the unsolicited events */
2495static void via_auto_init_unsol_event(struct hda_codec *codec)
2496{
2497 struct via_spec *spec = codec->spec;
2498 struct auto_pin_cfg *cfg = &spec->autocfg;
2499 unsigned int ev;
2500 int i;
2501
2502 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2503 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2504 AC_VERB_SET_UNSOLICITED_ENABLE,
2505 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2506
2507 if (cfg->speaker_pins[0])
2508 ev = VIA_LINE_EVENT;
2509 else
2510 ev = 0;
2511 for (i = 0; i < cfg->line_outs; i++) {
2512 if (cfg->line_out_pins[i] &&
2513 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002514 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002515 AC_VERB_SET_UNSOLICITED_ENABLE,
2516 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2517 }
2518
2519 for (i = 0; i < cfg->num_inputs; i++) {
2520 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2521 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2522 AC_VERB_SET_UNSOLICITED_ENABLE,
2523 AC_USRSP_EN | VIA_JACK_EVENT);
2524 }
2525}
2526
Takashi Iwai5d417622011-06-20 11:32:27 +02002527static int via_init(struct hda_codec *codec)
2528{
2529 struct via_spec *spec = codec->spec;
2530 int i;
2531
2532 for (i = 0; i < spec->num_iverbs; i++)
2533 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2534
Joseph Chanc577b8a2006-11-29 15:29:40 +01002535 via_auto_init_multi_out(codec);
2536 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002537 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002538 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002539 via_auto_init_dig_outs(codec);
2540 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002541
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002542 via_auto_init_unsol_event(codec);
2543
2544 via_hp_automute(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002545
Joseph Chanc577b8a2006-11-29 15:29:40 +01002546 return 0;
2547}
2548
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002549static void vt1708_update_hp_jack_state(struct work_struct *work)
2550{
2551 struct via_spec *spec = container_of(work, struct via_spec,
2552 vt1708_hp_work.work);
2553 if (spec->codec_type != VT1708)
2554 return;
2555 /* if jack state toggled */
2556 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002557 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002558 spec->vt1708_hp_present ^= 1;
2559 via_hp_automute(spec->codec);
2560 }
2561 vt1708_start_hp_work(spec);
2562}
2563
Takashi Iwai337b9d02009-07-07 18:18:59 +02002564static int get_mux_nids(struct hda_codec *codec)
2565{
2566 struct via_spec *spec = codec->spec;
2567 hda_nid_t nid, conn[8];
2568 unsigned int type;
2569 int i, n;
2570
2571 for (i = 0; i < spec->num_adc_nids; i++) {
2572 nid = spec->adc_nids[i];
2573 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002574 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002575 if (type == AC_WID_PIN)
2576 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002577 n = snd_hda_get_connections(codec, nid, conn,
2578 ARRAY_SIZE(conn));
2579 if (n <= 0)
2580 break;
2581 if (n > 1) {
2582 spec->mux_nids[i] = nid;
2583 break;
2584 }
2585 nid = conn[0];
2586 }
2587 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002588 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002589}
2590
Joseph Chanc577b8a2006-11-29 15:29:40 +01002591static int patch_vt1708(struct hda_codec *codec)
2592{
2593 struct via_spec *spec;
2594 int err;
2595
2596 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002597 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002598 if (spec == NULL)
2599 return -ENOMEM;
2600
Takashi Iwai620e2b22011-06-17 17:19:19 +02002601 spec->aa_mix_nid = 0x17;
2602
Takashi Iwai12daef62011-06-18 17:45:49 +02002603 /* Add HP and CD pin config connect bit re-config action */
2604 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2605 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2606
Joseph Chanc577b8a2006-11-29 15:29:40 +01002607 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002608 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002609 if (err < 0) {
2610 via_free(codec);
2611 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002612 }
2613
Takashi Iwai12daef62011-06-18 17:45:49 +02002614 /* add jack detect on/off control */
2615 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2616 return -ENOMEM;
2617
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002618 /* disable 32bit format on VT1708 */
2619 if (codec->vendor_id == 0x11061708)
2620 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002621
Lydia Wange322a362011-06-29 13:52:02 +08002622 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2623
Joseph Chanc577b8a2006-11-29 15:29:40 +01002624 codec->patch_ops = via_patch_ops;
2625
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002626 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002627 return 0;
2628}
2629
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002630static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002631{
2632 struct via_spec *spec;
2633 int err;
2634
2635 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002636 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002637 if (spec == NULL)
2638 return -ENOMEM;
2639
Takashi Iwai620e2b22011-06-17 17:19:19 +02002640 spec->aa_mix_nid = 0x18;
2641
Takashi Iwai12daef62011-06-18 17:45:49 +02002642 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002643 if (err < 0) {
2644 via_free(codec);
2645 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002646 }
2647
Joseph Chanc577b8a2006-11-29 15:29:40 +01002648 codec->patch_ops = via_patch_ops;
2649
Josepch Chanf7278fd2007-12-13 16:40:40 +01002650 return 0;
2651}
2652
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002653static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2654{
2655 struct via_spec *spec = codec->spec;
2656 int imux_is_smixer;
2657 unsigned int parm;
2658 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002659 if ((spec->codec_type != VT1708B_4CH) &&
2660 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002661 is_8ch = 1;
2662
2663 /* SW0 (17h) = stereo mixer */
2664 imux_is_smixer =
2665 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2666 == ((spec->codec_type == VT1708S) ? 5 : 0));
2667 /* inputs */
2668 /* PW 1/2/5 (1ah/1bh/1eh) */
2669 parm = AC_PWRST_D3;
2670 set_pin_power_state(codec, 0x1a, &parm);
2671 set_pin_power_state(codec, 0x1b, &parm);
2672 set_pin_power_state(codec, 0x1e, &parm);
2673 if (imux_is_smixer)
2674 parm = AC_PWRST_D0;
2675 /* SW0 (17h), AIW 0/1 (13h/14h) */
2676 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2677 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2678 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2679
2680 /* outputs */
2681 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2682 parm = AC_PWRST_D3;
2683 set_pin_power_state(codec, 0x19, &parm);
2684 if (spec->smart51_enabled)
2685 set_pin_power_state(codec, 0x1b, &parm);
2686 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2687 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2688
2689 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2690 if (is_8ch) {
2691 parm = AC_PWRST_D3;
2692 set_pin_power_state(codec, 0x22, &parm);
2693 if (spec->smart51_enabled)
2694 set_pin_power_state(codec, 0x1a, &parm);
2695 snd_hda_codec_write(codec, 0x26, 0,
2696 AC_VERB_SET_POWER_STATE, parm);
2697 snd_hda_codec_write(codec, 0x24, 0,
2698 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002699 } else if (codec->vendor_id == 0x11064397) {
2700 /* PW7(23h), SW2(27h), AOW2(25h) */
2701 parm = AC_PWRST_D3;
2702 set_pin_power_state(codec, 0x23, &parm);
2703 if (spec->smart51_enabled)
2704 set_pin_power_state(codec, 0x1a, &parm);
2705 snd_hda_codec_write(codec, 0x27, 0,
2706 AC_VERB_SET_POWER_STATE, parm);
2707 snd_hda_codec_write(codec, 0x25, 0,
2708 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002709 }
2710
2711 /* PW 3/4/7 (1ch/1dh/23h) */
2712 parm = AC_PWRST_D3;
2713 /* force to D0 for internal Speaker */
2714 set_pin_power_state(codec, 0x1c, &parm);
2715 set_pin_power_state(codec, 0x1d, &parm);
2716 if (is_8ch)
2717 set_pin_power_state(codec, 0x23, &parm);
2718
2719 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2720 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2721 imux_is_smixer ? AC_PWRST_D0 : parm);
2722 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2723 if (is_8ch) {
2724 snd_hda_codec_write(codec, 0x25, 0,
2725 AC_VERB_SET_POWER_STATE, parm);
2726 snd_hda_codec_write(codec, 0x27, 0,
2727 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002728 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2729 snd_hda_codec_write(codec, 0x25, 0,
2730 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002731}
2732
Lydia Wang518bf3b2009-10-10 19:07:29 +08002733static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002734static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002735{
2736 struct via_spec *spec;
2737 int err;
2738
Lydia Wang518bf3b2009-10-10 19:07:29 +08002739 if (get_codec_type(codec) == VT1708BCE)
2740 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002741
Josepch Chanf7278fd2007-12-13 16:40:40 +01002742 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002743 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002744 if (spec == NULL)
2745 return -ENOMEM;
2746
Takashi Iwai620e2b22011-06-17 17:19:19 +02002747 spec->aa_mix_nid = 0x16;
2748
Josepch Chanf7278fd2007-12-13 16:40:40 +01002749 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002750 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002751 if (err < 0) {
2752 via_free(codec);
2753 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002754 }
2755
Josepch Chanf7278fd2007-12-13 16:40:40 +01002756 codec->patch_ops = via_patch_ops;
2757
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002758 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2759
Josepch Chanf7278fd2007-12-13 16:40:40 +01002760 return 0;
2761}
2762
Harald Welted949cac2008-09-09 15:56:01 +08002763/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002764static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002765 /* Enable Mic Boost Volume backdoor */
2766 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002767 /* don't bybass mixer */
2768 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002769 { }
2770};
2771
Takashi Iwai9da29272009-05-07 16:31:14 +02002772/* fill out digital output widgets; one for master and one for slave outputs */
2773static void fill_dig_outs(struct hda_codec *codec)
2774{
2775 struct via_spec *spec = codec->spec;
2776 int i;
2777
2778 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2779 hda_nid_t nid;
2780 int conn;
2781
2782 nid = spec->autocfg.dig_out_pins[i];
2783 if (!nid)
2784 continue;
2785 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2786 if (conn < 1)
2787 continue;
2788 if (!spec->multiout.dig_out_nid)
2789 spec->multiout.dig_out_nid = nid;
2790 else {
2791 spec->slave_dig_outs[0] = nid;
2792 break; /* at most two dig outs */
2793 }
2794 }
2795}
2796
Takashi Iwai12daef62011-06-18 17:45:49 +02002797static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002798{
2799 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002800 hda_nid_t dig_nid;
2801 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002802
Takashi Iwai12daef62011-06-18 17:45:49 +02002803 if (!spec->autocfg.dig_in_pin)
2804 return;
Harald Welted949cac2008-09-09 15:56:01 +08002805
Takashi Iwai12daef62011-06-18 17:45:49 +02002806 dig_nid = codec->start_nid;
2807 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2808 unsigned int wcaps = get_wcaps(codec, dig_nid);
2809 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2810 continue;
2811 if (!(wcaps & AC_WCAP_DIGITAL))
2812 continue;
2813 if (!(wcaps & AC_WCAP_CONN_LIST))
2814 continue;
2815 err = get_connection_index(codec, dig_nid,
2816 spec->autocfg.dig_in_pin);
2817 if (err >= 0) {
2818 spec->dig_in_nid = dig_nid;
2819 break;
2820 }
2821 }
Harald Welted949cac2008-09-09 15:56:01 +08002822}
2823
Lydia Wang6369bcf2009-10-10 19:08:31 +08002824static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2825 int offset, int num_steps, int step_size)
2826{
2827 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2828 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2829 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2830 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2831 (0 << AC_AMPCAP_MUTE_SHIFT));
2832}
2833
Harald Welted949cac2008-09-09 15:56:01 +08002834static int patch_vt1708S(struct hda_codec *codec)
2835{
2836 struct via_spec *spec;
2837 int err;
2838
2839 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002840 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002841 if (spec == NULL)
2842 return -ENOMEM;
2843
Takashi Iwai620e2b22011-06-17 17:19:19 +02002844 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002845 override_mic_boost(codec, 0x1a, 0, 3, 40);
2846 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002847
Harald Welted949cac2008-09-09 15:56:01 +08002848 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002849 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002850 if (err < 0) {
2851 via_free(codec);
2852 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002853 }
2854
Takashi Iwai096a8852011-06-20 12:09:02 +02002855 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002856
Harald Welted949cac2008-09-09 15:56:01 +08002857 codec->patch_ops = via_patch_ops;
2858
Lydia Wang518bf3b2009-10-10 19:07:29 +08002859 /* correct names for VT1708BCE */
2860 if (get_codec_type(codec) == VT1708BCE) {
2861 kfree(codec->chip_name);
2862 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2863 snprintf(codec->bus->card->mixername,
2864 sizeof(codec->bus->card->mixername),
2865 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002866 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002867 /* correct names for VT1705 */
2868 if (codec->vendor_id == 0x11064397) {
2869 kfree(codec->chip_name);
2870 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2871 snprintf(codec->bus->card->mixername,
2872 sizeof(codec->bus->card->mixername),
2873 "%s %s", codec->vendor_name, codec->chip_name);
2874 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002875 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002876 return 0;
2877}
2878
2879/* Patch for VT1702 */
2880
Takashi Iwai096a8852011-06-20 12:09:02 +02002881static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002882 /* mixer enable */
2883 {0x1, 0xF88, 0x3},
2884 /* GPIO 0~2 */
2885 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002886 { }
2887};
2888
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002889static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2890{
2891 int imux_is_smixer =
2892 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2893 unsigned int parm;
2894 /* inputs */
2895 /* PW 1/2/5 (14h/15h/18h) */
2896 parm = AC_PWRST_D3;
2897 set_pin_power_state(codec, 0x14, &parm);
2898 set_pin_power_state(codec, 0x15, &parm);
2899 set_pin_power_state(codec, 0x18, &parm);
2900 if (imux_is_smixer)
2901 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2902 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2903 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2904 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2905 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2906 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2907
2908 /* outputs */
2909 /* PW 3/4 (16h/17h) */
2910 parm = AC_PWRST_D3;
2911 set_pin_power_state(codec, 0x17, &parm);
2912 set_pin_power_state(codec, 0x16, &parm);
2913 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2914 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2915 imux_is_smixer ? AC_PWRST_D0 : parm);
2916 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2917 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2918}
2919
Harald Welted949cac2008-09-09 15:56:01 +08002920static int patch_vt1702(struct hda_codec *codec)
2921{
2922 struct via_spec *spec;
2923 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002924
2925 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002926 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002927 if (spec == NULL)
2928 return -ENOMEM;
2929
Takashi Iwai620e2b22011-06-17 17:19:19 +02002930 spec->aa_mix_nid = 0x1a;
2931
Takashi Iwai12daef62011-06-18 17:45:49 +02002932 /* limit AA path volume to 0 dB */
2933 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2934 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2935 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2936 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2937 (1 << AC_AMPCAP_MUTE_SHIFT));
2938
Harald Welted949cac2008-09-09 15:56:01 +08002939 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002940 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002941 if (err < 0) {
2942 via_free(codec);
2943 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002944 }
2945
Takashi Iwai096a8852011-06-20 12:09:02 +02002946 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002947
Harald Welted949cac2008-09-09 15:56:01 +08002948 codec->patch_ops = via_patch_ops;
2949
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002950 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002951 return 0;
2952}
2953
Lydia Wangeb7188c2009-10-10 19:08:34 +08002954/* Patch for VT1718S */
2955
Takashi Iwai096a8852011-06-20 12:09:02 +02002956static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002957 /* Enable MW0 adjust Gain 5 */
2958 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002959 /* Enable Boost Volume backdoor */
2960 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002961
Lydia Wangeb7188c2009-10-10 19:08:34 +08002962 { }
2963};
2964
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002965static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2966{
2967 struct via_spec *spec = codec->spec;
2968 int imux_is_smixer;
2969 unsigned int parm;
2970 /* MUX6 (1eh) = stereo mixer */
2971 imux_is_smixer =
2972 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2973 /* inputs */
2974 /* PW 5/6/7 (29h/2ah/2bh) */
2975 parm = AC_PWRST_D3;
2976 set_pin_power_state(codec, 0x29, &parm);
2977 set_pin_power_state(codec, 0x2a, &parm);
2978 set_pin_power_state(codec, 0x2b, &parm);
2979 if (imux_is_smixer)
2980 parm = AC_PWRST_D0;
2981 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2982 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2983 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2984 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2985 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2986
2987 /* outputs */
2988 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2989 parm = AC_PWRST_D3;
2990 set_pin_power_state(codec, 0x27, &parm);
2991 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2992 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2993
2994 /* PW2 (26h), AOW2 (ah) */
2995 parm = AC_PWRST_D3;
2996 set_pin_power_state(codec, 0x26, &parm);
2997 if (spec->smart51_enabled)
2998 set_pin_power_state(codec, 0x2b, &parm);
2999 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
3000
3001 /* PW0 (24h), AOW0 (8h) */
3002 parm = AC_PWRST_D3;
3003 set_pin_power_state(codec, 0x24, &parm);
3004 if (!spec->hp_independent_mode) /* check for redirected HP */
3005 set_pin_power_state(codec, 0x28, &parm);
3006 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3007 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
3008 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
3009 imux_is_smixer ? AC_PWRST_D0 : parm);
3010
3011 /* PW1 (25h), AOW1 (9h) */
3012 parm = AC_PWRST_D3;
3013 set_pin_power_state(codec, 0x25, &parm);
3014 if (spec->smart51_enabled)
3015 set_pin_power_state(codec, 0x2a, &parm);
3016 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
3017
3018 if (spec->hp_independent_mode) {
3019 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3020 parm = AC_PWRST_D3;
3021 set_pin_power_state(codec, 0x28, &parm);
3022 snd_hda_codec_write(codec, 0x1b, 0,
3023 AC_VERB_SET_POWER_STATE, parm);
3024 snd_hda_codec_write(codec, 0x34, 0,
3025 AC_VERB_SET_POWER_STATE, parm);
3026 snd_hda_codec_write(codec, 0xc, 0,
3027 AC_VERB_SET_POWER_STATE, parm);
3028 }
3029}
3030
Lydia Wangeb7188c2009-10-10 19:08:34 +08003031static int patch_vt1718S(struct hda_codec *codec)
3032{
3033 struct via_spec *spec;
3034 int err;
3035
3036 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003037 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003038 if (spec == NULL)
3039 return -ENOMEM;
3040
Takashi Iwai620e2b22011-06-17 17:19:19 +02003041 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003042 override_mic_boost(codec, 0x2b, 0, 3, 40);
3043 override_mic_boost(codec, 0x29, 0, 3, 40);
Lydia Wangc4394f52011-07-04 16:54:15 +08003044 spec->dac_mixer_idx = 5;
Takashi Iwai620e2b22011-06-17 17:19:19 +02003045
Lydia Wangeb7188c2009-10-10 19:08:34 +08003046 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003047 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003048 if (err < 0) {
3049 via_free(codec);
3050 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003051 }
3052
Takashi Iwai096a8852011-06-20 12:09:02 +02003053 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003054
Lydia Wangeb7188c2009-10-10 19:08:34 +08003055 codec->patch_ops = via_patch_ops;
3056
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003057 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3058
Lydia Wangeb7188c2009-10-10 19:08:34 +08003059 return 0;
3060}
Lydia Wangf3db4232009-10-10 19:08:41 +08003061
3062/* Patch for VT1716S */
3063
3064static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3065 struct snd_ctl_elem_info *uinfo)
3066{
3067 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3068 uinfo->count = 1;
3069 uinfo->value.integer.min = 0;
3070 uinfo->value.integer.max = 1;
3071 return 0;
3072}
3073
3074static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3075 struct snd_ctl_elem_value *ucontrol)
3076{
3077 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3078 int index = 0;
3079
3080 index = snd_hda_codec_read(codec, 0x26, 0,
3081 AC_VERB_GET_CONNECT_SEL, 0);
3082 if (index != -1)
3083 *ucontrol->value.integer.value = index;
3084
3085 return 0;
3086}
3087
3088static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3089 struct snd_ctl_elem_value *ucontrol)
3090{
3091 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3092 struct via_spec *spec = codec->spec;
3093 int index = *ucontrol->value.integer.value;
3094
3095 snd_hda_codec_write(codec, 0x26, 0,
3096 AC_VERB_SET_CONNECT_SEL, index);
3097 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003098 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003099 return 1;
3100}
3101
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003102static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003103 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3104 {
3105 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3106 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003107 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003108 .count = 1,
3109 .info = vt1716s_dmic_info,
3110 .get = vt1716s_dmic_get,
3111 .put = vt1716s_dmic_put,
3112 },
3113 {} /* end */
3114};
3115
3116
3117/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003118static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003119 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3120 { } /* end */
3121};
3122
Takashi Iwai096a8852011-06-20 12:09:02 +02003123static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003124 /* Enable Boost Volume backdoor */
3125 {0x1, 0xf8a, 0x80},
3126 /* don't bybass mixer */
3127 {0x1, 0xf88, 0xc0},
3128 /* Enable mono output */
3129 {0x1, 0xf90, 0x08},
3130 { }
3131};
3132
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003133static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3134{
3135 struct via_spec *spec = codec->spec;
3136 int imux_is_smixer;
3137 unsigned int parm;
3138 unsigned int mono_out, present;
3139 /* SW0 (17h) = stereo mixer */
3140 imux_is_smixer =
3141 (snd_hda_codec_read(codec, 0x17, 0,
3142 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3143 /* inputs */
3144 /* PW 1/2/5 (1ah/1bh/1eh) */
3145 parm = AC_PWRST_D3;
3146 set_pin_power_state(codec, 0x1a, &parm);
3147 set_pin_power_state(codec, 0x1b, &parm);
3148 set_pin_power_state(codec, 0x1e, &parm);
3149 if (imux_is_smixer)
3150 parm = AC_PWRST_D0;
3151 /* SW0 (17h), AIW0(13h) */
3152 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3153 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3154
3155 parm = AC_PWRST_D3;
3156 set_pin_power_state(codec, 0x1e, &parm);
3157 /* PW11 (22h) */
3158 if (spec->dmic_enabled)
3159 set_pin_power_state(codec, 0x22, &parm);
3160 else
3161 snd_hda_codec_write(codec, 0x22, 0,
3162 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3163
3164 /* SW2(26h), AIW1(14h) */
3165 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3166 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3167
3168 /* outputs */
3169 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3170 parm = AC_PWRST_D3;
3171 set_pin_power_state(codec, 0x19, &parm);
3172 /* Smart 5.1 PW2(1bh) */
3173 if (spec->smart51_enabled)
3174 set_pin_power_state(codec, 0x1b, &parm);
3175 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3176 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3177
3178 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3179 parm = AC_PWRST_D3;
3180 set_pin_power_state(codec, 0x23, &parm);
3181 /* Smart 5.1 PW1(1ah) */
3182 if (spec->smart51_enabled)
3183 set_pin_power_state(codec, 0x1a, &parm);
3184 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3185
3186 /* Smart 5.1 PW5(1eh) */
3187 if (spec->smart51_enabled)
3188 set_pin_power_state(codec, 0x1e, &parm);
3189 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3190
3191 /* Mono out */
3192 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3193 present = snd_hda_jack_detect(codec, 0x1c);
3194
3195 if (present)
3196 mono_out = 0;
3197 else {
3198 present = snd_hda_jack_detect(codec, 0x1d);
3199 if (!spec->hp_independent_mode && present)
3200 mono_out = 0;
3201 else
3202 mono_out = 1;
3203 }
3204 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3205 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3206 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3207 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3208
3209 /* PW 3/4 (1ch/1dh) */
3210 parm = AC_PWRST_D3;
3211 set_pin_power_state(codec, 0x1c, &parm);
3212 set_pin_power_state(codec, 0x1d, &parm);
3213 /* HP Independent Mode, power on AOW3 */
3214 if (spec->hp_independent_mode)
3215 snd_hda_codec_write(codec, 0x25, 0,
3216 AC_VERB_SET_POWER_STATE, parm);
3217
3218 /* force to D0 for internal Speaker */
3219 /* MW0 (16h), AOW0 (10h) */
3220 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3221 imux_is_smixer ? AC_PWRST_D0 : parm);
3222 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3223 mono_out ? AC_PWRST_D0 : parm);
3224}
3225
Lydia Wangf3db4232009-10-10 19:08:41 +08003226static int patch_vt1716S(struct hda_codec *codec)
3227{
3228 struct via_spec *spec;
3229 int err;
3230
3231 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003232 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003233 if (spec == NULL)
3234 return -ENOMEM;
3235
Takashi Iwai620e2b22011-06-17 17:19:19 +02003236 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003237 override_mic_boost(codec, 0x1a, 0, 3, 40);
3238 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003239
Lydia Wangf3db4232009-10-10 19:08:41 +08003240 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003241 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003242 if (err < 0) {
3243 via_free(codec);
3244 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003245 }
3246
Takashi Iwai096a8852011-06-20 12:09:02 +02003247 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003248
Lydia Wangf3db4232009-10-10 19:08:41 +08003249 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3250 spec->num_mixers++;
3251
3252 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3253
3254 codec->patch_ops = via_patch_ops;
3255
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003256 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003257 return 0;
3258}
Lydia Wang25eaba22009-10-10 19:08:43 +08003259
3260/* for vt2002P */
3261
Takashi Iwai096a8852011-06-20 12:09:02 +02003262static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003263 /* Class-D speaker related verbs */
3264 {0x1, 0xfe0, 0x4},
3265 {0x1, 0xfe9, 0x80},
3266 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003267 /* Enable Boost Volume backdoor */
3268 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003269 /* Enable AOW0 to MW9 */
3270 {0x1, 0xfb8, 0x88},
3271 { }
3272};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003273
Takashi Iwai096a8852011-06-20 12:09:02 +02003274static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003275 /* Enable Boost Volume backdoor */
3276 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003277 /* Enable AOW0 to MW9 */
3278 {0x1, 0xfb8, 0x88},
3279 { }
3280};
Lydia Wang25eaba22009-10-10 19:08:43 +08003281
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003282static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3283{
3284 struct via_spec *spec = codec->spec;
3285 int imux_is_smixer;
3286 unsigned int parm;
3287 unsigned int present;
3288 /* MUX9 (1eh) = stereo mixer */
3289 imux_is_smixer =
3290 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3291 /* inputs */
3292 /* PW 5/6/7 (29h/2ah/2bh) */
3293 parm = AC_PWRST_D3;
3294 set_pin_power_state(codec, 0x29, &parm);
3295 set_pin_power_state(codec, 0x2a, &parm);
3296 set_pin_power_state(codec, 0x2b, &parm);
3297 parm = AC_PWRST_D0;
3298 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3299 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3300 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3301 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3302 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3303
3304 /* outputs */
3305 /* AOW0 (8h)*/
3306 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3307
Lydia Wang118909562011-03-23 17:57:34 +08003308 if (spec->codec_type == VT1802) {
3309 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3310 parm = AC_PWRST_D3;
3311 set_pin_power_state(codec, 0x28, &parm);
3312 snd_hda_codec_write(codec, 0x18, 0,
3313 AC_VERB_SET_POWER_STATE, parm);
3314 snd_hda_codec_write(codec, 0x38, 0,
3315 AC_VERB_SET_POWER_STATE, parm);
3316 } else {
3317 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3318 parm = AC_PWRST_D3;
3319 set_pin_power_state(codec, 0x26, &parm);
3320 snd_hda_codec_write(codec, 0x1c, 0,
3321 AC_VERB_SET_POWER_STATE, parm);
3322 snd_hda_codec_write(codec, 0x37, 0,
3323 AC_VERB_SET_POWER_STATE, parm);
3324 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003325
Lydia Wang118909562011-03-23 17:57:34 +08003326 if (spec->codec_type == VT1802) {
3327 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3328 parm = AC_PWRST_D3;
3329 set_pin_power_state(codec, 0x25, &parm);
3330 snd_hda_codec_write(codec, 0x15, 0,
3331 AC_VERB_SET_POWER_STATE, parm);
3332 snd_hda_codec_write(codec, 0x35, 0,
3333 AC_VERB_SET_POWER_STATE, parm);
3334 } else {
3335 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3336 parm = AC_PWRST_D3;
3337 set_pin_power_state(codec, 0x25, &parm);
3338 snd_hda_codec_write(codec, 0x19, 0,
3339 AC_VERB_SET_POWER_STATE, parm);
3340 snd_hda_codec_write(codec, 0x35, 0,
3341 AC_VERB_SET_POWER_STATE, parm);
3342 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003343
3344 if (spec->hp_independent_mode)
3345 snd_hda_codec_write(codec, 0x9, 0,
3346 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3347
3348 /* Class-D */
3349 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3350 present = snd_hda_jack_detect(codec, 0x25);
3351
3352 parm = AC_PWRST_D3;
3353 set_pin_power_state(codec, 0x24, &parm);
3354 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003355 if (spec->codec_type == VT1802)
3356 snd_hda_codec_write(codec, 0x14, 0,
3357 AC_VERB_SET_POWER_STATE, parm);
3358 else
3359 snd_hda_codec_write(codec, 0x18, 0,
3360 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003361 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3362
3363 /* Mono Out */
3364 present = snd_hda_jack_detect(codec, 0x26);
3365
3366 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003367 if (spec->codec_type == VT1802) {
3368 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3369 snd_hda_codec_write(codec, 0x33, 0,
3370 AC_VERB_SET_POWER_STATE, parm);
3371 snd_hda_codec_write(codec, 0x1c, 0,
3372 AC_VERB_SET_POWER_STATE, parm);
3373 snd_hda_codec_write(codec, 0x3c, 0,
3374 AC_VERB_SET_POWER_STATE, parm);
3375 } else {
3376 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3377 snd_hda_codec_write(codec, 0x31, 0,
3378 AC_VERB_SET_POWER_STATE, parm);
3379 snd_hda_codec_write(codec, 0x17, 0,
3380 AC_VERB_SET_POWER_STATE, parm);
3381 snd_hda_codec_write(codec, 0x3b, 0,
3382 AC_VERB_SET_POWER_STATE, parm);
3383 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003384 /* MW9 (21h) */
3385 if (imux_is_smixer || !is_aa_path_mute(codec))
3386 snd_hda_codec_write(codec, 0x21, 0,
3387 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3388 else
3389 snd_hda_codec_write(codec, 0x21, 0,
3390 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3391}
Lydia Wang25eaba22009-10-10 19:08:43 +08003392
3393/* patch for vt2002P */
3394static int patch_vt2002P(struct hda_codec *codec)
3395{
3396 struct via_spec *spec;
3397 int err;
3398
3399 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003400 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003401 if (spec == NULL)
3402 return -ENOMEM;
3403
Takashi Iwai620e2b22011-06-17 17:19:19 +02003404 spec->aa_mix_nid = 0x21;
Lydia Wang5c9a5612011-07-08 14:03:43 +08003405 spec->dac_mixer_idx = 3;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003406 override_mic_boost(codec, 0x2b, 0, 3, 40);
3407 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003408
Lydia Wang25eaba22009-10-10 19:08:43 +08003409 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003410 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003411 if (err < 0) {
3412 via_free(codec);
3413 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003414 }
3415
Lydia Wang118909562011-03-23 17:57:34 +08003416 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003417 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003418 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003419 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003420
Lydia Wang25eaba22009-10-10 19:08:43 +08003421 codec->patch_ops = via_patch_ops;
3422
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003423 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003424 return 0;
3425}
Lydia Wangab6734e2009-10-10 19:08:46 +08003426
3427/* for vt1812 */
3428
Takashi Iwai096a8852011-06-20 12:09:02 +02003429static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003430 /* Enable Boost Volume backdoor */
3431 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003432 /* Enable AOW0 to MW9 */
3433 {0x1, 0xfb8, 0xa8},
3434 { }
3435};
3436
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003437static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3438{
3439 struct via_spec *spec = codec->spec;
3440 int imux_is_smixer =
3441 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3442 unsigned int parm;
3443 unsigned int present;
3444 /* MUX10 (1eh) = stereo mixer */
3445 imux_is_smixer =
3446 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3447 /* inputs */
3448 /* PW 5/6/7 (29h/2ah/2bh) */
3449 parm = AC_PWRST_D3;
3450 set_pin_power_state(codec, 0x29, &parm);
3451 set_pin_power_state(codec, 0x2a, &parm);
3452 set_pin_power_state(codec, 0x2b, &parm);
3453 parm = AC_PWRST_D0;
3454 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3455 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3456 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3457 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3458 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3459
3460 /* outputs */
3461 /* AOW0 (8h)*/
3462 snd_hda_codec_write(codec, 0x8, 0,
3463 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3464
3465 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3466 parm = AC_PWRST_D3;
3467 set_pin_power_state(codec, 0x28, &parm);
3468 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3469 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3470
3471 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3472 parm = AC_PWRST_D3;
3473 set_pin_power_state(codec, 0x25, &parm);
3474 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3475 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3476 if (spec->hp_independent_mode)
3477 snd_hda_codec_write(codec, 0x9, 0,
3478 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3479
3480 /* Internal Speaker */
3481 /* PW0 (24h), MW0(14h), MUX0(34h) */
3482 present = snd_hda_jack_detect(codec, 0x25);
3483
3484 parm = AC_PWRST_D3;
3485 set_pin_power_state(codec, 0x24, &parm);
3486 if (present) {
3487 snd_hda_codec_write(codec, 0x14, 0,
3488 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3489 snd_hda_codec_write(codec, 0x34, 0,
3490 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3491 } else {
3492 snd_hda_codec_write(codec, 0x14, 0,
3493 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3494 snd_hda_codec_write(codec, 0x34, 0,
3495 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3496 }
3497
3498
3499 /* Mono Out */
3500 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3501 present = snd_hda_jack_detect(codec, 0x28);
3502
3503 parm = AC_PWRST_D3;
3504 set_pin_power_state(codec, 0x31, &parm);
3505 if (present) {
3506 snd_hda_codec_write(codec, 0x1c, 0,
3507 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3508 snd_hda_codec_write(codec, 0x3c, 0,
3509 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3510 snd_hda_codec_write(codec, 0x3e, 0,
3511 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3512 } else {
3513 snd_hda_codec_write(codec, 0x1c, 0,
3514 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3515 snd_hda_codec_write(codec, 0x3c, 0,
3516 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3517 snd_hda_codec_write(codec, 0x3e, 0,
3518 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3519 }
3520
3521 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3522 parm = AC_PWRST_D3;
3523 set_pin_power_state(codec, 0x33, &parm);
3524 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3525 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3526
3527}
Lydia Wangab6734e2009-10-10 19:08:46 +08003528
3529/* patch for vt1812 */
3530static int patch_vt1812(struct hda_codec *codec)
3531{
3532 struct via_spec *spec;
3533 int err;
3534
3535 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003536 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003537 if (spec == NULL)
3538 return -ENOMEM;
3539
Takashi Iwai620e2b22011-06-17 17:19:19 +02003540 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003541 override_mic_boost(codec, 0x2b, 0, 3, 40);
3542 override_mic_boost(codec, 0x29, 0, 3, 40);
Lydia Wang28dc10a2011-07-08 18:28:47 +08003543 spec->dac_mixer_idx = 5;
Takashi Iwai620e2b22011-06-17 17:19:19 +02003544
Lydia Wangab6734e2009-10-10 19:08:46 +08003545 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003546 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003547 if (err < 0) {
3548 via_free(codec);
3549 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003550 }
3551
Takashi Iwai096a8852011-06-20 12:09:02 +02003552 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003553
Lydia Wangab6734e2009-10-10 19:08:46 +08003554 codec->patch_ops = via_patch_ops;
3555
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003556 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003557 return 0;
3558}
3559
Joseph Chanc577b8a2006-11-29 15:29:40 +01003560/*
3561 * patch entries
3562 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003563static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003564 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3565 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3566 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3567 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3568 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003569 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003570 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003571 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003572 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003573 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003574 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003575 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003576 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003577 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003578 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003579 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003580 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003581 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003582 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003583 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003584 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003585 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003586 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003587 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003588 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003589 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003590 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003591 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003592 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003593 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003594 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003595 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003596 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003597 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003598 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003599 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003600 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003601 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003602 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003603 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003604 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003605 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003606 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003607 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003608 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003609 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003610 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003611 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003612 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003613 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003614 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003615 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003616 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003617 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003618 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003619 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003620 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003621 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003622 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003623 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003624 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003625 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003626 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003627 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003628 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003629 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003630 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003631 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003632 { .id = 0x11060428, .name = "VT1718S",
3633 .patch = patch_vt1718S},
3634 { .id = 0x11064428, .name = "VT1718S",
3635 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003636 { .id = 0x11060441, .name = "VT2020",
3637 .patch = patch_vt1718S},
3638 { .id = 0x11064441, .name = "VT1828S",
3639 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003640 { .id = 0x11060433, .name = "VT1716S",
3641 .patch = patch_vt1716S},
3642 { .id = 0x1106a721, .name = "VT1716S",
3643 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003644 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3645 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003646 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003647 { .id = 0x11060440, .name = "VT1818S",
3648 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003649 { .id = 0x11060446, .name = "VT1802",
3650 .patch = patch_vt2002P},
3651 { .id = 0x11068446, .name = "VT1802",
3652 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003653 {} /* terminator */
3654};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003655
3656MODULE_ALIAS("snd-hda-codec-id:1106*");
3657
3658static struct hda_codec_preset_list via_list = {
3659 .preset = snd_hda_preset_via,
3660 .owner = THIS_MODULE,
3661};
3662
3663MODULE_LICENSE("GPL");
3664MODULE_DESCRIPTION("VIA HD-audio codec");
3665
3666static int __init patch_via_init(void)
3667{
3668 return snd_hda_add_codec_preset(&via_list);
3669}
3670
3671static void __exit patch_via_exit(void)
3672{
3673 snd_hda_delete_codec_preset(&via_list);
3674}
3675
3676module_init(patch_via_init)
3677module_exit(patch_via_exit)