blob: 0da4f8ff54201e388a89fb6b841fb13fa099e8e1 [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);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200618
Joseph Chanc577b8a2006-11-29 15:29:40 +0100619static void via_auto_init_analog_input(struct hda_codec *codec)
620{
621 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200622 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200623 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200624 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200625 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100626
Takashi Iwai096a8852011-06-20 12:09:02 +0200627 /* init ADCs */
628 for (i = 0; i < spec->num_adc_nids; i++) {
629 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
630 AC_VERB_SET_AMP_GAIN_MUTE,
631 AMP_IN_UNMUTE(0));
632 }
633
634 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200635 for (i = 0; i < cfg->num_inputs; i++) {
636 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200637 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200638 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100639 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200640 ctl = PIN_VREF50;
641 else
642 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100643 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200644 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100645 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200646
647 /* init input-src */
648 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200649 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
650 if (spec->mux_nids[adc_idx]) {
651 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
652 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
653 AC_VERB_SET_CONNECT_SEL,
654 mux_idx);
655 }
656 if (spec->dyn_adc_switch)
657 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200658 }
659
660 /* init aa-mixer */
661 if (!spec->aa_mix_nid)
662 return;
663 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
664 ARRAY_SIZE(conn));
665 for (i = 0; i < num_conns; i++) {
666 unsigned int caps = get_wcaps(codec, conn[i]);
667 if (get_wcaps_type(caps) == AC_WID_PIN)
668 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
669 AC_VERB_SET_AMP_GAIN_MUTE,
670 AMP_IN_MUTE(i));
671 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100672}
Lydia Wangf5271102009-10-10 19:07:35 +0800673
674static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
675 unsigned int *affected_parm)
676{
677 unsigned parm;
678 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
679 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
680 >> AC_DEFCFG_MISC_SHIFT
681 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800682 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200683 unsigned present = 0;
684
685 no_presence |= spec->no_pin_power_ctl;
686 if (!no_presence)
687 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200688 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800689 || ((no_presence || present)
690 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800691 *affected_parm = AC_PWRST_D0; /* if it's connected */
692 parm = AC_PWRST_D0;
693 } else
694 parm = AC_PWRST_D3;
695
696 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
697}
698
Takashi Iwai24088a52011-06-17 16:59:21 +0200699static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
700 struct snd_ctl_elem_info *uinfo)
701{
702 static const char * const texts[] = {
703 "Disabled", "Enabled"
704 };
705
706 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
707 uinfo->count = 1;
708 uinfo->value.enumerated.items = 2;
709 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
710 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
711 strcpy(uinfo->value.enumerated.name,
712 texts[uinfo->value.enumerated.item]);
713 return 0;
714}
715
716static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
717 struct snd_ctl_elem_value *ucontrol)
718{
719 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
720 struct via_spec *spec = codec->spec;
721 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
722 return 0;
723}
724
725static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
726 struct snd_ctl_elem_value *ucontrol)
727{
728 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
729 struct via_spec *spec = codec->spec;
730 unsigned int val = !ucontrol->value.enumerated.item[0];
731
732 if (val == spec->no_pin_power_ctl)
733 return 0;
734 spec->no_pin_power_ctl = val;
735 set_widgets_power_state(codec);
736 return 1;
737}
738
739static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
740 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
741 .name = "Dynamic Power-Control",
742 .info = via_pin_power_ctl_info,
743 .get = via_pin_power_ctl_get,
744 .put = via_pin_power_ctl_put,
745};
746
747
Harald Welte0aa62ae2008-09-09 15:58:27 +0800748static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
749 struct snd_ctl_elem_info *uinfo)
750{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200751 static const char * const texts[] = { "OFF", "ON" };
752
753 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
754 uinfo->count = 1;
755 uinfo->value.enumerated.items = 2;
756 if (uinfo->value.enumerated.item >= 2)
757 uinfo->value.enumerated.item = 1;
758 strcpy(uinfo->value.enumerated.name,
759 texts[uinfo->value.enumerated.item]);
760 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800761}
762
763static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
764 struct snd_ctl_elem_value *ucontrol)
765{
766 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800767 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800768
Takashi Iwaiece8d042011-06-19 16:24:21 +0200769 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800770 return 0;
771}
772
Harald Welte0aa62ae2008-09-09 15:58:27 +0800773static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
774 struct snd_ctl_elem_value *ucontrol)
775{
776 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
777 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +0200778 int cur;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200779
Takashi Iwai25250502011-06-30 17:24:47 +0200780 /* no independent-hp status change during PCM playback is running */
781 if (spec->num_active_streams)
782 return -EBUSY;
783
784 cur = !!ucontrol->value.enumerated.item[0];
785 if (spec->hp_independent_mode == cur)
786 return 0;
787 spec->hp_independent_mode = cur;
788 if (cur) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200789 activate_output_path(codec, &spec->hp_dep_path, false, false);
790 activate_output_path(codec, &spec->hp_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200791 if (spec->hp_indep_shared)
792 activate_output_path(codec, &spec->out_path[HDA_SIDE],
793 false, false);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200794 } else {
795 activate_output_path(codec, &spec->hp_path, false, false);
796 activate_output_path(codec, &spec->hp_dep_path, true, false);
Takashi Iwai25250502011-06-30 17:24:47 +0200797 if (spec->hp_indep_shared)
798 activate_output_path(codec, &spec->out_path[HDA_SIDE],
799 true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200800 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800801
Lydia Wangce0e5a92011-03-22 16:22:37 +0800802 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800803 set_widgets_power_state(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200804 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800805}
806
Takashi Iwaiece8d042011-06-19 16:24:21 +0200807static const struct snd_kcontrol_new via_hp_mixer = {
808 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
809 .name = "Independent HP",
810 .info = via_independent_hp_info,
811 .get = via_independent_hp_get,
812 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800813};
814
Takashi Iwai3d83e572010-04-14 14:36:23 +0200815static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100816{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200817 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100818 struct snd_kcontrol_new *knew;
819 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100820
Takashi Iwaiece8d042011-06-19 16:24:21 +0200821 nid = spec->autocfg.hp_pins[0];
822 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200823 if (knew == NULL)
824 return -ENOMEM;
825
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100826 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100827
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100828 return 0;
829}
830
Lydia Wang1564b282009-10-10 19:07:52 +0800831static void notify_aa_path_ctls(struct hda_codec *codec)
832{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200833 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800834 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800835
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200836 for (i = 0; i < spec->smart51_nums; i++) {
837 struct snd_kcontrol *ctl;
838 struct snd_ctl_elem_id id;
839 memset(&id, 0, sizeof(id));
840 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
841 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800842 ctl = snd_hda_find_mixer_ctl(codec, id.name);
843 if (ctl)
844 snd_ctl_notify(codec->bus->card,
845 SNDRV_CTL_EVENT_MASK_VALUE,
846 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800847 }
848}
849
850static void mute_aa_path(struct hda_codec *codec, int mute)
851{
852 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200853 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800854 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200855
Lydia Wang1564b282009-10-10 19:07:52 +0800856 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200857 for (i = 0; i < spec->smart51_nums; i++) {
858 if (spec->smart51_idxs[i] < 0)
859 continue;
860 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
861 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800862 HDA_AMP_MUTE, val);
863 }
864}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200865
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200866static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
867{
868 struct via_spec *spec = codec->spec;
869 int i;
870
871 for (i = 0; i < spec->smart51_nums; i++)
872 if (spec->smart51_pins[i] == pin)
873 return true;
874 return false;
875}
876
Lydia Wang1564b282009-10-10 19:07:52 +0800877static int via_smart51_get(struct snd_kcontrol *kcontrol,
878 struct snd_ctl_elem_value *ucontrol)
879{
880 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
881 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800882
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200883 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800884 return 0;
885}
886
887static int via_smart51_put(struct snd_kcontrol *kcontrol,
888 struct snd_ctl_elem_value *ucontrol)
889{
890 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
891 struct via_spec *spec = codec->spec;
892 int out_in = *ucontrol->value.integer.value
893 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800894 int i;
895
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200896 for (i = 0; i < spec->smart51_nums; i++) {
897 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200898 unsigned int parm;
899
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200900 parm = snd_hda_codec_read(codec, nid, 0,
901 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
902 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
903 parm |= out_in;
904 snd_hda_codec_write(codec, nid, 0,
905 AC_VERB_SET_PIN_WIDGET_CONTROL,
906 parm);
907 if (out_in == AC_PINCTL_OUT_EN) {
908 mute_aa_path(codec, 1);
909 notify_aa_path_ctls(codec);
910 }
Lydia Wang1564b282009-10-10 19:07:52 +0800911 }
912 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800913 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800914 return 1;
915}
916
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200917static const struct snd_kcontrol_new via_smart51_mixer = {
918 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
919 .name = "Smart 5.1",
920 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200921 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200922 .get = via_smart51_get,
923 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800924};
925
Takashi Iwaif4a78282011-06-17 18:46:48 +0200926static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100927{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200928 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100929
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200930 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800931 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200932 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100933 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100934 return 0;
935}
936
Takashi Iwaiada509e2011-06-20 15:40:19 +0200937/* check AA path's mute status */
938static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800939{
Lydia Wangf5271102009-10-10 19:07:35 +0800940 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200941 const struct hda_amp_list *p;
942 int i, ch, v;
943
944 for (i = 0; i < spec->num_loopbacks; i++) {
945 p = &spec->loopback_list[i];
946 for (ch = 0; ch < 2; ch++) {
947 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
948 p->idx);
949 if (!(v & HDA_AMP_MUTE) && v > 0)
950 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800951 }
952 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200953 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800954}
955
956/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200957static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800958{
959 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200960 bool enable;
961 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800962
Takashi Iwaiada509e2011-06-20 15:40:19 +0200963 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800964
965 /* decide low current mode's verb & parameter */
966 switch (spec->codec_type) {
967 case VT1708B_8CH:
968 case VT1708B_4CH:
969 verb = 0xf70;
970 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
971 break;
972 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800973 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800974 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800975 verb = 0xf73;
976 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
977 break;
978 case VT1702:
979 verb = 0xf73;
980 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
981 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800982 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800983 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800984 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800985 verb = 0xf93;
986 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
987 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800988 default:
989 return; /* other codecs are not supported */
990 }
991 /* send verb */
992 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
993}
994
Joseph Chanc577b8a2006-11-29 15:29:40 +0100995/*
996 * generic initialization of ADC, input mixers and output mixers
997 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200998static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800999 /* power down jack detect function */
1000 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +01001001 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001002};
1003
Takashi Iwaiada509e2011-06-20 15:40:19 +02001004static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001005{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001006 struct via_spec *spec = codec->spec;
1007
1008 if (active)
1009 spec->num_active_streams++;
1010 else
1011 spec->num_active_streams--;
1012 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001013}
1014
Takashi Iwaiece8d042011-06-19 16:24:21 +02001015static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001016 struct hda_codec *codec,
1017 struct snd_pcm_substream *substream)
1018{
1019 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001020 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001021 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001022
Takashi Iwai25250502011-06-30 17:24:47 +02001023 spec->multiout.hp_nid = 0;
1024 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
1025 if (!spec->hp_independent_mode) {
1026 if (!spec->hp_indep_shared)
1027 spec->multiout.hp_nid = spec->hp_dac_nid;
1028 } else {
1029 if (spec->hp_indep_shared)
1030 spec->multiout.num_dacs = cfg->line_outs - 1;
1031 }
1032 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001033 set_stream_active(codec, true);
1034 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1035 hinfo);
1036 if (err < 0) {
1037 spec->multiout.hp_nid = 0;
1038 set_stream_active(codec, false);
1039 return err;
1040 }
1041 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001042}
1043
Takashi Iwaiece8d042011-06-19 16:24:21 +02001044static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001045 struct hda_codec *codec,
1046 struct snd_pcm_substream *substream)
1047{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001048 struct via_spec *spec = codec->spec;
1049
1050 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001051 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001052 return 0;
1053}
1054
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001055static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1056 struct hda_codec *codec,
1057 struct snd_pcm_substream *substream)
1058{
1059 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001060
Takashi Iwaiece8d042011-06-19 16:24:21 +02001061 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001062 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001063 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1064 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001065 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001066 return 0;
1067}
1068
1069static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1070 struct hda_codec *codec,
1071 struct snd_pcm_substream *substream)
1072{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001073 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001074 return 0;
1075}
1076
1077static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1078 struct hda_codec *codec,
1079 unsigned int stream_tag,
1080 unsigned int format,
1081 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001082{
1083 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001084
Takashi Iwaiece8d042011-06-19 16:24:21 +02001085 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1086 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001087 vt1708_start_hp_work(spec);
1088 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001089}
1090
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001091static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1092 struct hda_codec *codec,
1093 unsigned int stream_tag,
1094 unsigned int format,
1095 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001096{
1097 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001098
Takashi Iwaiece8d042011-06-19 16:24:21 +02001099 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1100 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001101 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001102 return 0;
1103}
1104
1105static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1106 struct hda_codec *codec,
1107 struct snd_pcm_substream *substream)
1108{
1109 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001110
Takashi Iwaiece8d042011-06-19 16:24:21 +02001111 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001112 vt1708_stop_hp_work(spec);
1113 return 0;
1114}
1115
1116static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1117 struct hda_codec *codec,
1118 struct snd_pcm_substream *substream)
1119{
1120 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001121
Takashi Iwaiece8d042011-06-19 16:24:21 +02001122 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001123 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001124 return 0;
1125}
1126
Joseph Chanc577b8a2006-11-29 15:29:40 +01001127/*
1128 * Digital out
1129 */
1130static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1131 struct hda_codec *codec,
1132 struct snd_pcm_substream *substream)
1133{
1134 struct via_spec *spec = codec->spec;
1135 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1136}
1137
1138static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1139 struct hda_codec *codec,
1140 struct snd_pcm_substream *substream)
1141{
1142 struct via_spec *spec = codec->spec;
1143 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1144}
1145
Harald Welte5691ec72008-09-15 22:42:26 +08001146static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001147 struct hda_codec *codec,
1148 unsigned int stream_tag,
1149 unsigned int format,
1150 struct snd_pcm_substream *substream)
1151{
1152 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001153 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1154 stream_tag, format, substream);
1155}
Harald Welte5691ec72008-09-15 22:42:26 +08001156
Takashi Iwai9da29272009-05-07 16:31:14 +02001157static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1158 struct hda_codec *codec,
1159 struct snd_pcm_substream *substream)
1160{
1161 struct via_spec *spec = codec->spec;
1162 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001163 return 0;
1164}
1165
Joseph Chanc577b8a2006-11-29 15:29:40 +01001166/*
1167 * Analog capture
1168 */
1169static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1170 struct hda_codec *codec,
1171 unsigned int stream_tag,
1172 unsigned int format,
1173 struct snd_pcm_substream *substream)
1174{
1175 struct via_spec *spec = codec->spec;
1176
1177 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1178 stream_tag, 0, format);
1179 return 0;
1180}
1181
1182static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1183 struct hda_codec *codec,
1184 struct snd_pcm_substream *substream)
1185{
1186 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001187 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001188 return 0;
1189}
1190
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001191/* analog capture with dynamic ADC switching */
1192static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1193 struct hda_codec *codec,
1194 unsigned int stream_tag,
1195 unsigned int format,
1196 struct snd_pcm_substream *substream)
1197{
1198 struct via_spec *spec = codec->spec;
1199 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1200
1201 spec->cur_adc = spec->adc_nids[adc_idx];
1202 spec->cur_adc_stream_tag = stream_tag;
1203 spec->cur_adc_format = format;
1204 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
1205 return 0;
1206}
1207
1208static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1209 struct hda_codec *codec,
1210 struct snd_pcm_substream *substream)
1211{
1212 struct via_spec *spec = codec->spec;
1213
1214 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1215 spec->cur_adc = 0;
1216 return 0;
1217}
1218
1219/* re-setup the stream if running; called from input-src put */
1220static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1221{
1222 struct via_spec *spec = codec->spec;
1223 int adc_idx = spec->inputs[cur].adc_idx;
1224 hda_nid_t adc = spec->adc_nids[adc_idx];
1225
1226 if (spec->cur_adc && spec->cur_adc != adc) {
1227 /* stream is running, let's swap the current ADC */
1228 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1229 spec->cur_adc = adc;
1230 snd_hda_codec_setup_stream(codec, adc,
1231 spec->cur_adc_stream_tag, 0,
1232 spec->cur_adc_format);
1233 return true;
1234 }
1235 return false;
1236}
1237
Takashi Iwai9af74212011-06-18 16:17:45 +02001238static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001239 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001240 .channels_min = 2,
1241 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001242 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001243 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001244 .open = via_playback_multi_pcm_open,
1245 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001246 .prepare = via_playback_multi_pcm_prepare,
1247 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001248 },
1249};
1250
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001251static const struct hda_pcm_stream via_pcm_hp_playback = {
1252 .substreams = 1,
1253 .channels_min = 2,
1254 .channels_max = 2,
1255 /* NID is set in via_build_pcms */
1256 .ops = {
1257 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001258 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001259 .prepare = via_playback_hp_pcm_prepare,
1260 .cleanup = via_playback_hp_pcm_cleanup
1261 },
1262};
1263
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001264static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001265 .substreams = 1,
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001266 .channels_min = 2,
1267 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001268 /* NID is set in via_build_pcms */
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001269 /* We got noisy outputs on the right channel on VT1708 when
1270 * 24bit samples are used. Until any workaround is found,
1271 * disable the 24bit format, so far.
1272 */
1273 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1274 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001275 .open = via_playback_multi_pcm_open,
1276 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001277 .prepare = via_playback_multi_pcm_prepare,
1278 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b5622008-05-23 17:50:27 +02001279 },
1280};
1281
Takashi Iwai9af74212011-06-18 16:17:45 +02001282static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001283 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001284 .channels_min = 2,
1285 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001286 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001287 .ops = {
1288 .prepare = via_capture_pcm_prepare,
1289 .cleanup = via_capture_pcm_cleanup
1290 },
1291};
1292
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001293static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1294 .substreams = 1,
1295 .channels_min = 2,
1296 .channels_max = 2,
1297 /* NID is set in via_build_pcms */
1298 .ops = {
1299 .prepare = via_dyn_adc_capture_pcm_prepare,
1300 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1301 },
1302};
1303
Takashi Iwai9af74212011-06-18 16:17:45 +02001304static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001305 .substreams = 1,
1306 .channels_min = 2,
1307 .channels_max = 2,
1308 /* NID is set in via_build_pcms */
1309 .ops = {
1310 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001311 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001312 .prepare = via_dig_playback_pcm_prepare,
1313 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001314 },
1315};
1316
Takashi Iwai9af74212011-06-18 16:17:45 +02001317static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001318 .substreams = 1,
1319 .channels_min = 2,
1320 .channels_max = 2,
1321};
1322
Takashi Iwai370bafb2011-06-20 12:47:45 +02001323/*
1324 * slave controls for virtual master
1325 */
1326static const char * const via_slave_vols[] = {
1327 "Front Playback Volume",
1328 "Surround Playback Volume",
1329 "Center Playback Volume",
1330 "LFE Playback Volume",
1331 "Side Playback Volume",
1332 "Headphone Playback Volume",
1333 "Speaker Playback Volume",
1334 NULL,
1335};
1336
1337static const char * const via_slave_sws[] = {
1338 "Front Playback Switch",
1339 "Surround Playback Switch",
1340 "Center Playback Switch",
1341 "LFE Playback Switch",
1342 "Side Playback Switch",
1343 "Headphone Playback Switch",
1344 "Speaker Playback Switch",
1345 NULL,
1346};
1347
Joseph Chanc577b8a2006-11-29 15:29:40 +01001348static int via_build_controls(struct hda_codec *codec)
1349{
1350 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001351 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001352 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001353
Takashi Iwai24088a52011-06-17 16:59:21 +02001354 if (spec->set_widgets_power_state)
1355 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1356 return -ENOMEM;
1357
Joseph Chanc577b8a2006-11-29 15:29:40 +01001358 for (i = 0; i < spec->num_mixers; i++) {
1359 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1360 if (err < 0)
1361 return err;
1362 }
1363
1364 if (spec->multiout.dig_out_nid) {
1365 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001366 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001367 spec->multiout.dig_out_nid);
1368 if (err < 0)
1369 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001370 err = snd_hda_create_spdif_share_sw(codec,
1371 &spec->multiout);
1372 if (err < 0)
1373 return err;
1374 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001375 }
1376 if (spec->dig_in_nid) {
1377 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1378 if (err < 0)
1379 return err;
1380 }
Lydia Wang17314372009-10-10 19:07:37 +08001381
Takashi Iwai370bafb2011-06-20 12:47:45 +02001382 /* if we have no master control, let's create it */
1383 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1384 unsigned int vmaster_tlv[4];
1385 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1386 HDA_OUTPUT, vmaster_tlv);
1387 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1388 vmaster_tlv, via_slave_vols);
1389 if (err < 0)
1390 return err;
1391 }
1392 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1393 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1394 NULL, via_slave_sws);
1395 if (err < 0)
1396 return err;
1397 }
1398
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001399 /* assign Capture Source enums to NID */
1400 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1401 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001402 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001403 if (err < 0)
1404 return err;
1405 }
1406
Lydia Wang17314372009-10-10 19:07:37 +08001407 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001408 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001409 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001410
Takashi Iwai603c4012008-07-30 15:01:44 +02001411 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001412 return 0;
1413}
1414
1415static int via_build_pcms(struct hda_codec *codec)
1416{
1417 struct via_spec *spec = codec->spec;
1418 struct hda_pcm *info = spec->pcm_rec;
1419
1420 codec->num_pcms = 1;
1421 codec->pcm_info = info;
1422
Takashi Iwai82673bc2011-06-17 16:24:21 +02001423 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1424 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001425 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001426
1427 if (!spec->stream_analog_playback)
1428 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001429 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001430 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001431 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1432 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001433 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1434 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001435
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001436 if (!spec->stream_analog_capture) {
1437 if (spec->dyn_adc_switch)
1438 spec->stream_analog_capture =
1439 &via_pcm_dyn_adc_analog_capture;
1440 else
1441 spec->stream_analog_capture = &via_pcm_analog_capture;
1442 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001443 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1444 *spec->stream_analog_capture;
1445 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001446 if (!spec->dyn_adc_switch)
1447 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1448 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001449
1450 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1451 codec->num_pcms++;
1452 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001453 snprintf(spec->stream_name_digital,
1454 sizeof(spec->stream_name_digital),
1455 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001456 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001457 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001458 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001459 if (!spec->stream_digital_playback)
1460 spec->stream_digital_playback =
1461 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001462 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001463 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001464 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1465 spec->multiout.dig_out_nid;
1466 }
1467 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001468 if (!spec->stream_digital_capture)
1469 spec->stream_digital_capture =
1470 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001471 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001472 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001473 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1474 spec->dig_in_nid;
1475 }
1476 }
1477
Takashi Iwaiece8d042011-06-19 16:24:21 +02001478 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001479 codec->num_pcms++;
1480 info++;
1481 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1482 "%s HP", codec->chip_name);
1483 info->name = spec->stream_name_hp;
1484 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1485 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001486 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001487 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001488 return 0;
1489}
1490
1491static void via_free(struct hda_codec *codec)
1492{
1493 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001494
1495 if (!spec)
1496 return;
1497
Takashi Iwai603c4012008-07-30 15:01:44 +02001498 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001499 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001500 kfree(spec->bind_cap_vol);
1501 kfree(spec->bind_cap_sw);
1502 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001503}
1504
Takashi Iwai64be2852011-06-17 16:51:39 +02001505/* mute/unmute outputs */
1506static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1507 hda_nid_t *pins, bool mute)
1508{
1509 int i;
1510 for (i = 0; i < num_pins; i++)
1511 snd_hda_codec_write(codec, pins[i], 0,
1512 AC_VERB_SET_PIN_WIDGET_CONTROL,
1513 mute ? 0 : PIN_OUT);
1514}
1515
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001516/* mute internal speaker if line-out is plugged */
1517static void via_line_automute(struct hda_codec *codec, int present)
1518{
1519 struct via_spec *spec = codec->spec;
1520
1521 if (!spec->autocfg.speaker_outs)
1522 return;
1523 if (!present)
1524 present = snd_hda_jack_detect(codec,
1525 spec->autocfg.line_out_pins[0]);
1526 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1527 spec->autocfg.speaker_pins,
1528 present);
1529}
1530
Harald Welte69e52a82008-09-09 15:57:32 +08001531/* mute internal speaker if HP is plugged */
1532static void via_hp_automute(struct hda_codec *codec)
1533{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001534 int present = 0;
Harald Welte69e52a82008-09-09 15:57:32 +08001535 struct via_spec *spec = codec->spec;
1536
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001537 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0]) {
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001538 int nums;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001539 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +02001540 if (spec->smart51_enabled)
1541 nums = spec->autocfg.line_outs + spec->smart51_nums;
1542 else
1543 nums = spec->autocfg.line_outs;
1544 toggle_output_mutes(codec, nums,
Takashi Iwai64be2852011-06-17 16:51:39 +02001545 spec->autocfg.line_out_pins,
1546 present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001547 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001548 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001549}
1550
Harald Welte69e52a82008-09-09 15:57:32 +08001551static void via_gpio_control(struct hda_codec *codec)
1552{
1553 unsigned int gpio_data;
1554 unsigned int vol_counter;
1555 unsigned int vol;
1556 unsigned int master_vol;
1557
1558 struct via_spec *spec = codec->spec;
1559
1560 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1561 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1562
1563 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1564 0xF84, 0) & 0x3F0000) >> 16;
1565
1566 vol = vol_counter & 0x1F;
1567 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1568 AC_VERB_GET_AMP_GAIN_MUTE,
1569 AC_AMP_GET_INPUT);
1570
1571 if (gpio_data == 0x02) {
1572 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001573 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1574 AC_VERB_SET_PIN_WIDGET_CONTROL,
1575 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001576 if (vol_counter & 0x20) {
1577 /* decrease volume */
1578 if (vol > master_vol)
1579 vol = master_vol;
1580 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1581 0, HDA_AMP_VOLMASK,
1582 master_vol-vol);
1583 } else {
1584 /* increase volume */
1585 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1586 HDA_AMP_VOLMASK,
1587 ((master_vol+vol) > 0x2A) ? 0x2A :
1588 (master_vol+vol));
1589 }
1590 } else if (!(gpio_data & 0x02)) {
1591 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001592 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1593 AC_VERB_SET_PIN_WIDGET_CONTROL,
1594 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001595 }
1596}
1597
1598/* unsolicited event for jack sensing */
1599static void via_unsol_event(struct hda_codec *codec,
1600 unsigned int res)
1601{
1602 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001603
Lydia Wanga34df192009-10-10 19:08:01 +08001604 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001605 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001606
1607 res &= ~VIA_JACK_EVENT;
1608
1609 if (res == VIA_HP_EVENT)
1610 via_hp_automute(codec);
1611 else if (res == VIA_GPIO_EVENT)
1612 via_gpio_control(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001613 else if (res == VIA_LINE_EVENT)
1614 via_line_automute(codec, false);
Harald Welte69e52a82008-09-09 15:57:32 +08001615}
1616
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001617#ifdef SND_HDA_NEEDS_RESUME
1618static int via_suspend(struct hda_codec *codec, pm_message_t state)
1619{
1620 struct via_spec *spec = codec->spec;
1621 vt1708_stop_hp_work(spec);
1622 return 0;
1623}
1624#endif
1625
Takashi Iwaicb53c622007-08-10 17:21:45 +02001626#ifdef CONFIG_SND_HDA_POWER_SAVE
1627static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1628{
1629 struct via_spec *spec = codec->spec;
1630 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1631}
1632#endif
1633
Joseph Chanc577b8a2006-11-29 15:29:40 +01001634/*
1635 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001636
1637static int via_init(struct hda_codec *codec);
1638
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001639static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001640 .build_controls = via_build_controls,
1641 .build_pcms = via_build_pcms,
1642 .init = via_init,
1643 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001644 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001645#ifdef SND_HDA_NEEDS_RESUME
1646 .suspend = via_suspend,
1647#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001648#ifdef CONFIG_SND_HDA_POWER_SAVE
1649 .check_power_status = via_check_power_status,
1650#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001651};
1652
Takashi Iwai4a796162011-06-17 17:53:38 +02001653static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001654{
Takashi Iwai4a796162011-06-17 17:53:38 +02001655 struct via_spec *spec = codec->spec;
1656 int i;
1657
1658 for (i = 0; i < spec->multiout.num_dacs; i++) {
1659 if (spec->multiout.dac_nids[i] == dac)
1660 return false;
1661 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001662 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001663 return false;
1664 return true;
1665}
1666
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001667static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai4a796162011-06-17 17:53:38 +02001668 hda_nid_t target_dac, struct nid_path *path,
1669 int depth, int wid_type)
1670{
1671 hda_nid_t conn[8];
1672 int i, nums;
1673
1674 nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
1675 for (i = 0; i < nums; i++) {
1676 if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
1677 continue;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001678 if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
1679 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001680 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001681 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001682 return false;
1683 for (i = 0; i < nums; i++) {
1684 unsigned int type;
1685 type = get_wcaps_type(get_wcaps(codec, conn[i]));
1686 if (type == AC_WID_AUD_OUT ||
1687 (wid_type != -1 && type != wid_type))
1688 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001689 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001690 path, depth + 1, AC_WID_AUD_SEL))
1691 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001692 }
1693 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001694
1695 found:
1696 path->path[path->depth] = conn[i];
1697 path->idx[path->depth] = i;
1698 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1699 path->multi[path->depth] = 1;
1700 path->depth++;
1701 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001702}
1703
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001704static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
1705 hda_nid_t target_dac, struct nid_path *path)
1706{
1707 if (__parse_output_path(codec, nid, target_dac, path, 1, -1)) {
1708 path->path[path->depth] = nid;
1709 path->depth++;
1710 return true;
1711 }
1712 return false;
1713}
1714
Takashi Iwai4a796162011-06-17 17:53:38 +02001715static int via_auto_fill_dac_nids(struct hda_codec *codec)
1716{
1717 struct via_spec *spec = codec->spec;
1718 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001719 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001720 hda_nid_t nid;
1721
Joseph Chanc577b8a2006-11-29 15:29:40 +01001722 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001723 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001724 for (i = 0; i < cfg->line_outs; i++) {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001725 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001726 if (!nid)
1727 continue;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001728 if (parse_output_path(codec, nid, 0, &spec->out_path[i])) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001729 spec->private_dac_nids[i] = spec->out_path[i].path[0];
Lydia Wang5c9a5612011-07-08 14:03:43 +08001730 dac_num++;
1731 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001732 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001733 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001734 return 0;
1735}
1736
Takashi Iwai4a796162011-06-17 17:53:38 +02001737static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001738 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001739{
Takashi Iwai4a796162011-06-17 17:53:38 +02001740 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001741 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001742 hda_nid_t dac, pin, sel, nid;
1743 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001744
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001745 dac = check_dac ? path->path[0] : 0;
1746 pin = path->path[path->depth - 1];
1747 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001748
Takashi Iwai8df2a312011-06-21 11:48:29 +02001749 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001750 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001751 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001752 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001753 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1754 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001755 else
1756 nid = 0;
1757 if (nid) {
1758 sprintf(name, "%s Playback Volume", pfx);
1759 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001760 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001761 if (err < 0)
1762 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001763 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001764 }
1765
Takashi Iwai8df2a312011-06-21 11:48:29 +02001766 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001767 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001768 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001769 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001770 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1771 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001772 else
1773 nid = 0;
1774 if (nid) {
1775 sprintf(name, "%s Playback Switch", pfx);
1776 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1777 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1778 if (err < 0)
1779 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001780 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001781 }
1782 return 0;
1783}
1784
Takashi Iwaif4a78282011-06-17 18:46:48 +02001785static void mangle_smart51(struct hda_codec *codec)
1786{
1787 struct via_spec *spec = codec->spec;
1788 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001789 struct auto_pin_cfg_item *ins = cfg->inputs;
1790 int i, j, nums, attr;
1791 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001792
Takashi Iwai0f98c242011-06-21 12:51:33 +02001793 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1794 nums = 0;
1795 for (i = 0; i < cfg->num_inputs; i++) {
1796 unsigned int def;
1797 if (ins[i].type > AUTO_PIN_LINE_IN)
1798 continue;
1799 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1800 if (snd_hda_get_input_pin_attr(def) != attr)
1801 continue;
1802 for (j = 0; j < nums; j++)
1803 if (ins[pins[j]].type < ins[i].type) {
1804 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001805 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001806 break;
1807 }
1808 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001809 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001810 }
1811 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001812 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001813 for (i = 0; i < nums; i++) {
1814 hda_nid_t pin = ins[pins[i]].pin;
1815 spec->smart51_pins[spec->smart51_nums++] = pin;
1816 cfg->line_out_pins[cfg->line_outs++] = pin;
1817 if (cfg->line_outs == 3)
1818 break;
1819 }
1820 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001821 }
1822}
1823
Takashi Iwai4a796162011-06-17 17:53:38 +02001824/* add playback controls from the parsed DAC table */
1825static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1826{
1827 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001828 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiea734962011-01-17 11:29:34 +01001829 static const char * const chname[4] = {
1830 "Front", "Surround", "C/LFE", "Side"
1831 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001832 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001833 int old_line_outs;
1834
1835 /* check smart51 */
1836 old_line_outs = cfg->line_outs;
1837 if (cfg->line_outs == 1)
1838 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001839
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001840 err = via_auto_fill_dac_nids(codec);
1841 if (err < 0)
1842 return err;
1843
Lydia Wang5c9a5612011-07-08 14:03:43 +08001844 if (spec->multiout.num_dacs < 3) {
1845 spec->smart51_nums = 0;
1846 cfg->line_outs = old_line_outs;
1847 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001848 for (i = 0; i < cfg->line_outs; i++) {
1849 hda_nid_t pin, dac;
1850 pin = cfg->line_out_pins[i];
1851 dac = spec->multiout.dac_nids[i];
1852 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001853 continue;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001854 if (i == HDA_CLFE) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001855 err = create_ch_ctls(codec, "Center", 1, true,
1856 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001857 if (err < 0)
1858 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001859 err = create_ch_ctls(codec, "LFE", 2, true,
1860 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001861 if (err < 0)
1862 return err;
1863 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001864 const char *pfx = chname[i];
1865 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1866 cfg->line_outs == 1)
1867 pfx = "Speaker";
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001868 err = create_ch_ctls(codec, pfx, 3, true,
1869 &spec->out_path[i]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001870 if (err < 0)
1871 return err;
1872 }
1873 }
1874
Takashi Iwai4a796162011-06-17 17:53:38 +02001875 idx = get_connection_index(codec, spec->aa_mix_nid,
1876 spec->multiout.dac_nids[0]);
Lydia Wangc4394f52011-07-04 16:54:15 +08001877 if (idx < 0 && spec->dac_mixer_idx)
1878 idx = spec->dac_mixer_idx;
Takashi Iwai4a796162011-06-17 17:53:38 +02001879 if (idx >= 0) {
1880 /* add control to mixer */
1881 err = via_add_control(spec, VIA_CTL_WIDGET_VOL,
1882 "PCM Playback Volume",
1883 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1884 idx, HDA_INPUT));
1885 if (err < 0)
1886 return err;
1887 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE,
1888 "PCM Playback Switch",
1889 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1890 idx, HDA_INPUT));
1891 if (err < 0)
1892 return err;
1893 }
1894
Takashi Iwaif4a78282011-06-17 18:46:48 +02001895 cfg->line_outs = old_line_outs;
1896
Joseph Chanc577b8a2006-11-29 15:29:40 +01001897 return 0;
1898}
1899
Takashi Iwai4a796162011-06-17 17:53:38 +02001900static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001901{
Takashi Iwai4a796162011-06-17 17:53:38 +02001902 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001903 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001904 bool check_dac;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001905 int err;
1906
1907 if (!pin)
1908 return 0;
1909
Takashi Iwai8df2a312011-06-21 11:48:29 +02001910 if (parse_output_path(codec, pin, 0, &spec->hp_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001911 spec->hp_dac_nid = spec->hp_path.path[0];
Takashi Iwai25250502011-06-30 17:24:47 +02001912 else if (spec->multiout.dac_nids[HDA_SIDE] &&
1913 parse_output_path(codec, pin,
1914 spec->multiout.dac_nids[HDA_SIDE],
1915 &spec->hp_path)) {
1916 spec->hp_dac_nid = spec->hp_path.path[0];
1917 spec->hp_indep_shared = true;
Lydia Wanga2a870c2011-07-08 14:04:33 +08001918 } else if (spec->multiout.dac_nids[HDA_CLFE] &&
1919 parse_output_path(codec, pin,
1920 spec->multiout.dac_nids[HDA_CLFE],
1921 &spec->hp_path)) {
1922 spec->hp_dac_nid = spec->hp_path.path[0];
1923 spec->hp_indep_shared = true;
Takashi Iwai25250502011-06-30 17:24:47 +02001924 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001925
Takashi Iwaiece8d042011-06-19 16:24:21 +02001926 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001927 &spec->hp_dep_path) &&
Takashi Iwaiece8d042011-06-19 16:24:21 +02001928 !spec->hp_dac_nid)
1929 return 0;
1930
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001931 if (spec->hp_dac_nid && !spec->hp_indep_shared) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001932 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001933 check_dac = true;
1934 } else {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001935 path = &spec->hp_dep_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001936 check_dac = false;
1937 }
1938 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001939 if (err < 0)
1940 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001941 if (spec->hp_dac_nid) {
1942 spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
1943 spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
1944 }
Harald Welte0aa62ae2008-09-09 15:58:27 +08001945
Joseph Chanc577b8a2006-11-29 15:29:40 +01001946 return 0;
1947}
1948
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001949static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1950{
1951 struct via_spec *spec = codec->spec;
1952 hda_nid_t pin, dac;
1953
1954 pin = spec->autocfg.speaker_pins[0];
1955 if (!spec->autocfg.speaker_outs || !pin)
1956 return 0;
1957
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001958 if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
1959 dac = spec->speaker_path.path[0];
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001960 spec->multiout.extra_out_nid[0] = dac;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001961 return create_ch_ctls(codec, "Speaker", 3, true,
1962 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001963 }
1964 if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001965 &spec->speaker_path))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001966 return create_ch_ctls(codec, "Speaker", 3, false,
1967 &spec->speaker_path);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001968
1969 return 0;
1970}
1971
Takashi Iwaia766d0d2011-06-17 09:01:29 +02001972/* look for ADCs */
1973static int via_fill_adcs(struct hda_codec *codec)
1974{
1975 struct via_spec *spec = codec->spec;
1976 hda_nid_t nid = codec->start_nid;
1977 int i;
1978
1979 for (i = 0; i < codec->num_nodes; i++, nid++) {
1980 unsigned int wcaps = get_wcaps(codec, nid);
1981 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
1982 continue;
1983 if (wcaps & AC_WCAP_DIGITAL)
1984 continue;
1985 if (!(wcaps & AC_WCAP_CONN_LIST))
1986 continue;
1987 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
1988 return -ENOMEM;
1989 spec->adc_nids[spec->num_adc_nids++] = nid;
1990 }
1991 return 0;
1992}
1993
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001994/* input-src control */
1995static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
1996 struct snd_ctl_elem_info *uinfo)
1997{
1998 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1999 struct via_spec *spec = codec->spec;
2000
2001 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2002 uinfo->count = 1;
2003 uinfo->value.enumerated.items = spec->num_inputs;
2004 if (uinfo->value.enumerated.item >= spec->num_inputs)
2005 uinfo->value.enumerated.item = spec->num_inputs - 1;
2006 strcpy(uinfo->value.enumerated.name,
2007 spec->inputs[uinfo->value.enumerated.item].label);
2008 return 0;
2009}
2010
2011static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2012 struct snd_ctl_elem_value *ucontrol)
2013{
2014 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2015 struct via_spec *spec = codec->spec;
2016 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2017
2018 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2019 return 0;
2020}
2021
2022static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2023 struct snd_ctl_elem_value *ucontrol)
2024{
2025 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2026 struct via_spec *spec = codec->spec;
2027 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2028 hda_nid_t mux;
2029 int cur;
2030
2031 cur = ucontrol->value.enumerated.item[0];
2032 if (cur < 0 || cur >= spec->num_inputs)
2033 return -EINVAL;
2034 if (spec->cur_mux[idx] == cur)
2035 return 0;
2036 spec->cur_mux[idx] = cur;
2037 if (spec->dyn_adc_switch) {
2038 int adc_idx = spec->inputs[cur].adc_idx;
2039 mux = spec->mux_nids[adc_idx];
2040 via_dyn_adc_pcm_resetup(codec, cur);
2041 } else {
2042 mux = spec->mux_nids[idx];
2043 if (snd_BUG_ON(!mux))
2044 return -EINVAL;
2045 }
2046
2047 if (mux) {
2048 /* switch to D0 beofre change index */
2049 if (snd_hda_codec_read(codec, mux, 0,
2050 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2051 snd_hda_codec_write(codec, mux, 0,
2052 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2053 snd_hda_codec_write(codec, mux, 0,
2054 AC_VERB_SET_CONNECT_SEL,
2055 spec->inputs[cur].mux_idx);
2056 }
2057
2058 /* update jack power state */
2059 set_widgets_power_state(codec);
2060 return 0;
2061}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002062
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002063static const struct snd_kcontrol_new via_input_src_ctl = {
2064 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2065 /* The multiple "Capture Source" controls confuse alsamixer
2066 * So call somewhat different..
2067 */
2068 /* .name = "Capture Source", */
2069 .name = "Input Source",
2070 .info = via_mux_enum_info,
2071 .get = via_mux_enum_get,
2072 .put = via_mux_enum_put,
2073};
2074
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002075static int create_input_src_ctls(struct hda_codec *codec, int count)
2076{
2077 struct via_spec *spec = codec->spec;
2078 struct snd_kcontrol_new *knew;
2079
2080 if (spec->num_inputs <= 1 || !count)
2081 return 0; /* no need for single src */
2082
2083 knew = via_clone_control(spec, &via_input_src_ctl);
2084 if (!knew)
2085 return -ENOMEM;
2086 knew->count = count;
2087 return 0;
2088}
2089
2090/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002091static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2092{
2093 struct hda_amp_list *list;
2094
2095 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2096 return;
2097 list = spec->loopback_list + spec->num_loopbacks;
2098 list->nid = mix;
2099 list->dir = HDA_INPUT;
2100 list->idx = idx;
2101 spec->num_loopbacks++;
2102 spec->loopback.amplist = spec->loopback_list;
2103}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002104
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002105static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002106 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002107{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002108 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002109}
2110
2111/* add the input-route to the given pin */
2112static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002113{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002114 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002115 int c, idx;
2116
2117 spec->inputs[spec->num_inputs].adc_idx = -1;
2118 spec->inputs[spec->num_inputs].pin = pin;
2119 for (c = 0; c < spec->num_adc_nids; c++) {
2120 if (spec->mux_nids[c]) {
2121 idx = get_connection_index(codec, spec->mux_nids[c],
2122 pin);
2123 if (idx < 0)
2124 continue;
2125 spec->inputs[spec->num_inputs].mux_idx = idx;
2126 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002127 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002128 continue;
2129 }
2130 spec->inputs[spec->num_inputs].adc_idx = c;
2131 /* Can primary ADC satisfy all inputs? */
2132 if (!spec->dyn_adc_switch &&
2133 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2134 snd_printd(KERN_INFO
2135 "via: dynamic ADC switching enabled\n");
2136 spec->dyn_adc_switch = 1;
2137 }
2138 return true;
2139 }
2140 return false;
2141}
2142
2143static int get_mux_nids(struct hda_codec *codec);
2144
2145/* parse input-routes; fill ADCs, MUXs and input-src entries */
2146static int parse_analog_inputs(struct hda_codec *codec)
2147{
2148 struct via_spec *spec = codec->spec;
2149 const struct auto_pin_cfg *cfg = &spec->autocfg;
2150 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002151
2152 err = via_fill_adcs(codec);
2153 if (err < 0)
2154 return err;
2155 err = get_mux_nids(codec);
2156 if (err < 0)
2157 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002158
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002159 /* fill all input-routes */
2160 for (i = 0; i < cfg->num_inputs; i++) {
2161 if (add_input_route(codec, cfg->inputs[i].pin))
2162 spec->inputs[spec->num_inputs++].label =
2163 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002164 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002165
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002166 /* check for internal loopback recording */
2167 if (spec->aa_mix_nid &&
2168 add_input_route(codec, spec->aa_mix_nid))
2169 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2170
2171 return 0;
2172}
2173
2174/* create analog-loopback volume/switch controls */
2175static int create_loopback_ctls(struct hda_codec *codec)
2176{
2177 struct via_spec *spec = codec->spec;
2178 const struct auto_pin_cfg *cfg = &spec->autocfg;
2179 const char *prev_label = NULL;
2180 int type_idx = 0;
2181 int i, j, err, idx;
2182
2183 if (!spec->aa_mix_nid)
2184 return 0;
2185
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002186 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002187 hda_nid_t pin = cfg->inputs[i].pin;
2188 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2189
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002190 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002191 type_idx++;
2192 else
2193 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002194 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002195 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2196 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002197 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002198 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002199 if (err < 0)
2200 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002201 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002202 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002203
2204 /* remember the label for smart51 control */
2205 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002206 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002207 spec->smart51_idxs[j] = idx;
2208 spec->smart51_labels[j] = label;
2209 break;
2210 }
2211 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002212 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002213 return 0;
2214}
2215
2216/* create mic-boost controls (if present) */
2217static int create_mic_boost_ctls(struct hda_codec *codec)
2218{
2219 struct via_spec *spec = codec->spec;
2220 const struct auto_pin_cfg *cfg = &spec->autocfg;
2221 int i, err;
2222
2223 for (i = 0; i < cfg->num_inputs; i++) {
2224 hda_nid_t pin = cfg->inputs[i].pin;
2225 unsigned int caps;
2226 const char *label;
2227 char name[32];
2228
2229 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2230 continue;
2231 caps = query_amp_caps(codec, pin, HDA_INPUT);
2232 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2233 continue;
2234 label = hda_get_autocfg_input_label(codec, cfg, i);
2235 snprintf(name, sizeof(name), "%s Boost Volume", label);
2236 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2237 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2238 if (err < 0)
2239 return err;
2240 }
2241 return 0;
2242}
2243
2244/* create capture and input-src controls for multiple streams */
2245static int create_multi_adc_ctls(struct hda_codec *codec)
2246{
2247 struct via_spec *spec = codec->spec;
2248 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002249
2250 /* create capture mixer elements */
2251 for (i = 0; i < spec->num_adc_nids; i++) {
2252 hda_nid_t adc = spec->adc_nids[i];
2253 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2254 "Capture Volume", i,
2255 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2256 HDA_INPUT));
2257 if (err < 0)
2258 return err;
2259 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2260 "Capture Switch", i,
2261 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2262 HDA_INPUT));
2263 if (err < 0)
2264 return err;
2265 }
2266
2267 /* input-source control */
2268 for (i = 0; i < spec->num_adc_nids; i++)
2269 if (!spec->mux_nids[i])
2270 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002271 err = create_input_src_ctls(codec, i);
2272 if (err < 0)
2273 return err;
2274 return 0;
2275}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002276
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002277/* bind capture volume/switch */
2278static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2279 HDA_BIND_VOL("Capture Volume", 0);
2280static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2281 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002282
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002283static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2284 struct hda_ctl_ops *ops)
2285{
2286 struct hda_bind_ctls *ctl;
2287 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002288
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002289 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2290 if (!ctl)
2291 return -ENOMEM;
2292 ctl->ops = ops;
2293 for (i = 0; i < spec->num_adc_nids; i++)
2294 ctl->values[i] =
2295 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2296 *ctl_ret = ctl;
2297 return 0;
2298}
2299
2300/* create capture and input-src controls for dynamic ADC-switch case */
2301static int create_dyn_adc_ctls(struct hda_codec *codec)
2302{
2303 struct via_spec *spec = codec->spec;
2304 struct snd_kcontrol_new *knew;
2305 int err;
2306
2307 /* set up the bind capture ctls */
2308 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2309 if (err < 0)
2310 return err;
2311 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2312 if (err < 0)
2313 return err;
2314
2315 /* create capture mixer elements */
2316 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2317 if (!knew)
2318 return -ENOMEM;
2319 knew->private_value = (long)spec->bind_cap_vol;
2320
2321 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2322 if (!knew)
2323 return -ENOMEM;
2324 knew->private_value = (long)spec->bind_cap_sw;
2325
2326 /* input-source control */
2327 err = create_input_src_ctls(codec, 1);
2328 if (err < 0)
2329 return err;
2330 return 0;
2331}
2332
2333/* parse and create capture-related stuff */
2334static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2335{
2336 struct via_spec *spec = codec->spec;
2337 int err;
2338
2339 err = parse_analog_inputs(codec);
2340 if (err < 0)
2341 return err;
2342 if (spec->dyn_adc_switch)
2343 err = create_dyn_adc_ctls(codec);
2344 else
2345 err = create_multi_adc_ctls(codec);
2346 if (err < 0)
2347 return err;
2348 err = create_loopback_ctls(codec);
2349 if (err < 0)
2350 return err;
2351 err = create_mic_boost_ctls(codec);
2352 if (err < 0)
2353 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002354 return 0;
2355}
2356
Harald Welte76d9b0d2008-09-09 15:50:37 +08002357static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2358{
2359 unsigned int def_conf;
2360 unsigned char seqassoc;
2361
Takashi Iwai2f334f92009-02-20 14:37:42 +01002362 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002363 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2364 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002365 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2366 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2367 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2368 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002369 }
2370
2371 return;
2372}
2373
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002374static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002375 struct snd_ctl_elem_value *ucontrol)
2376{
2377 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2378 struct via_spec *spec = codec->spec;
2379
2380 if (spec->codec_type != VT1708)
2381 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002382 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002383 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002384 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002385 return 0;
2386}
2387
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002388static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002389 struct snd_ctl_elem_value *ucontrol)
2390{
2391 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2392 struct via_spec *spec = codec->spec;
2393 int change;
2394
2395 if (spec->codec_type != VT1708)
2396 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002397 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002398 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002399 == !spec->vt1708_jack_detect;
2400 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002401 mute_aa_path(codec, 1);
2402 notify_aa_path_ctls(codec);
2403 }
2404 return change;
2405}
2406
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002407static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2408 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2409 .name = "Jack Detect",
2410 .count = 1,
2411 .info = snd_ctl_boolean_mono_info,
2412 .get = vt1708_jack_detect_get,
2413 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002414};
2415
Takashi Iwai12daef62011-06-18 17:45:49 +02002416static void fill_dig_outs(struct hda_codec *codec);
2417static void fill_dig_in(struct hda_codec *codec);
2418
2419static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002420{
2421 struct via_spec *spec = codec->spec;
2422 int err;
2423
2424 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2425 if (err < 0)
2426 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002427 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002428 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002429
Takashi Iwai4a796162011-06-17 17:53:38 +02002430 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002431 if (err < 0)
2432 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002433 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002434 if (err < 0)
2435 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002436 err = via_auto_create_speaker_ctls(codec);
2437 if (err < 0)
2438 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002439 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002440 if (err < 0)
2441 return err;
2442
2443 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2444
Takashi Iwai12daef62011-06-18 17:45:49 +02002445 fill_dig_outs(codec);
2446 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002447
Takashi Iwai603c4012008-07-30 15:01:44 +02002448 if (spec->kctls.list)
2449 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002450
Joseph Chanc577b8a2006-11-29 15:29:40 +01002451
Takashi Iwai8df2a312011-06-21 11:48:29 +02002452 if (spec->hp_dac_nid && spec->hp_dep_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002453 err = via_hp_build(codec);
2454 if (err < 0)
2455 return err;
2456 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002457
Takashi Iwaif4a78282011-06-17 18:46:48 +02002458 err = via_smart51_build(codec);
2459 if (err < 0)
2460 return err;
2461
Takashi Iwai5d417622011-06-20 11:32:27 +02002462 /* assign slave outs */
2463 if (spec->slave_dig_outs[0])
2464 codec->slave_dig_outs = spec->slave_dig_outs;
2465
Joseph Chanc577b8a2006-11-29 15:29:40 +01002466 return 1;
2467}
2468
Takashi Iwai5d417622011-06-20 11:32:27 +02002469static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002470{
Lydia Wang25eaba22009-10-10 19:08:43 +08002471 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002472 if (spec->multiout.dig_out_nid)
2473 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2474 if (spec->slave_dig_outs[0])
2475 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2476}
Lydia Wang25eaba22009-10-10 19:08:43 +08002477
Takashi Iwai5d417622011-06-20 11:32:27 +02002478static void via_auto_init_dig_in(struct hda_codec *codec)
2479{
2480 struct via_spec *spec = codec->spec;
2481 if (!spec->dig_in_nid)
2482 return;
2483 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2484 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2485}
2486
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002487/* initialize the unsolicited events */
2488static void via_auto_init_unsol_event(struct hda_codec *codec)
2489{
2490 struct via_spec *spec = codec->spec;
2491 struct auto_pin_cfg *cfg = &spec->autocfg;
2492 unsigned int ev;
2493 int i;
2494
2495 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2496 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2497 AC_VERB_SET_UNSOLICITED_ENABLE,
2498 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2499
2500 if (cfg->speaker_pins[0])
2501 ev = VIA_LINE_EVENT;
2502 else
2503 ev = 0;
2504 for (i = 0; i < cfg->line_outs; i++) {
2505 if (cfg->line_out_pins[i] &&
2506 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002507 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002508 AC_VERB_SET_UNSOLICITED_ENABLE,
2509 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2510 }
2511
2512 for (i = 0; i < cfg->num_inputs; i++) {
2513 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2514 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2515 AC_VERB_SET_UNSOLICITED_ENABLE,
2516 AC_USRSP_EN | VIA_JACK_EVENT);
2517 }
2518}
2519
Takashi Iwai5d417622011-06-20 11:32:27 +02002520static int via_init(struct hda_codec *codec)
2521{
2522 struct via_spec *spec = codec->spec;
2523 int i;
2524
2525 for (i = 0; i < spec->num_iverbs; i++)
2526 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2527
Joseph Chanc577b8a2006-11-29 15:29:40 +01002528 via_auto_init_multi_out(codec);
2529 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002530 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002531 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002532 via_auto_init_dig_outs(codec);
2533 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002534
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002535 via_auto_init_unsol_event(codec);
2536
2537 via_hp_automute(codec);
2538 via_line_automute(codec, false);
Lydia Wang25eaba22009-10-10 19:08:43 +08002539
Joseph Chanc577b8a2006-11-29 15:29:40 +01002540 return 0;
2541}
2542
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002543static void vt1708_update_hp_jack_state(struct work_struct *work)
2544{
2545 struct via_spec *spec = container_of(work, struct via_spec,
2546 vt1708_hp_work.work);
2547 if (spec->codec_type != VT1708)
2548 return;
2549 /* if jack state toggled */
2550 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002551 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002552 spec->vt1708_hp_present ^= 1;
2553 via_hp_automute(spec->codec);
2554 }
2555 vt1708_start_hp_work(spec);
2556}
2557
Takashi Iwai337b9d02009-07-07 18:18:59 +02002558static int get_mux_nids(struct hda_codec *codec)
2559{
2560 struct via_spec *spec = codec->spec;
2561 hda_nid_t nid, conn[8];
2562 unsigned int type;
2563 int i, n;
2564
2565 for (i = 0; i < spec->num_adc_nids; i++) {
2566 nid = spec->adc_nids[i];
2567 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002568 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002569 if (type == AC_WID_PIN)
2570 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002571 n = snd_hda_get_connections(codec, nid, conn,
2572 ARRAY_SIZE(conn));
2573 if (n <= 0)
2574 break;
2575 if (n > 1) {
2576 spec->mux_nids[i] = nid;
2577 break;
2578 }
2579 nid = conn[0];
2580 }
2581 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002582 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002583}
2584
Joseph Chanc577b8a2006-11-29 15:29:40 +01002585static int patch_vt1708(struct hda_codec *codec)
2586{
2587 struct via_spec *spec;
2588 int err;
2589
2590 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002591 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002592 if (spec == NULL)
2593 return -ENOMEM;
2594
Takashi Iwai620e2b22011-06-17 17:19:19 +02002595 spec->aa_mix_nid = 0x17;
2596
Takashi Iwai12daef62011-06-18 17:45:49 +02002597 /* Add HP and CD pin config connect bit re-config action */
2598 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2599 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2600
Joseph Chanc577b8a2006-11-29 15:29:40 +01002601 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002602 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002603 if (err < 0) {
2604 via_free(codec);
2605 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002606 }
2607
Takashi Iwai12daef62011-06-18 17:45:49 +02002608 /* add jack detect on/off control */
2609 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2610 return -ENOMEM;
2611
Takashi Iwaibc9b5622008-05-23 17:50:27 +02002612 /* disable 32bit format on VT1708 */
2613 if (codec->vendor_id == 0x11061708)
2614 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002615
Lydia Wange322a362011-06-29 13:52:02 +08002616 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2617
Joseph Chanc577b8a2006-11-29 15:29:40 +01002618 codec->patch_ops = via_patch_ops;
2619
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002620 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002621 return 0;
2622}
2623
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002624static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002625{
2626 struct via_spec *spec;
2627 int err;
2628
2629 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002630 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002631 if (spec == NULL)
2632 return -ENOMEM;
2633
Takashi Iwai620e2b22011-06-17 17:19:19 +02002634 spec->aa_mix_nid = 0x18;
2635
Takashi Iwai12daef62011-06-18 17:45:49 +02002636 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002637 if (err < 0) {
2638 via_free(codec);
2639 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002640 }
2641
Joseph Chanc577b8a2006-11-29 15:29:40 +01002642 codec->patch_ops = via_patch_ops;
2643
Josepch Chanf7278fd2007-12-13 16:40:40 +01002644 return 0;
2645}
2646
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002647static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2648{
2649 struct via_spec *spec = codec->spec;
2650 int imux_is_smixer;
2651 unsigned int parm;
2652 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002653 if ((spec->codec_type != VT1708B_4CH) &&
2654 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002655 is_8ch = 1;
2656
2657 /* SW0 (17h) = stereo mixer */
2658 imux_is_smixer =
2659 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2660 == ((spec->codec_type == VT1708S) ? 5 : 0));
2661 /* inputs */
2662 /* PW 1/2/5 (1ah/1bh/1eh) */
2663 parm = AC_PWRST_D3;
2664 set_pin_power_state(codec, 0x1a, &parm);
2665 set_pin_power_state(codec, 0x1b, &parm);
2666 set_pin_power_state(codec, 0x1e, &parm);
2667 if (imux_is_smixer)
2668 parm = AC_PWRST_D0;
2669 /* SW0 (17h), AIW 0/1 (13h/14h) */
2670 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2671 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2672 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2673
2674 /* outputs */
2675 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2676 parm = AC_PWRST_D3;
2677 set_pin_power_state(codec, 0x19, &parm);
2678 if (spec->smart51_enabled)
2679 set_pin_power_state(codec, 0x1b, &parm);
2680 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2681 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2682
2683 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2684 if (is_8ch) {
2685 parm = AC_PWRST_D3;
2686 set_pin_power_state(codec, 0x22, &parm);
2687 if (spec->smart51_enabled)
2688 set_pin_power_state(codec, 0x1a, &parm);
2689 snd_hda_codec_write(codec, 0x26, 0,
2690 AC_VERB_SET_POWER_STATE, parm);
2691 snd_hda_codec_write(codec, 0x24, 0,
2692 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002693 } else if (codec->vendor_id == 0x11064397) {
2694 /* PW7(23h), SW2(27h), AOW2(25h) */
2695 parm = AC_PWRST_D3;
2696 set_pin_power_state(codec, 0x23, &parm);
2697 if (spec->smart51_enabled)
2698 set_pin_power_state(codec, 0x1a, &parm);
2699 snd_hda_codec_write(codec, 0x27, 0,
2700 AC_VERB_SET_POWER_STATE, parm);
2701 snd_hda_codec_write(codec, 0x25, 0,
2702 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002703 }
2704
2705 /* PW 3/4/7 (1ch/1dh/23h) */
2706 parm = AC_PWRST_D3;
2707 /* force to D0 for internal Speaker */
2708 set_pin_power_state(codec, 0x1c, &parm);
2709 set_pin_power_state(codec, 0x1d, &parm);
2710 if (is_8ch)
2711 set_pin_power_state(codec, 0x23, &parm);
2712
2713 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2714 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2715 imux_is_smixer ? AC_PWRST_D0 : parm);
2716 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2717 if (is_8ch) {
2718 snd_hda_codec_write(codec, 0x25, 0,
2719 AC_VERB_SET_POWER_STATE, parm);
2720 snd_hda_codec_write(codec, 0x27, 0,
2721 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002722 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2723 snd_hda_codec_write(codec, 0x25, 0,
2724 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002725}
2726
Lydia Wang518bf3b2009-10-10 19:07:29 +08002727static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002728static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002729{
2730 struct via_spec *spec;
2731 int err;
2732
Lydia Wang518bf3b2009-10-10 19:07:29 +08002733 if (get_codec_type(codec) == VT1708BCE)
2734 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002735
Josepch Chanf7278fd2007-12-13 16:40:40 +01002736 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002737 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002738 if (spec == NULL)
2739 return -ENOMEM;
2740
Takashi Iwai620e2b22011-06-17 17:19:19 +02002741 spec->aa_mix_nid = 0x16;
2742
Josepch Chanf7278fd2007-12-13 16:40:40 +01002743 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002744 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002745 if (err < 0) {
2746 via_free(codec);
2747 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002748 }
2749
Josepch Chanf7278fd2007-12-13 16:40:40 +01002750 codec->patch_ops = via_patch_ops;
2751
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002752 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2753
Josepch Chanf7278fd2007-12-13 16:40:40 +01002754 return 0;
2755}
2756
Harald Welted949cac2008-09-09 15:56:01 +08002757/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002758static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002759 /* Enable Mic Boost Volume backdoor */
2760 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002761 /* don't bybass mixer */
2762 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002763 { }
2764};
2765
Takashi Iwai9da29272009-05-07 16:31:14 +02002766/* fill out digital output widgets; one for master and one for slave outputs */
2767static void fill_dig_outs(struct hda_codec *codec)
2768{
2769 struct via_spec *spec = codec->spec;
2770 int i;
2771
2772 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2773 hda_nid_t nid;
2774 int conn;
2775
2776 nid = spec->autocfg.dig_out_pins[i];
2777 if (!nid)
2778 continue;
2779 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2780 if (conn < 1)
2781 continue;
2782 if (!spec->multiout.dig_out_nid)
2783 spec->multiout.dig_out_nid = nid;
2784 else {
2785 spec->slave_dig_outs[0] = nid;
2786 break; /* at most two dig outs */
2787 }
2788 }
2789}
2790
Takashi Iwai12daef62011-06-18 17:45:49 +02002791static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002792{
2793 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002794 hda_nid_t dig_nid;
2795 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002796
Takashi Iwai12daef62011-06-18 17:45:49 +02002797 if (!spec->autocfg.dig_in_pin)
2798 return;
Harald Welted949cac2008-09-09 15:56:01 +08002799
Takashi Iwai12daef62011-06-18 17:45:49 +02002800 dig_nid = codec->start_nid;
2801 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2802 unsigned int wcaps = get_wcaps(codec, dig_nid);
2803 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2804 continue;
2805 if (!(wcaps & AC_WCAP_DIGITAL))
2806 continue;
2807 if (!(wcaps & AC_WCAP_CONN_LIST))
2808 continue;
2809 err = get_connection_index(codec, dig_nid,
2810 spec->autocfg.dig_in_pin);
2811 if (err >= 0) {
2812 spec->dig_in_nid = dig_nid;
2813 break;
2814 }
2815 }
Harald Welted949cac2008-09-09 15:56:01 +08002816}
2817
Lydia Wang6369bcf2009-10-10 19:08:31 +08002818static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2819 int offset, int num_steps, int step_size)
2820{
2821 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2822 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2823 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2824 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2825 (0 << AC_AMPCAP_MUTE_SHIFT));
2826}
2827
Harald Welted949cac2008-09-09 15:56:01 +08002828static int patch_vt1708S(struct hda_codec *codec)
2829{
2830 struct via_spec *spec;
2831 int err;
2832
2833 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002834 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002835 if (spec == NULL)
2836 return -ENOMEM;
2837
Takashi Iwai620e2b22011-06-17 17:19:19 +02002838 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002839 override_mic_boost(codec, 0x1a, 0, 3, 40);
2840 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002841
Harald Welted949cac2008-09-09 15:56:01 +08002842 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002843 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002844 if (err < 0) {
2845 via_free(codec);
2846 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002847 }
2848
Takashi Iwai096a8852011-06-20 12:09:02 +02002849 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002850
Harald Welted949cac2008-09-09 15:56:01 +08002851 codec->patch_ops = via_patch_ops;
2852
Lydia Wang518bf3b2009-10-10 19:07:29 +08002853 /* correct names for VT1708BCE */
2854 if (get_codec_type(codec) == VT1708BCE) {
2855 kfree(codec->chip_name);
2856 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2857 snprintf(codec->bus->card->mixername,
2858 sizeof(codec->bus->card->mixername),
2859 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f6302011-03-22 16:25:56 +08002860 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002861 /* correct names for VT1705 */
2862 if (codec->vendor_id == 0x11064397) {
2863 kfree(codec->chip_name);
2864 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2865 snprintf(codec->bus->card->mixername,
2866 sizeof(codec->bus->card->mixername),
2867 "%s %s", codec->vendor_name, codec->chip_name);
2868 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002869 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002870 return 0;
2871}
2872
2873/* Patch for VT1702 */
2874
Takashi Iwai096a8852011-06-20 12:09:02 +02002875static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002876 /* mixer enable */
2877 {0x1, 0xF88, 0x3},
2878 /* GPIO 0~2 */
2879 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08002880 { }
2881};
2882
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002883static void set_widgets_power_state_vt1702(struct hda_codec *codec)
2884{
2885 int imux_is_smixer =
2886 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
2887 unsigned int parm;
2888 /* inputs */
2889 /* PW 1/2/5 (14h/15h/18h) */
2890 parm = AC_PWRST_D3;
2891 set_pin_power_state(codec, 0x14, &parm);
2892 set_pin_power_state(codec, 0x15, &parm);
2893 set_pin_power_state(codec, 0x18, &parm);
2894 if (imux_is_smixer)
2895 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
2896 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
2897 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2898 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
2899 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2900 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
2901
2902 /* outputs */
2903 /* PW 3/4 (16h/17h) */
2904 parm = AC_PWRST_D3;
2905 set_pin_power_state(codec, 0x17, &parm);
2906 set_pin_power_state(codec, 0x16, &parm);
2907 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
2908 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
2909 imux_is_smixer ? AC_PWRST_D0 : parm);
2910 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2911 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
2912}
2913
Harald Welted949cac2008-09-09 15:56:01 +08002914static int patch_vt1702(struct hda_codec *codec)
2915{
2916 struct via_spec *spec;
2917 int err;
Harald Welted949cac2008-09-09 15:56:01 +08002918
2919 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002920 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002921 if (spec == NULL)
2922 return -ENOMEM;
2923
Takashi Iwai620e2b22011-06-17 17:19:19 +02002924 spec->aa_mix_nid = 0x1a;
2925
Takashi Iwai12daef62011-06-18 17:45:49 +02002926 /* limit AA path volume to 0 dB */
2927 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
2928 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
2929 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
2930 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
2931 (1 << AC_AMPCAP_MUTE_SHIFT));
2932
Harald Welted949cac2008-09-09 15:56:01 +08002933 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002934 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002935 if (err < 0) {
2936 via_free(codec);
2937 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002938 }
2939
Takashi Iwai096a8852011-06-20 12:09:02 +02002940 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002941
Harald Welted949cac2008-09-09 15:56:01 +08002942 codec->patch_ops = via_patch_ops;
2943
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002944 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08002945 return 0;
2946}
2947
Lydia Wangeb7188c2009-10-10 19:08:34 +08002948/* Patch for VT1718S */
2949
Takashi Iwai096a8852011-06-20 12:09:02 +02002950static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08002951 /* Enable MW0 adjust Gain 5 */
2952 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08002953 /* Enable Boost Volume backdoor */
2954 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02002955
Lydia Wangeb7188c2009-10-10 19:08:34 +08002956 { }
2957};
2958
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002959static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
2960{
2961 struct via_spec *spec = codec->spec;
2962 int imux_is_smixer;
2963 unsigned int parm;
2964 /* MUX6 (1eh) = stereo mixer */
2965 imux_is_smixer =
2966 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
2967 /* inputs */
2968 /* PW 5/6/7 (29h/2ah/2bh) */
2969 parm = AC_PWRST_D3;
2970 set_pin_power_state(codec, 0x29, &parm);
2971 set_pin_power_state(codec, 0x2a, &parm);
2972 set_pin_power_state(codec, 0x2b, &parm);
2973 if (imux_is_smixer)
2974 parm = AC_PWRST_D0;
2975 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
2976 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
2977 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
2978 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2979 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2980
2981 /* outputs */
2982 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
2983 parm = AC_PWRST_D3;
2984 set_pin_power_state(codec, 0x27, &parm);
2985 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
2986 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
2987
2988 /* PW2 (26h), AOW2 (ah) */
2989 parm = AC_PWRST_D3;
2990 set_pin_power_state(codec, 0x26, &parm);
2991 if (spec->smart51_enabled)
2992 set_pin_power_state(codec, 0x2b, &parm);
2993 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
2994
2995 /* PW0 (24h), AOW0 (8h) */
2996 parm = AC_PWRST_D3;
2997 set_pin_power_state(codec, 0x24, &parm);
2998 if (!spec->hp_independent_mode) /* check for redirected HP */
2999 set_pin_power_state(codec, 0x28, &parm);
3000 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3001 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
3002 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
3003 imux_is_smixer ? AC_PWRST_D0 : parm);
3004
3005 /* PW1 (25h), AOW1 (9h) */
3006 parm = AC_PWRST_D3;
3007 set_pin_power_state(codec, 0x25, &parm);
3008 if (spec->smart51_enabled)
3009 set_pin_power_state(codec, 0x2a, &parm);
3010 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
3011
3012 if (spec->hp_independent_mode) {
3013 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3014 parm = AC_PWRST_D3;
3015 set_pin_power_state(codec, 0x28, &parm);
3016 snd_hda_codec_write(codec, 0x1b, 0,
3017 AC_VERB_SET_POWER_STATE, parm);
3018 snd_hda_codec_write(codec, 0x34, 0,
3019 AC_VERB_SET_POWER_STATE, parm);
3020 snd_hda_codec_write(codec, 0xc, 0,
3021 AC_VERB_SET_POWER_STATE, parm);
3022 }
3023}
3024
Lydia Wangeb7188c2009-10-10 19:08:34 +08003025static int patch_vt1718S(struct hda_codec *codec)
3026{
3027 struct via_spec *spec;
3028 int err;
3029
3030 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003031 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003032 if (spec == NULL)
3033 return -ENOMEM;
3034
Takashi Iwai620e2b22011-06-17 17:19:19 +02003035 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003036 override_mic_boost(codec, 0x2b, 0, 3, 40);
3037 override_mic_boost(codec, 0x29, 0, 3, 40);
Lydia Wangc4394f52011-07-04 16:54:15 +08003038 spec->dac_mixer_idx = 5;
Takashi Iwai620e2b22011-06-17 17:19:19 +02003039
Lydia Wangeb7188c2009-10-10 19:08:34 +08003040 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003041 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003042 if (err < 0) {
3043 via_free(codec);
3044 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003045 }
3046
Takashi Iwai096a8852011-06-20 12:09:02 +02003047 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003048
Lydia Wangeb7188c2009-10-10 19:08:34 +08003049 codec->patch_ops = via_patch_ops;
3050
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003051 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3052
Lydia Wangeb7188c2009-10-10 19:08:34 +08003053 return 0;
3054}
Lydia Wangf3db4232009-10-10 19:08:41 +08003055
3056/* Patch for VT1716S */
3057
3058static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3059 struct snd_ctl_elem_info *uinfo)
3060{
3061 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3062 uinfo->count = 1;
3063 uinfo->value.integer.min = 0;
3064 uinfo->value.integer.max = 1;
3065 return 0;
3066}
3067
3068static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3069 struct snd_ctl_elem_value *ucontrol)
3070{
3071 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3072 int index = 0;
3073
3074 index = snd_hda_codec_read(codec, 0x26, 0,
3075 AC_VERB_GET_CONNECT_SEL, 0);
3076 if (index != -1)
3077 *ucontrol->value.integer.value = index;
3078
3079 return 0;
3080}
3081
3082static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3083 struct snd_ctl_elem_value *ucontrol)
3084{
3085 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3086 struct via_spec *spec = codec->spec;
3087 int index = *ucontrol->value.integer.value;
3088
3089 snd_hda_codec_write(codec, 0x26, 0,
3090 AC_VERB_SET_CONNECT_SEL, index);
3091 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003092 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003093 return 1;
3094}
3095
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003096static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003097 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3098 {
3099 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3100 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003101 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003102 .count = 1,
3103 .info = vt1716s_dmic_info,
3104 .get = vt1716s_dmic_get,
3105 .put = vt1716s_dmic_put,
3106 },
3107 {} /* end */
3108};
3109
3110
3111/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003112static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003113 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3114 { } /* end */
3115};
3116
Takashi Iwai096a8852011-06-20 12:09:02 +02003117static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003118 /* Enable Boost Volume backdoor */
3119 {0x1, 0xf8a, 0x80},
3120 /* don't bybass mixer */
3121 {0x1, 0xf88, 0xc0},
3122 /* Enable mono output */
3123 {0x1, 0xf90, 0x08},
3124 { }
3125};
3126
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003127static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3128{
3129 struct via_spec *spec = codec->spec;
3130 int imux_is_smixer;
3131 unsigned int parm;
3132 unsigned int mono_out, present;
3133 /* SW0 (17h) = stereo mixer */
3134 imux_is_smixer =
3135 (snd_hda_codec_read(codec, 0x17, 0,
3136 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3137 /* inputs */
3138 /* PW 1/2/5 (1ah/1bh/1eh) */
3139 parm = AC_PWRST_D3;
3140 set_pin_power_state(codec, 0x1a, &parm);
3141 set_pin_power_state(codec, 0x1b, &parm);
3142 set_pin_power_state(codec, 0x1e, &parm);
3143 if (imux_is_smixer)
3144 parm = AC_PWRST_D0;
3145 /* SW0 (17h), AIW0(13h) */
3146 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3147 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3148
3149 parm = AC_PWRST_D3;
3150 set_pin_power_state(codec, 0x1e, &parm);
3151 /* PW11 (22h) */
3152 if (spec->dmic_enabled)
3153 set_pin_power_state(codec, 0x22, &parm);
3154 else
3155 snd_hda_codec_write(codec, 0x22, 0,
3156 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3157
3158 /* SW2(26h), AIW1(14h) */
3159 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3160 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3161
3162 /* outputs */
3163 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3164 parm = AC_PWRST_D3;
3165 set_pin_power_state(codec, 0x19, &parm);
3166 /* Smart 5.1 PW2(1bh) */
3167 if (spec->smart51_enabled)
3168 set_pin_power_state(codec, 0x1b, &parm);
3169 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3170 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3171
3172 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3173 parm = AC_PWRST_D3;
3174 set_pin_power_state(codec, 0x23, &parm);
3175 /* Smart 5.1 PW1(1ah) */
3176 if (spec->smart51_enabled)
3177 set_pin_power_state(codec, 0x1a, &parm);
3178 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3179
3180 /* Smart 5.1 PW5(1eh) */
3181 if (spec->smart51_enabled)
3182 set_pin_power_state(codec, 0x1e, &parm);
3183 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3184
3185 /* Mono out */
3186 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3187 present = snd_hda_jack_detect(codec, 0x1c);
3188
3189 if (present)
3190 mono_out = 0;
3191 else {
3192 present = snd_hda_jack_detect(codec, 0x1d);
3193 if (!spec->hp_independent_mode && present)
3194 mono_out = 0;
3195 else
3196 mono_out = 1;
3197 }
3198 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3199 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3200 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3201 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3202
3203 /* PW 3/4 (1ch/1dh) */
3204 parm = AC_PWRST_D3;
3205 set_pin_power_state(codec, 0x1c, &parm);
3206 set_pin_power_state(codec, 0x1d, &parm);
3207 /* HP Independent Mode, power on AOW3 */
3208 if (spec->hp_independent_mode)
3209 snd_hda_codec_write(codec, 0x25, 0,
3210 AC_VERB_SET_POWER_STATE, parm);
3211
3212 /* force to D0 for internal Speaker */
3213 /* MW0 (16h), AOW0 (10h) */
3214 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3215 imux_is_smixer ? AC_PWRST_D0 : parm);
3216 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3217 mono_out ? AC_PWRST_D0 : parm);
3218}
3219
Lydia Wangf3db4232009-10-10 19:08:41 +08003220static int patch_vt1716S(struct hda_codec *codec)
3221{
3222 struct via_spec *spec;
3223 int err;
3224
3225 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003226 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003227 if (spec == NULL)
3228 return -ENOMEM;
3229
Takashi Iwai620e2b22011-06-17 17:19:19 +02003230 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003231 override_mic_boost(codec, 0x1a, 0, 3, 40);
3232 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003233
Lydia Wangf3db4232009-10-10 19:08:41 +08003234 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003235 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003236 if (err < 0) {
3237 via_free(codec);
3238 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003239 }
3240
Takashi Iwai096a8852011-06-20 12:09:02 +02003241 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003242
Lydia Wangf3db4232009-10-10 19:08:41 +08003243 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3244 spec->num_mixers++;
3245
3246 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3247
3248 codec->patch_ops = via_patch_ops;
3249
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003250 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003251 return 0;
3252}
Lydia Wang25eaba22009-10-10 19:08:43 +08003253
3254/* for vt2002P */
3255
Takashi Iwai096a8852011-06-20 12:09:02 +02003256static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003257 /* Class-D speaker related verbs */
3258 {0x1, 0xfe0, 0x4},
3259 {0x1, 0xfe9, 0x80},
3260 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003261 /* Enable Boost Volume backdoor */
3262 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003263 /* Enable AOW0 to MW9 */
3264 {0x1, 0xfb8, 0x88},
3265 { }
3266};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003267
Takashi Iwai096a8852011-06-20 12:09:02 +02003268static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003269 /* Enable Boost Volume backdoor */
3270 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003271 /* Enable AOW0 to MW9 */
3272 {0x1, 0xfb8, 0x88},
3273 { }
3274};
Lydia Wang25eaba22009-10-10 19:08:43 +08003275
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003276static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3277{
3278 struct via_spec *spec = codec->spec;
3279 int imux_is_smixer;
3280 unsigned int parm;
3281 unsigned int present;
3282 /* MUX9 (1eh) = stereo mixer */
3283 imux_is_smixer =
3284 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3285 /* inputs */
3286 /* PW 5/6/7 (29h/2ah/2bh) */
3287 parm = AC_PWRST_D3;
3288 set_pin_power_state(codec, 0x29, &parm);
3289 set_pin_power_state(codec, 0x2a, &parm);
3290 set_pin_power_state(codec, 0x2b, &parm);
3291 parm = AC_PWRST_D0;
3292 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3293 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3294 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3295 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3296 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3297
3298 /* outputs */
3299 /* AOW0 (8h)*/
3300 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3301
Lydia Wang118909562011-03-23 17:57:34 +08003302 if (spec->codec_type == VT1802) {
3303 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3304 parm = AC_PWRST_D3;
3305 set_pin_power_state(codec, 0x28, &parm);
3306 snd_hda_codec_write(codec, 0x18, 0,
3307 AC_VERB_SET_POWER_STATE, parm);
3308 snd_hda_codec_write(codec, 0x38, 0,
3309 AC_VERB_SET_POWER_STATE, parm);
3310 } else {
3311 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3312 parm = AC_PWRST_D3;
3313 set_pin_power_state(codec, 0x26, &parm);
3314 snd_hda_codec_write(codec, 0x1c, 0,
3315 AC_VERB_SET_POWER_STATE, parm);
3316 snd_hda_codec_write(codec, 0x37, 0,
3317 AC_VERB_SET_POWER_STATE, parm);
3318 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003319
Lydia Wang118909562011-03-23 17:57:34 +08003320 if (spec->codec_type == VT1802) {
3321 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3322 parm = AC_PWRST_D3;
3323 set_pin_power_state(codec, 0x25, &parm);
3324 snd_hda_codec_write(codec, 0x15, 0,
3325 AC_VERB_SET_POWER_STATE, parm);
3326 snd_hda_codec_write(codec, 0x35, 0,
3327 AC_VERB_SET_POWER_STATE, parm);
3328 } else {
3329 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3330 parm = AC_PWRST_D3;
3331 set_pin_power_state(codec, 0x25, &parm);
3332 snd_hda_codec_write(codec, 0x19, 0,
3333 AC_VERB_SET_POWER_STATE, parm);
3334 snd_hda_codec_write(codec, 0x35, 0,
3335 AC_VERB_SET_POWER_STATE, parm);
3336 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003337
3338 if (spec->hp_independent_mode)
3339 snd_hda_codec_write(codec, 0x9, 0,
3340 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3341
3342 /* Class-D */
3343 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3344 present = snd_hda_jack_detect(codec, 0x25);
3345
3346 parm = AC_PWRST_D3;
3347 set_pin_power_state(codec, 0x24, &parm);
3348 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003349 if (spec->codec_type == VT1802)
3350 snd_hda_codec_write(codec, 0x14, 0,
3351 AC_VERB_SET_POWER_STATE, parm);
3352 else
3353 snd_hda_codec_write(codec, 0x18, 0,
3354 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003355 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3356
3357 /* Mono Out */
3358 present = snd_hda_jack_detect(codec, 0x26);
3359
3360 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003361 if (spec->codec_type == VT1802) {
3362 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3363 snd_hda_codec_write(codec, 0x33, 0,
3364 AC_VERB_SET_POWER_STATE, parm);
3365 snd_hda_codec_write(codec, 0x1c, 0,
3366 AC_VERB_SET_POWER_STATE, parm);
3367 snd_hda_codec_write(codec, 0x3c, 0,
3368 AC_VERB_SET_POWER_STATE, parm);
3369 } else {
3370 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3371 snd_hda_codec_write(codec, 0x31, 0,
3372 AC_VERB_SET_POWER_STATE, parm);
3373 snd_hda_codec_write(codec, 0x17, 0,
3374 AC_VERB_SET_POWER_STATE, parm);
3375 snd_hda_codec_write(codec, 0x3b, 0,
3376 AC_VERB_SET_POWER_STATE, parm);
3377 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003378 /* MW9 (21h) */
3379 if (imux_is_smixer || !is_aa_path_mute(codec))
3380 snd_hda_codec_write(codec, 0x21, 0,
3381 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3382 else
3383 snd_hda_codec_write(codec, 0x21, 0,
3384 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3385}
Lydia Wang25eaba22009-10-10 19:08:43 +08003386
3387/* patch for vt2002P */
3388static int patch_vt2002P(struct hda_codec *codec)
3389{
3390 struct via_spec *spec;
3391 int err;
3392
3393 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003394 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003395 if (spec == NULL)
3396 return -ENOMEM;
3397
Takashi Iwai620e2b22011-06-17 17:19:19 +02003398 spec->aa_mix_nid = 0x21;
Lydia Wang5c9a5612011-07-08 14:03:43 +08003399 spec->dac_mixer_idx = 3;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003400 override_mic_boost(codec, 0x2b, 0, 3, 40);
3401 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003402
Lydia Wang25eaba22009-10-10 19:08:43 +08003403 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003404 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003405 if (err < 0) {
3406 via_free(codec);
3407 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003408 }
3409
Lydia Wang118909562011-03-23 17:57:34 +08003410 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003411 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003412 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003413 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003414
Lydia Wang25eaba22009-10-10 19:08:43 +08003415 codec->patch_ops = via_patch_ops;
3416
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003417 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003418 return 0;
3419}
Lydia Wangab6734e2009-10-10 19:08:46 +08003420
3421/* for vt1812 */
3422
Takashi Iwai096a8852011-06-20 12:09:02 +02003423static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003424 /* Enable Boost Volume backdoor */
3425 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003426 /* Enable AOW0 to MW9 */
3427 {0x1, 0xfb8, 0xa8},
3428 { }
3429};
3430
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003431static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3432{
3433 struct via_spec *spec = codec->spec;
3434 int imux_is_smixer =
3435 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3436 unsigned int parm;
3437 unsigned int present;
3438 /* MUX10 (1eh) = stereo mixer */
3439 imux_is_smixer =
3440 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3441 /* inputs */
3442 /* PW 5/6/7 (29h/2ah/2bh) */
3443 parm = AC_PWRST_D3;
3444 set_pin_power_state(codec, 0x29, &parm);
3445 set_pin_power_state(codec, 0x2a, &parm);
3446 set_pin_power_state(codec, 0x2b, &parm);
3447 parm = AC_PWRST_D0;
3448 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3449 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3450 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3451 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3452 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3453
3454 /* outputs */
3455 /* AOW0 (8h)*/
3456 snd_hda_codec_write(codec, 0x8, 0,
3457 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3458
3459 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3460 parm = AC_PWRST_D3;
3461 set_pin_power_state(codec, 0x28, &parm);
3462 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3463 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3464
3465 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3466 parm = AC_PWRST_D3;
3467 set_pin_power_state(codec, 0x25, &parm);
3468 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3469 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3470 if (spec->hp_independent_mode)
3471 snd_hda_codec_write(codec, 0x9, 0,
3472 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3473
3474 /* Internal Speaker */
3475 /* PW0 (24h), MW0(14h), MUX0(34h) */
3476 present = snd_hda_jack_detect(codec, 0x25);
3477
3478 parm = AC_PWRST_D3;
3479 set_pin_power_state(codec, 0x24, &parm);
3480 if (present) {
3481 snd_hda_codec_write(codec, 0x14, 0,
3482 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3483 snd_hda_codec_write(codec, 0x34, 0,
3484 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3485 } else {
3486 snd_hda_codec_write(codec, 0x14, 0,
3487 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3488 snd_hda_codec_write(codec, 0x34, 0,
3489 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3490 }
3491
3492
3493 /* Mono Out */
3494 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3495 present = snd_hda_jack_detect(codec, 0x28);
3496
3497 parm = AC_PWRST_D3;
3498 set_pin_power_state(codec, 0x31, &parm);
3499 if (present) {
3500 snd_hda_codec_write(codec, 0x1c, 0,
3501 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3502 snd_hda_codec_write(codec, 0x3c, 0,
3503 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3504 snd_hda_codec_write(codec, 0x3e, 0,
3505 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3506 } else {
3507 snd_hda_codec_write(codec, 0x1c, 0,
3508 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3509 snd_hda_codec_write(codec, 0x3c, 0,
3510 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3511 snd_hda_codec_write(codec, 0x3e, 0,
3512 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3513 }
3514
3515 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3516 parm = AC_PWRST_D3;
3517 set_pin_power_state(codec, 0x33, &parm);
3518 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3519 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3520
3521}
Lydia Wangab6734e2009-10-10 19:08:46 +08003522
3523/* patch for vt1812 */
3524static int patch_vt1812(struct hda_codec *codec)
3525{
3526 struct via_spec *spec;
3527 int err;
3528
3529 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003530 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003531 if (spec == NULL)
3532 return -ENOMEM;
3533
Takashi Iwai620e2b22011-06-17 17:19:19 +02003534 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003535 override_mic_boost(codec, 0x2b, 0, 3, 40);
3536 override_mic_boost(codec, 0x29, 0, 3, 40);
Lydia Wang28dc10a2011-07-08 18:28:47 +08003537 spec->dac_mixer_idx = 5;
Takashi Iwai620e2b22011-06-17 17:19:19 +02003538
Lydia Wangab6734e2009-10-10 19:08:46 +08003539 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003540 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003541 if (err < 0) {
3542 via_free(codec);
3543 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003544 }
3545
Takashi Iwai096a8852011-06-20 12:09:02 +02003546 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003547
Lydia Wangab6734e2009-10-10 19:08:46 +08003548 codec->patch_ops = via_patch_ops;
3549
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003550 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003551 return 0;
3552}
3553
Joseph Chanc577b8a2006-11-29 15:29:40 +01003554/*
3555 * patch entries
3556 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003557static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003558 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3559 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3560 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3561 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3562 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003563 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003564 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003565 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003566 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003567 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003568 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003569 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003570 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003571 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003572 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003573 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003574 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003575 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003576 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003577 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003578 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003579 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003580 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003581 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003582 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003583 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003584 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003585 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003586 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003587 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003588 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003589 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003590 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003591 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003592 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003593 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003594 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003595 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003596 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003597 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003598 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003599 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003600 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003601 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003602 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003603 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003604 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003605 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003606 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003607 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003608 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003609 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003610 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003611 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003612 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003613 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003614 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003615 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003616 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003617 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003618 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003619 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003620 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003621 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003622 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003623 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003624 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003625 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003626 { .id = 0x11060428, .name = "VT1718S",
3627 .patch = patch_vt1718S},
3628 { .id = 0x11064428, .name = "VT1718S",
3629 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003630 { .id = 0x11060441, .name = "VT2020",
3631 .patch = patch_vt1718S},
3632 { .id = 0x11064441, .name = "VT1828S",
3633 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003634 { .id = 0x11060433, .name = "VT1716S",
3635 .patch = patch_vt1716S},
3636 { .id = 0x1106a721, .name = "VT1716S",
3637 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003638 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3639 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003640 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003641 { .id = 0x11060440, .name = "VT1818S",
3642 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003643 { .id = 0x11060446, .name = "VT1802",
3644 .patch = patch_vt2002P},
3645 { .id = 0x11068446, .name = "VT1802",
3646 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003647 {} /* terminator */
3648};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003649
3650MODULE_ALIAS("snd-hda-codec-id:1106*");
3651
3652static struct hda_codec_preset_list via_list = {
3653 .preset = snd_hda_preset_via,
3654 .owner = THIS_MODULE,
3655};
3656
3657MODULE_LICENSE("GPL");
3658MODULE_DESCRIPTION("VIA HD-audio codec");
3659
3660static int __init patch_via_init(void)
3661{
3662 return snd_hda_add_codec_preset(&via_list);
3663}
3664
3665static void __exit patch_via_exit(void)
3666{
3667 snd_hda_delete_codec_preset(&via_list);
3668}
3669
3670module_init(patch_via_init)
3671module_exit(patch_via_exit)