blob: 76c688409cd8d5d901c94fb6308f70056e6264bd [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 Iwai3214b962011-07-18 12:49:25 +0200133 hda_nid_t speaker_dac_nid;
134 int hp_indep_shared; /* indep HP-DAC is shared with side ch */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200135 int num_active_streams;
Takashi Iwai3214b962011-07-18 12:49:25 +0200136 int aamix_mode; /* loopback is enabled for output-path? */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800137
Takashi Iwai3214b962011-07-18 12:49:25 +0200138 /* Output-paths:
139 * There are different output-paths depending on the setup.
140 * out_path, hp_path and speaker_path are primary paths. If both
141 * direct DAC and aa-loopback routes are available, these contain
142 * the former paths. Meanwhile *_mix_path contain the paths with
143 * loopback mixer. (Since the loopback is only for front channel,
144 * no out_mix_path for surround channels.)
145 * The HP output has another path, hp_indep_path, which is used in
146 * the independent-HP mode.
147 */
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200148 struct nid_path out_path[HDA_SIDE + 1];
Takashi Iwai3214b962011-07-18 12:49:25 +0200149 struct nid_path out_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200150 struct nid_path hp_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200151 struct nid_path hp_mix_path;
152 struct nid_path hp_indep_path;
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200153 struct nid_path speaker_path;
Takashi Iwai3214b962011-07-18 12:49:25 +0200154 struct nid_path speaker_mix_path;
Takashi Iwai4a796162011-06-17 17:53:38 +0200155
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800156 /* capture */
157 unsigned int num_adc_nids;
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200158 hda_nid_t adc_nids[VIA_MAX_ADCS];
159 hda_nid_t mux_nids[VIA_MAX_ADCS];
Takashi Iwai620e2b22011-06-17 17:19:19 +0200160 hda_nid_t aa_mix_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800161 hda_nid_t dig_in_nid;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800162
163 /* capture source */
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200164 bool dyn_adc_switch;
165 int num_inputs;
166 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
Takashi Iwaide6c74f2011-07-04 14:46:42 +0200167 unsigned int cur_mux[VIA_MAX_ADCS];
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800168
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200169 /* dynamic ADC switching */
170 hda_nid_t cur_adc;
171 unsigned int cur_adc_stream_tag;
172 unsigned int cur_adc_format;
173
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800174 /* PCM information */
175 struct hda_pcm pcm_rec[3];
176
177 /* dynamic controls, init_verbs and input_mux */
178 struct auto_pin_cfg autocfg;
179 struct snd_array kctls;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800180 hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS];
181
182 /* HP mode source */
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800183 unsigned int hp_independent_mode;
Lydia Wangf3db4232009-10-10 19:08:41 +0800184 unsigned int dmic_enabled;
Takashi Iwai24088a52011-06-17 16:59:21 +0200185 unsigned int no_pin_power_ctl;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800186 enum VIA_HDA_CODEC codec_type;
187
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200188 /* smart51 setup */
189 unsigned int smart51_nums;
190 hda_nid_t smart51_pins[2];
191 int smart51_idxs[2];
192 const char *smart51_labels[2];
193 unsigned int smart51_enabled;
194
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800195 /* work to check hp jack state */
196 struct hda_codec *codec;
197 struct delayed_work vt1708_hp_work;
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200198 int vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800199 int vt1708_hp_present;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800200
201 void (*set_widgets_power_state)(struct hda_codec *codec);
202
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800203 struct hda_loopback_check loopback;
Takashi Iwai13af8e72011-06-20 14:05:46 +0200204 int num_loopbacks;
205 struct hda_amp_list loopback_list[8];
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200206
207 /* bind capture-volume */
208 struct hda_bind_ctls *bind_cap_vol;
209 struct hda_bind_ctls *bind_cap_sw;
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800210};
211
Lydia Wang0341ccd2011-03-22 16:25:03 +0800212static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100213static struct via_spec * via_new_spec(struct hda_codec *codec)
214{
215 struct via_spec *spec;
216
217 spec = kzalloc(sizeof(*spec), GFP_KERNEL);
218 if (spec == NULL)
219 return NULL;
220
221 codec->spec = spec;
222 spec->codec = codec;
Lydia Wang0341ccd2011-03-22 16:25:03 +0800223 spec->codec_type = get_codec_type(codec);
224 /* VT1708BCE & VT1708S are almost same */
225 if (spec->codec_type == VT1708BCE)
226 spec->codec_type = VT1708S;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100227 return spec;
228}
229
Lydia Wang744ff5f2009-10-10 19:07:26 +0800230static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
Harald Welted7426322008-09-15 22:43:23 +0800231{
Lydia Wang744ff5f2009-10-10 19:07:26 +0800232 u32 vendor_id = codec->vendor_id;
Harald Welted7426322008-09-15 22:43:23 +0800233 u16 ven_id = vendor_id >> 16;
234 u16 dev_id = vendor_id & 0xffff;
235 enum VIA_HDA_CODEC codec_type;
236
237 /* get codec type */
238 if (ven_id != 0x1106)
239 codec_type = UNKNOWN;
240 else if (dev_id >= 0x1708 && dev_id <= 0x170b)
241 codec_type = VT1708;
242 else if (dev_id >= 0xe710 && dev_id <= 0xe713)
243 codec_type = VT1709_10CH;
244 else if (dev_id >= 0xe714 && dev_id <= 0xe717)
245 codec_type = VT1709_6CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800246 else if (dev_id >= 0xe720 && dev_id <= 0xe723) {
Harald Welted7426322008-09-15 22:43:23 +0800247 codec_type = VT1708B_8CH;
Lydia Wang518bf3b2009-10-10 19:07:29 +0800248 if (snd_hda_param_read(codec, 0x16, AC_PAR_CONNLIST_LEN) == 0x7)
249 codec_type = VT1708BCE;
250 } else if (dev_id >= 0xe724 && dev_id <= 0xe727)
Harald Welted7426322008-09-15 22:43:23 +0800251 codec_type = VT1708B_4CH;
252 else if ((dev_id & 0xfff) == 0x397
253 && (dev_id >> 12) < 8)
254 codec_type = VT1708S;
255 else if ((dev_id & 0xfff) == 0x398
256 && (dev_id >> 12) < 8)
257 codec_type = VT1702;
Lydia Wangeb7188c2009-10-10 19:08:34 +0800258 else if ((dev_id & 0xfff) == 0x428
259 && (dev_id >> 12) < 8)
260 codec_type = VT1718S;
Lydia Wangf3db4232009-10-10 19:08:41 +0800261 else if (dev_id == 0x0433 || dev_id == 0xa721)
262 codec_type = VT1716S;
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +0800263 else if (dev_id == 0x0441 || dev_id == 0x4441)
264 codec_type = VT1718S;
Lydia Wang25eaba22009-10-10 19:08:43 +0800265 else if (dev_id == 0x0438 || dev_id == 0x4438)
266 codec_type = VT2002P;
Lydia Wangab6734e2009-10-10 19:08:46 +0800267 else if (dev_id == 0x0448)
268 codec_type = VT1812;
Lydia Wang36dd5c42009-10-20 13:18:04 +0800269 else if (dev_id == 0x0440)
270 codec_type = VT1708S;
Lydia Wang118909562011-03-23 17:57:34 +0800271 else if ((dev_id & 0xfff) == 0x446)
272 codec_type = VT1802;
Harald Welted7426322008-09-15 22:43:23 +0800273 else
274 codec_type = UNKNOWN;
275 return codec_type;
276};
277
Lydia Wangec7e7e42011-03-24 12:43:44 +0800278#define VIA_JACK_EVENT 0x20
Harald Welte69e52a82008-09-09 15:57:32 +0800279#define VIA_HP_EVENT 0x01
280#define VIA_GPIO_EVENT 0x02
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200281#define VIA_LINE_EVENT 0x03
Harald Welte69e52a82008-09-09 15:57:32 +0800282
Joseph Chanc577b8a2006-11-29 15:29:40 +0100283enum {
284 VIA_CTL_WIDGET_VOL,
285 VIA_CTL_WIDGET_MUTE,
Lydia Wangf5271102009-10-10 19:07:35 +0800286 VIA_CTL_WIDGET_ANALOG_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100287};
288
Takashi Iwaiada509e2011-06-20 15:40:19 +0200289static void analog_low_current_mode(struct hda_codec *codec);
290static bool is_aa_path_mute(struct hda_codec *codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800291
292static void vt1708_start_hp_work(struct via_spec *spec)
293{
294 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
295 return;
296 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200297 !spec->vt1708_jack_detect);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800298 if (!delayed_work_pending(&spec->vt1708_hp_work))
299 schedule_delayed_work(&spec->vt1708_hp_work,
300 msecs_to_jiffies(100));
301}
302
303static void vt1708_stop_hp_work(struct via_spec *spec)
304{
305 if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0)
306 return;
307 if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1
308 && !is_aa_path_mute(spec->codec))
309 return;
310 snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81,
Takashi Iwaie06e5a22011-06-17 15:46:13 +0200311 !spec->vt1708_jack_detect);
Tejun Heo5b84ba22010-12-11 17:51:26 +0100312 cancel_delayed_work_sync(&spec->vt1708_hp_work);
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800313}
Lydia Wangf5271102009-10-10 19:07:35 +0800314
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800315static void set_widgets_power_state(struct hda_codec *codec)
316{
317 struct via_spec *spec = codec->spec;
318 if (spec->set_widgets_power_state)
319 spec->set_widgets_power_state(codec);
320}
Lydia Wang25eaba22009-10-10 19:08:43 +0800321
Lydia Wangf5271102009-10-10 19:07:35 +0800322static int analog_input_switch_put(struct snd_kcontrol *kcontrol,
323 struct snd_ctl_elem_value *ucontrol)
324{
325 int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
326 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
327
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800328 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +0200329 analog_low_current_mode(snd_kcontrol_chip(kcontrol));
Lydia Wang1f2e99f2009-10-10 19:08:17 +0800330 if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) {
331 if (is_aa_path_mute(codec))
332 vt1708_start_hp_work(codec->spec);
333 else
334 vt1708_stop_hp_work(codec->spec);
335 }
Lydia Wangf5271102009-10-10 19:07:35 +0800336 return change;
337}
338
339/* modify .put = snd_hda_mixer_amp_switch_put */
340#define ANALOG_INPUT_MUTE \
341 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
342 .name = NULL, \
343 .index = 0, \
344 .info = snd_hda_mixer_amp_switch_info, \
345 .get = snd_hda_mixer_amp_switch_get, \
346 .put = analog_input_switch_put, \
347 .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0) }
348
Takashi Iwai90dd48a2011-05-02 12:38:19 +0200349static const struct snd_kcontrol_new via_control_templates[] = {
Joseph Chanc577b8a2006-11-29 15:29:40 +0100350 HDA_CODEC_VOLUME(NULL, 0, 0, 0),
351 HDA_CODEC_MUTE(NULL, 0, 0, 0),
Lydia Wangf5271102009-10-10 19:07:35 +0800352 ANALOG_INPUT_MUTE,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100353};
354
Lydia Wangab6734e2009-10-10 19:08:46 +0800355
Joseph Chanc577b8a2006-11-29 15:29:40 +0100356/* add dynamic controls */
Takashi Iwai291c9e32011-06-17 16:15:26 +0200357static struct snd_kcontrol_new *__via_clone_ctl(struct via_spec *spec,
358 const struct snd_kcontrol_new *tmpl,
359 const char *name)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100360{
361 struct snd_kcontrol_new *knew;
362
Takashi Iwai603c4012008-07-30 15:01:44 +0200363 snd_array_init(&spec->kctls, sizeof(*knew), 32);
364 knew = snd_array_new(&spec->kctls);
365 if (!knew)
Takashi Iwai291c9e32011-06-17 16:15:26 +0200366 return NULL;
367 *knew = *tmpl;
368 if (!name)
369 name = tmpl->name;
370 if (name) {
371 knew->name = kstrdup(name, GFP_KERNEL);
372 if (!knew->name)
373 return NULL;
374 }
375 return knew;
376}
377
378static int __via_add_control(struct via_spec *spec, int type, const char *name,
379 int idx, unsigned long val)
380{
381 struct snd_kcontrol_new *knew;
382
383 knew = __via_clone_ctl(spec, &via_control_templates[type], name);
384 if (!knew)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100385 return -ENOMEM;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +0200386 knew->index = idx;
Jaroslav Kysela4d02d1b2009-11-12 10:15:48 +0100387 if (get_amp_nid_(val))
Jaroslav Kysela5e26dfd2009-12-10 13:57:01 +0100388 knew->subdevice = HDA_SUBDEV_AMP_FLAG;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100389 knew->private_value = val;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100390 return 0;
391}
392
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200393#define via_add_control(spec, type, name, val) \
394 __via_add_control(spec, type, name, 0, val)
395
Takashi Iwai291c9e32011-06-17 16:15:26 +0200396#define via_clone_control(spec, tmpl) __via_clone_ctl(spec, tmpl, NULL)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100397
Takashi Iwai603c4012008-07-30 15:01:44 +0200398static void via_free_kctls(struct hda_codec *codec)
399{
400 struct via_spec *spec = codec->spec;
401
402 if (spec->kctls.list) {
403 struct snd_kcontrol_new *kctl = spec->kctls.list;
404 int i;
405 for (i = 0; i < spec->kctls.used; i++)
406 kfree(kctl[i].name);
407 }
408 snd_array_free(&spec->kctls);
409}
410
Joseph Chanc577b8a2006-11-29 15:29:40 +0100411/* create input playback/capture controls for the given pin */
Lydia Wang9510e8d2009-10-10 19:07:39 +0800412static int via_new_analog_input(struct via_spec *spec, const char *ctlname,
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200413 int type_idx, int idx, int mix_nid)
Joseph Chanc577b8a2006-11-29 15:29:40 +0100414{
415 char name[32];
416 int err;
417
418 sprintf(name, "%s Playback Volume", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200419 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100420 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
421 if (err < 0)
422 return err;
423 sprintf(name, "%s Playback Switch", ctlname);
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200424 err = __via_add_control(spec, VIA_CTL_WIDGET_ANALOG_MUTE, name, type_idx,
Joseph Chanc577b8a2006-11-29 15:29:40 +0100425 HDA_COMPOSE_AMP_VAL(mix_nid, 3, idx, HDA_INPUT));
426 if (err < 0)
427 return err;
428 return 0;
429}
430
Takashi Iwai5d417622011-06-20 11:32:27 +0200431#define get_connection_index(codec, mux, nid) \
Takashi Iwai8d087c72011-06-28 12:45:47 +0200432 snd_hda_get_conn_index(codec, mux, nid, 0)
Takashi Iwai5d417622011-06-20 11:32:27 +0200433
Takashi Iwai8df2a312011-06-21 11:48:29 +0200434static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
435 unsigned int mask)
436{
Takashi Iwaia934d5a2011-06-21 14:22:14 +0200437 unsigned int caps;
438 if (!nid)
439 return false;
440 caps = get_wcaps(codec, nid);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200441 if (dir == HDA_INPUT)
442 caps &= AC_WCAP_IN_AMP;
443 else
444 caps &= AC_WCAP_OUT_AMP;
445 if (!caps)
446 return false;
447 if (query_amp_caps(codec, nid, dir) & mask)
448 return true;
449 return false;
450}
451
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200452#define have_mute(codec, nid, dir) \
453 check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
Takashi Iwai8df2a312011-06-21 11:48:29 +0200454
Lydia Wangd69607b32011-07-08 14:02:52 +0800455/* enable/disable the output-route mixers */
456static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
Takashi Iwai3214b962011-07-18 12:49:25 +0200457 hda_nid_t mix_nid, int idx, bool enable)
Lydia Wangd69607b32011-07-08 14:02:52 +0800458{
459 int i, num, val;
Lydia Wangd69607b32011-07-08 14:02:52 +0800460
461 if (!path)
462 return;
463 num = snd_hda_get_conn_list(codec, mix_nid, NULL);
Lydia Wangd69607b32011-07-08 14:02:52 +0800464 for (i = 0; i < num; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +0200465 if (i == idx)
466 val = AMP_IN_UNMUTE(i);
467 else
468 val = AMP_IN_MUTE(i);
Lydia Wangd69607b32011-07-08 14:02:52 +0800469 snd_hda_codec_write(codec, mix_nid, 0,
470 AC_VERB_SET_AMP_GAIN_MUTE, val);
471 }
472}
473
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200474/* enable/disable the output-route */
475static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
476 bool enable, bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200477{
Lydia Wangd69607b32011-07-08 14:02:52 +0800478 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200479 int i;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200480 for (i = 0; i < path->depth; i++) {
481 hda_nid_t src, dst;
482 int idx = path->idx[i];
483 src = path->path[i];
484 if (i < path->depth - 1)
485 dst = path->path[i + 1];
486 else
487 dst = 0;
488 if (enable && path->multi[i])
489 snd_hda_codec_write(codec, dst, 0,
490 AC_VERB_SET_CONNECT_SEL, idx);
Takashi Iwai3214b962011-07-18 12:49:25 +0200491 if (!force && (dst == spec->aa_mix_nid))
Lydia Wange5e14682011-07-01 10:55:07 +0800492 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +0200493 if (have_mute(codec, dst, HDA_INPUT))
494 activate_output_mix(codec, path, dst, idx, enable);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200495 if (!force && (src == path->vol_ctl || src == path->mute_ctl))
496 continue;
497 if (have_mute(codec, src, HDA_OUTPUT)) {
498 int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
499 snd_hda_codec_write(codec, src, 0,
500 AC_VERB_SET_AMP_GAIN_MUTE, val);
501 }
502 }
Takashi Iwai5d417622011-06-20 11:32:27 +0200503}
504
505/* set the given pin as output */
506static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
507 int pin_type)
508{
509 if (!pin)
510 return;
511 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
512 pin_type);
513 if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
514 snd_hda_codec_write(codec, pin, 0,
Takashi Iwaid3a11e62009-07-07 13:43:35 +0200515 AC_VERB_SET_EAPD_BTLENABLE, 0x02);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100516}
517
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200518static void via_auto_init_output(struct hda_codec *codec,
519 struct nid_path *path, int pin_type,
Takashi Iwai3214b962011-07-18 12:49:25 +0200520 bool force)
Takashi Iwai5d417622011-06-20 11:32:27 +0200521{
Takashi Iwai5d417622011-06-20 11:32:27 +0200522 unsigned int caps;
Lydia Wangd69607b32011-07-08 14:02:52 +0800523 hda_nid_t pin;
Takashi Iwai5d417622011-06-20 11:32:27 +0200524
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200525 if (!path->depth)
Takashi Iwai5d417622011-06-20 11:32:27 +0200526 return;
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200527 pin = path->path[path->depth - 1];
Takashi Iwai5d417622011-06-20 11:32:27 +0200528
529 init_output_pin(codec, pin, pin_type);
530 caps = query_amp_caps(codec, pin, HDA_OUTPUT);
531 if (caps & AC_AMPCAP_MUTE) {
532 unsigned int val;
533 val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
534 snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_AMP_GAIN_MUTE,
535 AMP_OUT_MUTE | val);
536 }
Lydia Wangd69607b32011-07-08 14:02:52 +0800537 activate_output_path(codec, path, true, force);
Takashi Iwai5d417622011-06-20 11:32:27 +0200538}
539
Joseph Chanc577b8a2006-11-29 15:29:40 +0100540static void via_auto_init_multi_out(struct hda_codec *codec)
541{
542 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200543 struct nid_path *path;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100544 int i;
545
Takashi Iwai3214b962011-07-18 12:49:25 +0200546 for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++) {
547 path = &spec->out_path[i];
548 if (!i && spec->aamix_mode && spec->out_mix_path.depth)
549 path = &spec->out_mix_path;
550 via_auto_init_output(codec, path, PIN_OUT, true);
551 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100552}
553
554static void via_auto_init_hp_out(struct hda_codec *codec)
555{
556 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200557 int shared = spec->hp_indep_shared;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100558
Takashi Iwai3214b962011-07-18 12:49:25 +0200559 if (!spec->hp_path.depth) {
560 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP, true);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200561 return;
562 }
563 if (spec->hp_independent_mode) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200564 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200565 activate_output_path(codec, &spec->hp_mix_path, false, false);
566 if (shared)
567 activate_output_path(codec, &spec->out_path[shared],
568 false, false);
569 via_auto_init_output(codec, &spec->hp_indep_path, PIN_HP, true);
570 } else if (spec->aamix_mode) {
571 activate_output_path(codec, &spec->hp_path, false, false);
572 via_auto_init_output(codec, &spec->hp_mix_path, PIN_HP, true);
573 } else {
574 activate_output_path(codec, &spec->hp_mix_path, false, false);
575 via_auto_init_output(codec, &spec->hp_path, PIN_HP, true);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200576 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100577}
578
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200579static void via_auto_init_speaker_out(struct hda_codec *codec)
580{
581 struct via_spec *spec = codec->spec;
582
Takashi Iwai3214b962011-07-18 12:49:25 +0200583 if (!spec->autocfg.speaker_outs)
584 return;
585 if (!spec->speaker_path.depth) {
586 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT,
587 true);
588 return;
589 }
590 if (!spec->aamix_mode) {
591 activate_output_path(codec, &spec->speaker_mix_path,
592 false, false);
Takashi Iwaibac4b922011-07-04 17:35:51 +0200593 via_auto_init_output(codec, &spec->speaker_path, PIN_OUT,
Takashi Iwai3214b962011-07-18 12:49:25 +0200594 true);
595 } else {
596 activate_output_path(codec, &spec->speaker_path, false, false);
597 via_auto_init_output(codec, &spec->speaker_mix_path, PIN_OUT,
598 true);
599 }
Takashi Iwai4a918ff2011-06-20 12:39:26 +0200600}
601
Takashi Iwaif4a78282011-06-17 18:46:48 +0200602static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200603static void via_hp_automute(struct hda_codec *codec);
Clemens Ladisch32e01912010-07-12 16:28:50 +0200604
Joseph Chanc577b8a2006-11-29 15:29:40 +0100605static void via_auto_init_analog_input(struct hda_codec *codec)
606{
607 struct via_spec *spec = codec->spec;
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200608 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai096a8852011-06-20 12:09:02 +0200609 hda_nid_t conn[HDA_MAX_CONNECTIONS];
Clemens Ladisch32e01912010-07-12 16:28:50 +0200610 unsigned int ctl;
Takashi Iwai096a8852011-06-20 12:09:02 +0200611 int i, num_conns;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100612
Takashi Iwai096a8852011-06-20 12:09:02 +0200613 /* init ADCs */
614 for (i = 0; i < spec->num_adc_nids; i++) {
615 snd_hda_codec_write(codec, spec->adc_nids[i], 0,
616 AC_VERB_SET_AMP_GAIN_MUTE,
617 AMP_IN_UNMUTE(0));
618 }
619
620 /* init pins */
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200621 for (i = 0; i < cfg->num_inputs; i++) {
622 hda_nid_t nid = cfg->inputs[i].pin;
Takashi Iwaif4a78282011-06-17 18:46:48 +0200623 if (spec->smart51_enabled && is_smart51_pins(codec, nid))
Clemens Ladisch32e01912010-07-12 16:28:50 +0200624 ctl = PIN_OUT;
David Henningsson30649672011-02-21 10:23:18 +0100625 else if (cfg->inputs[i].type == AUTO_PIN_MIC)
Clemens Ladisch32e01912010-07-12 16:28:50 +0200626 ctl = PIN_VREF50;
627 else
628 ctl = PIN_IN;
Joseph Chanc577b8a2006-11-29 15:29:40 +0100629 snd_hda_codec_write(codec, nid, 0,
Clemens Ladisch32e01912010-07-12 16:28:50 +0200630 AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
Joseph Chanc577b8a2006-11-29 15:29:40 +0100631 }
Takashi Iwai096a8852011-06-20 12:09:02 +0200632
633 /* init input-src */
634 for (i = 0; i < spec->num_adc_nids; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +0200635 int adc_idx = spec->inputs[spec->cur_mux[i]].adc_idx;
636 if (spec->mux_nids[adc_idx]) {
637 int mux_idx = spec->inputs[spec->cur_mux[i]].mux_idx;
638 snd_hda_codec_write(codec, spec->mux_nids[adc_idx], 0,
639 AC_VERB_SET_CONNECT_SEL,
640 mux_idx);
641 }
642 if (spec->dyn_adc_switch)
643 break; /* only one input-src */
Takashi Iwai096a8852011-06-20 12:09:02 +0200644 }
645
646 /* init aa-mixer */
647 if (!spec->aa_mix_nid)
648 return;
649 num_conns = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
650 ARRAY_SIZE(conn));
651 for (i = 0; i < num_conns; i++) {
652 unsigned int caps = get_wcaps(codec, conn[i]);
653 if (get_wcaps_type(caps) == AC_WID_PIN)
654 snd_hda_codec_write(codec, spec->aa_mix_nid, 0,
655 AC_VERB_SET_AMP_GAIN_MUTE,
656 AMP_IN_MUTE(i));
657 }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100658}
Lydia Wangf5271102009-10-10 19:07:35 +0800659
660static void set_pin_power_state(struct hda_codec *codec, hda_nid_t nid,
661 unsigned int *affected_parm)
662{
663 unsigned parm;
664 unsigned def_conf = snd_hda_codec_get_pincfg(codec, nid);
665 unsigned no_presence = (def_conf & AC_DEFCFG_MISC)
666 >> AC_DEFCFG_MISC_SHIFT
667 & AC_DEFCFG_MISC_NO_PRESENCE; /* do not support pin sense */
Lydia Wang1564b282009-10-10 19:07:52 +0800668 struct via_spec *spec = codec->spec;
Takashi Iwai24088a52011-06-17 16:59:21 +0200669 unsigned present = 0;
670
671 no_presence |= spec->no_pin_power_ctl;
672 if (!no_presence)
673 present = snd_hda_jack_detect(codec, nid);
Takashi Iwaif4a78282011-06-17 18:46:48 +0200674 if ((spec->smart51_enabled && is_smart51_pins(codec, nid))
Lydia Wang1564b282009-10-10 19:07:52 +0800675 || ((no_presence || present)
676 && get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)) {
Lydia Wangf5271102009-10-10 19:07:35 +0800677 *affected_parm = AC_PWRST_D0; /* if it's connected */
678 parm = AC_PWRST_D0;
679 } else
680 parm = AC_PWRST_D3;
681
682 snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE, parm);
683}
684
Takashi Iwai24088a52011-06-17 16:59:21 +0200685static int via_pin_power_ctl_info(struct snd_kcontrol *kcontrol,
686 struct snd_ctl_elem_info *uinfo)
687{
688 static const char * const texts[] = {
689 "Disabled", "Enabled"
690 };
691
692 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
693 uinfo->count = 1;
694 uinfo->value.enumerated.items = 2;
695 if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
696 uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
697 strcpy(uinfo->value.enumerated.name,
698 texts[uinfo->value.enumerated.item]);
699 return 0;
700}
701
702static int via_pin_power_ctl_get(struct snd_kcontrol *kcontrol,
703 struct snd_ctl_elem_value *ucontrol)
704{
705 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
706 struct via_spec *spec = codec->spec;
707 ucontrol->value.enumerated.item[0] = !spec->no_pin_power_ctl;
708 return 0;
709}
710
711static int via_pin_power_ctl_put(struct snd_kcontrol *kcontrol,
712 struct snd_ctl_elem_value *ucontrol)
713{
714 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
715 struct via_spec *spec = codec->spec;
716 unsigned int val = !ucontrol->value.enumerated.item[0];
717
718 if (val == spec->no_pin_power_ctl)
719 return 0;
720 spec->no_pin_power_ctl = val;
721 set_widgets_power_state(codec);
722 return 1;
723}
724
725static const struct snd_kcontrol_new via_pin_power_ctl_enum = {
726 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
727 .name = "Dynamic Power-Control",
728 .info = via_pin_power_ctl_info,
729 .get = via_pin_power_ctl_get,
730 .put = via_pin_power_ctl_put,
731};
732
733
Harald Welte0aa62ae2008-09-09 15:58:27 +0800734static int via_independent_hp_info(struct snd_kcontrol *kcontrol,
735 struct snd_ctl_elem_info *uinfo)
736{
Takashi Iwai8df2a312011-06-21 11:48:29 +0200737 static const char * const texts[] = { "OFF", "ON" };
738
739 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
740 uinfo->count = 1;
741 uinfo->value.enumerated.items = 2;
742 if (uinfo->value.enumerated.item >= 2)
743 uinfo->value.enumerated.item = 1;
744 strcpy(uinfo->value.enumerated.name,
745 texts[uinfo->value.enumerated.item]);
746 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800747}
748
749static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
750 struct snd_ctl_elem_value *ucontrol)
751{
752 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
Lydia Wangcdc17842009-10-10 19:07:47 +0800753 struct via_spec *spec = codec->spec;
Lydia Wangcdc17842009-10-10 19:07:47 +0800754
Takashi Iwaiece8d042011-06-19 16:24:21 +0200755 ucontrol->value.enumerated.item[0] = spec->hp_independent_mode;
Lydia Wangcdc17842009-10-10 19:07:47 +0800756 return 0;
757}
758
Harald Welte0aa62ae2008-09-09 15:58:27 +0800759static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
760 struct snd_ctl_elem_value *ucontrol)
761{
762 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
763 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +0200764 int cur, shared;
Takashi Iwai8df2a312011-06-21 11:48:29 +0200765
Takashi Iwai25250502011-06-30 17:24:47 +0200766 /* no independent-hp status change during PCM playback is running */
767 if (spec->num_active_streams)
768 return -EBUSY;
769
770 cur = !!ucontrol->value.enumerated.item[0];
771 if (spec->hp_independent_mode == cur)
772 return 0;
773 spec->hp_independent_mode = cur;
Takashi Iwai3214b962011-07-18 12:49:25 +0200774 shared = spec->hp_indep_shared;
Takashi Iwai25250502011-06-30 17:24:47 +0200775 if (cur) {
Takashi Iwai3214b962011-07-18 12:49:25 +0200776 activate_output_path(codec, &spec->hp_mix_path, false, false);
777 if (shared)
778 activate_output_path(codec, &spec->out_path[shared],
Takashi Iwai25250502011-06-30 17:24:47 +0200779 false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200780 activate_output_path(codec, &spec->hp_path, true, false);
Takashi Iwai09a9ad692011-06-21 15:57:44 +0200781 } else {
782 activate_output_path(codec, &spec->hp_path, false, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200783 if (shared)
784 activate_output_path(codec, &spec->out_path[shared],
Takashi Iwai25250502011-06-30 17:24:47 +0200785 true, false);
Takashi Iwai3214b962011-07-18 12:49:25 +0200786 activate_output_path(codec, &spec->hp_mix_path, true, false);
Takashi Iwai8df2a312011-06-21 11:48:29 +0200787 }
Harald Welte0aa62ae2008-09-09 15:58:27 +0800788
Lydia Wangce0e5a92011-03-22 16:22:37 +0800789 /* update jack power state */
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800790 set_widgets_power_state(codec);
Takashi Iwai6e969d92011-07-11 11:28:13 +0200791 via_hp_automute(codec);
Takashi Iwai25250502011-06-30 17:24:47 +0200792 return 1;
Harald Welte0aa62ae2008-09-09 15:58:27 +0800793}
794
Takashi Iwaiece8d042011-06-19 16:24:21 +0200795static const struct snd_kcontrol_new via_hp_mixer = {
796 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
797 .name = "Independent HP",
798 .info = via_independent_hp_info,
799 .get = via_independent_hp_get,
800 .put = via_independent_hp_put,
Harald Welte0aa62ae2008-09-09 15:58:27 +0800801};
802
Takashi Iwai3d83e572010-04-14 14:36:23 +0200803static int via_hp_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100804{
Takashi Iwai3d83e572010-04-14 14:36:23 +0200805 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100806 struct snd_kcontrol_new *knew;
807 hda_nid_t nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100808
Takashi Iwaiece8d042011-06-19 16:24:21 +0200809 nid = spec->autocfg.hp_pins[0];
810 knew = via_clone_control(spec, &via_hp_mixer);
Takashi Iwai3d83e572010-04-14 14:36:23 +0200811 if (knew == NULL)
812 return -ENOMEM;
813
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100814 knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100815
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100816 return 0;
817}
818
Lydia Wang1564b282009-10-10 19:07:52 +0800819static void notify_aa_path_ctls(struct hda_codec *codec)
820{
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200821 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800822 int i;
Lydia Wang1564b282009-10-10 19:07:52 +0800823
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200824 for (i = 0; i < spec->smart51_nums; i++) {
825 struct snd_kcontrol *ctl;
826 struct snd_ctl_elem_id id;
827 memset(&id, 0, sizeof(id));
828 id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
829 sprintf(id.name, "%s Playback Volume", spec->smart51_labels[i]);
Lydia Wang525566c2011-04-28 16:03:39 +0800830 ctl = snd_hda_find_mixer_ctl(codec, id.name);
831 if (ctl)
832 snd_ctl_notify(codec->bus->card,
833 SNDRV_CTL_EVENT_MASK_VALUE,
834 &ctl->id);
Lydia Wang1564b282009-10-10 19:07:52 +0800835 }
836}
837
838static void mute_aa_path(struct hda_codec *codec, int mute)
839{
840 struct via_spec *spec = codec->spec;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200841 int val = mute ? HDA_AMP_MUTE : HDA_AMP_UNMUTE;
Lydia Wang1564b282009-10-10 19:07:52 +0800842 int i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200843
Lydia Wang1564b282009-10-10 19:07:52 +0800844 /* check AA path's mute status */
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200845 for (i = 0; i < spec->smart51_nums; i++) {
846 if (spec->smart51_idxs[i] < 0)
847 continue;
848 snd_hda_codec_amp_stereo(codec, spec->aa_mix_nid,
849 HDA_INPUT, spec->smart51_idxs[i],
Lydia Wang1564b282009-10-10 19:07:52 +0800850 HDA_AMP_MUTE, val);
851 }
852}
Takashi Iwaif4a78282011-06-17 18:46:48 +0200853
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200854static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin)
855{
856 struct via_spec *spec = codec->spec;
857 int i;
858
859 for (i = 0; i < spec->smart51_nums; i++)
860 if (spec->smart51_pins[i] == pin)
861 return true;
862 return false;
863}
864
Lydia Wang1564b282009-10-10 19:07:52 +0800865static int via_smart51_get(struct snd_kcontrol *kcontrol,
866 struct snd_ctl_elem_value *ucontrol)
867{
868 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
869 struct via_spec *spec = codec->spec;
Lydia Wang1564b282009-10-10 19:07:52 +0800870
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200871 *ucontrol->value.integer.value = spec->smart51_enabled;
Lydia Wang1564b282009-10-10 19:07:52 +0800872 return 0;
873}
874
875static int via_smart51_put(struct snd_kcontrol *kcontrol,
876 struct snd_ctl_elem_value *ucontrol)
877{
878 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
879 struct via_spec *spec = codec->spec;
880 int out_in = *ucontrol->value.integer.value
881 ? AC_PINCTL_OUT_EN : AC_PINCTL_IN_EN;
Lydia Wang1564b282009-10-10 19:07:52 +0800882 int i;
883
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200884 for (i = 0; i < spec->smart51_nums; i++) {
885 hda_nid_t nid = spec->smart51_pins[i];
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200886 unsigned int parm;
887
Takashi Iwai7b315bb2010-08-30 13:06:30 +0200888 parm = snd_hda_codec_read(codec, nid, 0,
889 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
890 parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
891 parm |= out_in;
892 snd_hda_codec_write(codec, nid, 0,
893 AC_VERB_SET_PIN_WIDGET_CONTROL,
894 parm);
895 if (out_in == AC_PINCTL_OUT_EN) {
896 mute_aa_path(codec, 1);
897 notify_aa_path_ctls(codec);
898 }
Lydia Wang1564b282009-10-10 19:07:52 +0800899 }
900 spec->smart51_enabled = *ucontrol->value.integer.value;
Lydia Wang3e95b9a2011-03-23 15:13:28 +0800901 set_widgets_power_state(codec);
Lydia Wang1564b282009-10-10 19:07:52 +0800902 return 1;
903}
904
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200905static const struct snd_kcontrol_new via_smart51_mixer = {
906 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
907 .name = "Smart 5.1",
908 .count = 1,
Takashi Iwaif2b1c9f2011-06-21 16:52:39 +0200909 .info = snd_ctl_boolean_mono_info,
Takashi Iwai5f4b36d2011-06-17 14:55:02 +0200910 .get = via_smart51_get,
911 .put = via_smart51_put,
Lydia Wang1564b282009-10-10 19:07:52 +0800912};
913
Takashi Iwaif4a78282011-06-17 18:46:48 +0200914static int via_smart51_build(struct hda_codec *codec)
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100915{
Takashi Iwaif4a78282011-06-17 18:46:48 +0200916 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100917
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200918 if (!spec->smart51_nums)
Lydia Wangcb34c202011-04-27 17:44:16 +0800919 return 0;
Takashi Iwaie3d7a142011-06-20 13:52:33 +0200920 if (!via_clone_control(spec, &via_smart51_mixer))
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100921 return -ENOMEM;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +0100922 return 0;
923}
924
Takashi Iwaiada509e2011-06-20 15:40:19 +0200925/* check AA path's mute status */
926static bool is_aa_path_mute(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800927{
Lydia Wangf5271102009-10-10 19:07:35 +0800928 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200929 const struct hda_amp_list *p;
930 int i, ch, v;
931
932 for (i = 0; i < spec->num_loopbacks; i++) {
933 p = &spec->loopback_list[i];
934 for (ch = 0; ch < 2; ch++) {
935 v = snd_hda_codec_amp_read(codec, p->nid, ch, p->dir,
936 p->idx);
937 if (!(v & HDA_AMP_MUTE) && v > 0)
938 return false;
Lydia Wangf5271102009-10-10 19:07:35 +0800939 }
940 }
Takashi Iwaiada509e2011-06-20 15:40:19 +0200941 return true;
Lydia Wangf5271102009-10-10 19:07:35 +0800942}
943
944/* enter/exit analog low-current mode */
Takashi Iwaiada509e2011-06-20 15:40:19 +0200945static void analog_low_current_mode(struct hda_codec *codec)
Lydia Wangf5271102009-10-10 19:07:35 +0800946{
947 struct via_spec *spec = codec->spec;
Takashi Iwaiada509e2011-06-20 15:40:19 +0200948 bool enable;
949 unsigned int verb, parm;
Lydia Wangf5271102009-10-10 19:07:35 +0800950
Takashi Iwaiada509e2011-06-20 15:40:19 +0200951 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0);
Lydia Wangf5271102009-10-10 19:07:35 +0800952
953 /* decide low current mode's verb & parameter */
954 switch (spec->codec_type) {
955 case VT1708B_8CH:
956 case VT1708B_4CH:
957 verb = 0xf70;
958 parm = enable ? 0x02 : 0x00; /* 0x02: 2/3x, 0x00: 1x */
959 break;
960 case VT1708S:
Lydia Wangeb7188c2009-10-10 19:08:34 +0800961 case VT1718S:
Lydia Wangf3db4232009-10-10 19:08:41 +0800962 case VT1716S:
Lydia Wangf5271102009-10-10 19:07:35 +0800963 verb = 0xf73;
964 parm = enable ? 0x51 : 0xe1; /* 0x51: 4/28x, 0xe1: 1x */
965 break;
966 case VT1702:
967 verb = 0xf73;
968 parm = enable ? 0x01 : 0x1d; /* 0x01: 4/40x, 0x1d: 1x */
969 break;
Lydia Wang25eaba22009-10-10 19:08:43 +0800970 case VT2002P:
Lydia Wangab6734e2009-10-10 19:08:46 +0800971 case VT1812:
Lydia Wang118909562011-03-23 17:57:34 +0800972 case VT1802:
Lydia Wang25eaba22009-10-10 19:08:43 +0800973 verb = 0xf93;
974 parm = enable ? 0x00 : 0xe0; /* 0x00: 4/40x, 0xe0: 1x */
975 break;
Lydia Wangf5271102009-10-10 19:07:35 +0800976 default:
977 return; /* other codecs are not supported */
978 }
979 /* send verb */
980 snd_hda_codec_write(codec, codec->afg, 0, verb, parm);
981}
982
Joseph Chanc577b8a2006-11-29 15:29:40 +0100983/*
984 * generic initialization of ADC, input mixers and output mixers
985 */
Takashi Iwai096a8852011-06-20 12:09:02 +0200986static const struct hda_verb vt1708_init_verbs[] = {
Lydia Wangaa266fc2011-03-24 12:41:01 +0800987 /* power down jack detect function */
988 {0x1, 0xf81, 0x1},
Josepch Chanf7278fd2007-12-13 16:40:40 +0100989 { }
Joseph Chanc577b8a2006-11-29 15:29:40 +0100990};
991
Takashi Iwaiada509e2011-06-20 15:40:19 +0200992static void set_stream_active(struct hda_codec *codec, bool active)
Takashi Iwai7eb56e82011-06-18 16:40:14 +0200993{
Takashi Iwaiada509e2011-06-20 15:40:19 +0200994 struct via_spec *spec = codec->spec;
995
996 if (active)
997 spec->num_active_streams++;
998 else
999 spec->num_active_streams--;
1000 analog_low_current_mode(codec);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001001}
1002
Takashi Iwaiece8d042011-06-19 16:24:21 +02001003static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001004 struct hda_codec *codec,
1005 struct snd_pcm_substream *substream)
1006{
1007 struct via_spec *spec = codec->spec;
Takashi Iwai25250502011-06-30 17:24:47 +02001008 const struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001009 int err;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001010
Takashi Iwai25250502011-06-30 17:24:47 +02001011 spec->multiout.hp_nid = 0;
1012 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
1013 if (!spec->hp_independent_mode) {
1014 if (!spec->hp_indep_shared)
1015 spec->multiout.hp_nid = spec->hp_dac_nid;
1016 } else {
1017 if (spec->hp_indep_shared)
1018 spec->multiout.num_dacs = cfg->line_outs - 1;
1019 }
1020 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001021 set_stream_active(codec, true);
1022 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1023 hinfo);
1024 if (err < 0) {
1025 spec->multiout.hp_nid = 0;
1026 set_stream_active(codec, false);
1027 return err;
1028 }
1029 return 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001030}
1031
Takashi Iwaiece8d042011-06-19 16:24:21 +02001032static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
Takashi Iwai9af74212011-06-18 16:17:45 +02001033 struct hda_codec *codec,
1034 struct snd_pcm_substream *substream)
1035{
Takashi Iwaiece8d042011-06-19 16:24:21 +02001036 struct via_spec *spec = codec->spec;
1037
1038 spec->multiout.hp_nid = 0;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001039 set_stream_active(codec, false);
Takashi Iwai9af74212011-06-18 16:17:45 +02001040 return 0;
1041}
1042
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001043static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1044 struct hda_codec *codec,
1045 struct snd_pcm_substream *substream)
1046{
1047 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001048
Takashi Iwaiece8d042011-06-19 16:24:21 +02001049 if (snd_BUG_ON(!spec->hp_dac_nid))
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001050 return -EINVAL;
Takashi Iwaiece8d042011-06-19 16:24:21 +02001051 if (!spec->hp_independent_mode || spec->multiout.hp_nid)
1052 return -EBUSY;
Takashi Iwaiada509e2011-06-20 15:40:19 +02001053 set_stream_active(codec, true);
Takashi Iwaiece8d042011-06-19 16:24:21 +02001054 return 0;
1055}
1056
1057static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1058 struct hda_codec *codec,
1059 struct snd_pcm_substream *substream)
1060{
Takashi Iwaiada509e2011-06-20 15:40:19 +02001061 set_stream_active(codec, false);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001062 return 0;
1063}
1064
1065static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1066 struct hda_codec *codec,
1067 unsigned int stream_tag,
1068 unsigned int format,
1069 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001070{
1071 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001072
Takashi Iwaiece8d042011-06-19 16:24:21 +02001073 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1074 format, substream);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001075 vt1708_start_hp_work(spec);
1076 return 0;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001077}
1078
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001079static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1080 struct hda_codec *codec,
1081 unsigned int stream_tag,
1082 unsigned int format,
1083 struct snd_pcm_substream *substream)
Harald Welte0aa62ae2008-09-09 15:58:27 +08001084{
1085 struct via_spec *spec = codec->spec;
Harald Welte0aa62ae2008-09-09 15:58:27 +08001086
Takashi Iwaiece8d042011-06-19 16:24:21 +02001087 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1088 stream_tag, 0, format);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001089 vt1708_start_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001090 return 0;
1091}
1092
1093static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1094 struct hda_codec *codec,
1095 struct snd_pcm_substream *substream)
1096{
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_multi_out_analog_cleanup(codec, &spec->multiout);
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001100 vt1708_stop_hp_work(spec);
1101 return 0;
1102}
1103
1104static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1105 struct hda_codec *codec,
1106 struct snd_pcm_substream *substream)
1107{
1108 struct via_spec *spec = codec->spec;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001109
Takashi Iwaiece8d042011-06-19 16:24:21 +02001110 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001111 vt1708_stop_hp_work(spec);
Harald Welte0aa62ae2008-09-09 15:58:27 +08001112 return 0;
1113}
1114
Joseph Chanc577b8a2006-11-29 15:29:40 +01001115/*
1116 * Digital out
1117 */
1118static int via_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
1119 struct hda_codec *codec,
1120 struct snd_pcm_substream *substream)
1121{
1122 struct via_spec *spec = codec->spec;
1123 return snd_hda_multi_out_dig_open(codec, &spec->multiout);
1124}
1125
1126static int via_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
1127 struct hda_codec *codec,
1128 struct snd_pcm_substream *substream)
1129{
1130 struct via_spec *spec = codec->spec;
1131 return snd_hda_multi_out_dig_close(codec, &spec->multiout);
1132}
1133
Harald Welte5691ec72008-09-15 22:42:26 +08001134static int via_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
Harald Welte98aa34c2008-09-09 16:02:09 +08001135 struct hda_codec *codec,
1136 unsigned int stream_tag,
1137 unsigned int format,
1138 struct snd_pcm_substream *substream)
1139{
1140 struct via_spec *spec = codec->spec;
Takashi Iwai9da29272009-05-07 16:31:14 +02001141 return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
1142 stream_tag, format, substream);
1143}
Harald Welte5691ec72008-09-15 22:42:26 +08001144
Takashi Iwai9da29272009-05-07 16:31:14 +02001145static int via_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
1146 struct hda_codec *codec,
1147 struct snd_pcm_substream *substream)
1148{
1149 struct via_spec *spec = codec->spec;
1150 snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
Harald Welte98aa34c2008-09-09 16:02:09 +08001151 return 0;
1152}
1153
Joseph Chanc577b8a2006-11-29 15:29:40 +01001154/*
1155 * Analog capture
1156 */
1157static int via_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1158 struct hda_codec *codec,
1159 unsigned int stream_tag,
1160 unsigned int format,
1161 struct snd_pcm_substream *substream)
1162{
1163 struct via_spec *spec = codec->spec;
1164
1165 snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
1166 stream_tag, 0, format);
1167 return 0;
1168}
1169
1170static int via_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1171 struct hda_codec *codec,
1172 struct snd_pcm_substream *substream)
1173{
1174 struct via_spec *spec = codec->spec;
Takashi Iwai888afa12008-03-18 09:57:50 +01001175 snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001176 return 0;
1177}
1178
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001179/* analog capture with dynamic ADC switching */
1180static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1181 struct hda_codec *codec,
1182 unsigned int stream_tag,
1183 unsigned int format,
1184 struct snd_pcm_substream *substream)
1185{
1186 struct via_spec *spec = codec->spec;
1187 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1188
1189 spec->cur_adc = spec->adc_nids[adc_idx];
1190 spec->cur_adc_stream_tag = stream_tag;
1191 spec->cur_adc_format = format;
1192 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
1193 return 0;
1194}
1195
1196static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1197 struct hda_codec *codec,
1198 struct snd_pcm_substream *substream)
1199{
1200 struct via_spec *spec = codec->spec;
1201
1202 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1203 spec->cur_adc = 0;
1204 return 0;
1205}
1206
1207/* re-setup the stream if running; called from input-src put */
1208static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1209{
1210 struct via_spec *spec = codec->spec;
1211 int adc_idx = spec->inputs[cur].adc_idx;
1212 hda_nid_t adc = spec->adc_nids[adc_idx];
1213
1214 if (spec->cur_adc && spec->cur_adc != adc) {
1215 /* stream is running, let's swap the current ADC */
1216 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
1217 spec->cur_adc = adc;
1218 snd_hda_codec_setup_stream(codec, adc,
1219 spec->cur_adc_stream_tag, 0,
1220 spec->cur_adc_format);
1221 return true;
1222 }
1223 return false;
1224}
1225
Takashi Iwai9af74212011-06-18 16:17:45 +02001226static const struct hda_pcm_stream via_pcm_analog_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001227 .substreams = 1,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001228 .channels_min = 2,
1229 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001230 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001231 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001232 .open = via_playback_multi_pcm_open,
1233 .close = via_playback_multi_pcm_close,
Harald Welte0aa62ae2008-09-09 15:58:27 +08001234 .prepare = via_playback_multi_pcm_prepare,
1235 .cleanup = via_playback_multi_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001236 },
1237};
1238
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001239static const struct hda_pcm_stream via_pcm_hp_playback = {
1240 .substreams = 1,
1241 .channels_min = 2,
1242 .channels_max = 2,
1243 /* NID is set in via_build_pcms */
1244 .ops = {
1245 .open = via_playback_hp_pcm_open,
Takashi Iwaiece8d042011-06-19 16:24:21 +02001246 .close = via_playback_hp_pcm_close,
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001247 .prepare = via_playback_hp_pcm_prepare,
1248 .cleanup = via_playback_hp_pcm_cleanup
1249 },
1250};
1251
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001252static const struct hda_pcm_stream vt1708_pcm_analog_s16_playback = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001253 .substreams = 1,
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001254 .channels_min = 2,
1255 .channels_max = 8,
Takashi Iwai9af74212011-06-18 16:17:45 +02001256 /* NID is set in via_build_pcms */
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001257 /* We got noisy outputs on the right channel on VT1708 when
1258 * 24bit samples are used. Until any workaround is found,
1259 * disable the 24bit format, so far.
1260 */
1261 .formats = SNDRV_PCM_FMTBIT_S16_LE,
1262 .ops = {
Takashi Iwaiece8d042011-06-19 16:24:21 +02001263 .open = via_playback_multi_pcm_open,
1264 .close = via_playback_multi_pcm_close,
Lydia Wangc873cc22009-10-10 19:08:21 +08001265 .prepare = via_playback_multi_pcm_prepare,
1266 .cleanup = via_playback_multi_pcm_cleanup
Takashi Iwaibc9b562382008-05-23 17:50:27 +02001267 },
1268};
1269
Takashi Iwai9af74212011-06-18 16:17:45 +02001270static const struct hda_pcm_stream via_pcm_analog_capture = {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001271 .substreams = 1, /* will be changed in via_build_pcms() */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001272 .channels_min = 2,
1273 .channels_max = 2,
Takashi Iwai9af74212011-06-18 16:17:45 +02001274 /* NID is set in via_build_pcms */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001275 .ops = {
1276 .prepare = via_capture_pcm_prepare,
1277 .cleanup = via_capture_pcm_cleanup
1278 },
1279};
1280
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001281static const struct hda_pcm_stream via_pcm_dyn_adc_analog_capture = {
1282 .substreams = 1,
1283 .channels_min = 2,
1284 .channels_max = 2,
1285 /* NID is set in via_build_pcms */
1286 .ops = {
1287 .prepare = via_dyn_adc_capture_pcm_prepare,
1288 .cleanup = via_dyn_adc_capture_pcm_cleanup,
1289 },
1290};
1291
Takashi Iwai9af74212011-06-18 16:17:45 +02001292static const struct hda_pcm_stream via_pcm_digital_playback = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001293 .substreams = 1,
1294 .channels_min = 2,
1295 .channels_max = 2,
1296 /* NID is set in via_build_pcms */
1297 .ops = {
1298 .open = via_dig_playback_pcm_open,
Takashi Iwai6b97eb42007-04-05 14:51:48 +02001299 .close = via_dig_playback_pcm_close,
Takashi Iwai9da29272009-05-07 16:31:14 +02001300 .prepare = via_dig_playback_pcm_prepare,
1301 .cleanup = via_dig_playback_pcm_cleanup
Joseph Chanc577b8a2006-11-29 15:29:40 +01001302 },
1303};
1304
Takashi Iwai9af74212011-06-18 16:17:45 +02001305static const struct hda_pcm_stream via_pcm_digital_capture = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001306 .substreams = 1,
1307 .channels_min = 2,
1308 .channels_max = 2,
1309};
1310
Takashi Iwai370bafb2011-06-20 12:47:45 +02001311/*
1312 * slave controls for virtual master
1313 */
1314static const char * const via_slave_vols[] = {
1315 "Front Playback Volume",
1316 "Surround Playback Volume",
1317 "Center Playback Volume",
1318 "LFE Playback Volume",
1319 "Side Playback Volume",
1320 "Headphone Playback Volume",
1321 "Speaker Playback Volume",
1322 NULL,
1323};
1324
1325static const char * const via_slave_sws[] = {
1326 "Front Playback Switch",
1327 "Surround Playback Switch",
1328 "Center Playback Switch",
1329 "LFE Playback Switch",
1330 "Side Playback Switch",
1331 "Headphone Playback Switch",
1332 "Speaker Playback Switch",
1333 NULL,
1334};
1335
Joseph Chanc577b8a2006-11-29 15:29:40 +01001336static int via_build_controls(struct hda_codec *codec)
1337{
1338 struct via_spec *spec = codec->spec;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001339 struct snd_kcontrol *kctl;
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001340 int err, i;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001341
Takashi Iwai24088a52011-06-17 16:59:21 +02001342 if (spec->set_widgets_power_state)
1343 if (!via_clone_control(spec, &via_pin_power_ctl_enum))
1344 return -ENOMEM;
1345
Joseph Chanc577b8a2006-11-29 15:29:40 +01001346 for (i = 0; i < spec->num_mixers; i++) {
1347 err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
1348 if (err < 0)
1349 return err;
1350 }
1351
1352 if (spec->multiout.dig_out_nid) {
1353 err = snd_hda_create_spdif_out_ctls(codec,
Stephen Warren74b654c2011-06-01 11:14:18 -06001354 spec->multiout.dig_out_nid,
Joseph Chanc577b8a2006-11-29 15:29:40 +01001355 spec->multiout.dig_out_nid);
1356 if (err < 0)
1357 return err;
Takashi Iwai9a081602008-02-12 18:37:26 +01001358 err = snd_hda_create_spdif_share_sw(codec,
1359 &spec->multiout);
1360 if (err < 0)
1361 return err;
1362 spec->multiout.share_spdif = 1;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001363 }
1364 if (spec->dig_in_nid) {
1365 err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
1366 if (err < 0)
1367 return err;
1368 }
Lydia Wang17314372009-10-10 19:07:37 +08001369
Takashi Iwai370bafb2011-06-20 12:47:45 +02001370 /* if we have no master control, let's create it */
1371 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
1372 unsigned int vmaster_tlv[4];
1373 snd_hda_set_vmaster_tlv(codec, spec->multiout.dac_nids[0],
1374 HDA_OUTPUT, vmaster_tlv);
1375 err = snd_hda_add_vmaster(codec, "Master Playback Volume",
1376 vmaster_tlv, via_slave_vols);
1377 if (err < 0)
1378 return err;
1379 }
1380 if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
1381 err = snd_hda_add_vmaster(codec, "Master Playback Switch",
1382 NULL, via_slave_sws);
1383 if (err < 0)
1384 return err;
1385 }
1386
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001387 /* assign Capture Source enums to NID */
1388 kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
1389 for (i = 0; kctl && i < kctl->count; i++) {
Takashi Iwai21949f02009-12-23 08:31:59 +01001390 err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01001391 if (err < 0)
1392 return err;
1393 }
1394
Lydia Wang17314372009-10-10 19:07:37 +08001395 /* init power states */
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001396 set_widgets_power_state(codec);
Takashi Iwaiada509e2011-06-20 15:40:19 +02001397 analog_low_current_mode(codec);
Lydia Wang17314372009-10-10 19:07:37 +08001398
Takashi Iwai603c4012008-07-30 15:01:44 +02001399 via_free_kctls(codec); /* no longer needed */
Joseph Chanc577b8a2006-11-29 15:29:40 +01001400 return 0;
1401}
1402
1403static int via_build_pcms(struct hda_codec *codec)
1404{
1405 struct via_spec *spec = codec->spec;
1406 struct hda_pcm *info = spec->pcm_rec;
1407
1408 codec->num_pcms = 1;
1409 codec->pcm_info = info;
1410
Takashi Iwai82673bc2011-06-17 16:24:21 +02001411 snprintf(spec->stream_name_analog, sizeof(spec->stream_name_analog),
1412 "%s Analog", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001413 info->name = spec->stream_name_analog;
Takashi Iwai9af74212011-06-18 16:17:45 +02001414
1415 if (!spec->stream_analog_playback)
1416 spec->stream_analog_playback = &via_pcm_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001417 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001418 *spec->stream_analog_playback;
Lydia Wang377ff312009-10-10 19:08:55 +08001419 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1420 spec->multiout.dac_nids[0];
Joseph Chanc577b8a2006-11-29 15:29:40 +01001421 info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max =
1422 spec->multiout.max_channels;
Takashi Iwai9af74212011-06-18 16:17:45 +02001423
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001424 if (!spec->stream_analog_capture) {
1425 if (spec->dyn_adc_switch)
1426 spec->stream_analog_capture =
1427 &via_pcm_dyn_adc_analog_capture;
1428 else
1429 spec->stream_analog_capture = &via_pcm_analog_capture;
1430 }
Takashi Iwai9af74212011-06-18 16:17:45 +02001431 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
1432 *spec->stream_analog_capture;
1433 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001434 if (!spec->dyn_adc_switch)
1435 info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams =
1436 spec->num_adc_nids;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001437
1438 if (spec->multiout.dig_out_nid || spec->dig_in_nid) {
1439 codec->num_pcms++;
1440 info++;
Takashi Iwai82673bc2011-06-17 16:24:21 +02001441 snprintf(spec->stream_name_digital,
1442 sizeof(spec->stream_name_digital),
1443 "%s Digital", codec->chip_name);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001444 info->name = spec->stream_name_digital;
Takashi Iwai7ba72ba2008-02-06 14:03:20 +01001445 info->pcm_type = HDA_PCM_TYPE_SPDIF;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001446 if (spec->multiout.dig_out_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001447 if (!spec->stream_digital_playback)
1448 spec->stream_digital_playback =
1449 &via_pcm_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001450 info->stream[SNDRV_PCM_STREAM_PLAYBACK] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001451 *spec->stream_digital_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001452 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
1453 spec->multiout.dig_out_nid;
1454 }
1455 if (spec->dig_in_nid) {
Takashi Iwai9af74212011-06-18 16:17:45 +02001456 if (!spec->stream_digital_capture)
1457 spec->stream_digital_capture =
1458 &via_pcm_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001459 info->stream[SNDRV_PCM_STREAM_CAPTURE] =
Takashi Iwai9af74212011-06-18 16:17:45 +02001460 *spec->stream_digital_capture;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001461 info->stream[SNDRV_PCM_STREAM_CAPTURE].nid =
1462 spec->dig_in_nid;
1463 }
1464 }
1465
Takashi Iwaiece8d042011-06-19 16:24:21 +02001466 if (spec->hp_dac_nid) {
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001467 codec->num_pcms++;
1468 info++;
1469 snprintf(spec->stream_name_hp, sizeof(spec->stream_name_hp),
1470 "%s HP", codec->chip_name);
1471 info->name = spec->stream_name_hp;
1472 info->stream[SNDRV_PCM_STREAM_PLAYBACK] = via_pcm_hp_playback;
1473 info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid =
Takashi Iwaiece8d042011-06-19 16:24:21 +02001474 spec->hp_dac_nid;
Takashi Iwai7eb56e82011-06-18 16:40:14 +02001475 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001476 return 0;
1477}
1478
1479static void via_free(struct hda_codec *codec)
1480{
1481 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001482
1483 if (!spec)
1484 return;
1485
Takashi Iwai603c4012008-07-30 15:01:44 +02001486 via_free_kctls(codec);
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001487 vt1708_stop_hp_work(spec);
Takashi Iwaia86a88e2011-06-22 15:23:25 +02001488 kfree(spec->bind_cap_vol);
1489 kfree(spec->bind_cap_sw);
1490 kfree(spec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001491}
1492
Takashi Iwai64be2852011-06-17 16:51:39 +02001493/* mute/unmute outputs */
1494static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
1495 hda_nid_t *pins, bool mute)
1496{
1497 int i;
Takashi Iwai94994732011-07-11 11:36:44 +02001498 for (i = 0; i < num_pins; i++) {
1499 unsigned int parm = snd_hda_codec_read(codec, pins[i], 0,
1500 AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
1501 if (parm & AC_PINCTL_IN_EN)
1502 continue;
1503 if (mute)
1504 parm &= ~AC_PINCTL_OUT_EN;
1505 else
1506 parm |= AC_PINCTL_OUT_EN;
Takashi Iwai64be2852011-06-17 16:51:39 +02001507 snd_hda_codec_write(codec, pins[i], 0,
Takashi Iwai94994732011-07-11 11:36:44 +02001508 AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
1509 }
Takashi Iwai64be2852011-06-17 16:51:39 +02001510}
1511
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001512/* mute internal speaker if line-out is plugged */
1513static void via_line_automute(struct hda_codec *codec, int present)
1514{
1515 struct via_spec *spec = codec->spec;
1516
1517 if (!spec->autocfg.speaker_outs)
1518 return;
1519 if (!present)
1520 present = snd_hda_jack_detect(codec,
1521 spec->autocfg.line_out_pins[0]);
1522 toggle_output_mutes(codec, spec->autocfg.speaker_outs,
1523 spec->autocfg.speaker_pins,
1524 present);
1525}
1526
Harald Welte69e52a82008-09-09 15:57:32 +08001527/* mute internal speaker if HP is plugged */
1528static void via_hp_automute(struct hda_codec *codec)
1529{
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001530 int present = 0;
Takashi Iwai6e969d92011-07-11 11:28:13 +02001531 int nums;
Harald Welte69e52a82008-09-09 15:57:32 +08001532 struct via_spec *spec = codec->spec;
1533
Takashi Iwai6e969d92011-07-11 11:28:13 +02001534 if (!spec->hp_independent_mode && spec->autocfg.hp_pins[0])
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001535 present = snd_hda_jack_detect(codec, spec->autocfg.hp_pins[0]);
Takashi Iwai6e969d92011-07-11 11:28:13 +02001536
1537 if (spec->smart51_enabled)
1538 nums = spec->autocfg.line_outs + spec->smart51_nums;
1539 else
1540 nums = spec->autocfg.line_outs;
1541 toggle_output_mutes(codec, nums, spec->autocfg.line_out_pins, present);
1542
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001543 via_line_automute(codec, present);
Lydia Wangf3db4232009-10-10 19:08:41 +08001544}
1545
Harald Welte69e52a82008-09-09 15:57:32 +08001546static void via_gpio_control(struct hda_codec *codec)
1547{
1548 unsigned int gpio_data;
1549 unsigned int vol_counter;
1550 unsigned int vol;
1551 unsigned int master_vol;
1552
1553 struct via_spec *spec = codec->spec;
1554
1555 gpio_data = snd_hda_codec_read(codec, codec->afg, 0,
1556 AC_VERB_GET_GPIO_DATA, 0) & 0x03;
1557
1558 vol_counter = (snd_hda_codec_read(codec, codec->afg, 0,
1559 0xF84, 0) & 0x3F0000) >> 16;
1560
1561 vol = vol_counter & 0x1F;
1562 master_vol = snd_hda_codec_read(codec, 0x1A, 0,
1563 AC_VERB_GET_AMP_GAIN_MUTE,
1564 AC_AMP_GET_INPUT);
1565
1566 if (gpio_data == 0x02) {
1567 /* unmute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001568 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1569 AC_VERB_SET_PIN_WIDGET_CONTROL,
1570 PIN_OUT);
Harald Welte69e52a82008-09-09 15:57:32 +08001571 if (vol_counter & 0x20) {
1572 /* decrease volume */
1573 if (vol > master_vol)
1574 vol = master_vol;
1575 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT,
1576 0, HDA_AMP_VOLMASK,
1577 master_vol-vol);
1578 } else {
1579 /* increase volume */
1580 snd_hda_codec_amp_stereo(codec, 0x1A, HDA_INPUT, 0,
1581 HDA_AMP_VOLMASK,
1582 ((master_vol+vol) > 0x2A) ? 0x2A :
1583 (master_vol+vol));
1584 }
1585 } else if (!(gpio_data & 0x02)) {
1586 /* mute line out */
Takashi Iwai3e0693e2011-06-17 16:37:45 +02001587 snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
1588 AC_VERB_SET_PIN_WIDGET_CONTROL,
1589 0);
Harald Welte69e52a82008-09-09 15:57:32 +08001590 }
1591}
1592
1593/* unsolicited event for jack sensing */
1594static void via_unsol_event(struct hda_codec *codec,
1595 unsigned int res)
1596{
1597 res >>= 26;
Lydia Wangec7e7e42011-03-24 12:43:44 +08001598
Lydia Wanga34df192009-10-10 19:08:01 +08001599 if (res & VIA_JACK_EVENT)
Lydia Wang3e95b9a2011-03-23 15:13:28 +08001600 set_widgets_power_state(codec);
Lydia Wangec7e7e42011-03-24 12:43:44 +08001601
1602 res &= ~VIA_JACK_EVENT;
1603
Takashi Iwai21ce0b62011-07-11 10:33:47 +02001604 if (res == VIA_HP_EVENT || res == VIA_LINE_EVENT)
Lydia Wangec7e7e42011-03-24 12:43:44 +08001605 via_hp_automute(codec);
1606 else if (res == VIA_GPIO_EVENT)
1607 via_gpio_control(codec);
Harald Welte69e52a82008-09-09 15:57:32 +08001608}
1609
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001610#ifdef SND_HDA_NEEDS_RESUME
1611static int via_suspend(struct hda_codec *codec, pm_message_t state)
1612{
1613 struct via_spec *spec = codec->spec;
1614 vt1708_stop_hp_work(spec);
1615 return 0;
1616}
1617#endif
1618
Takashi Iwaicb53c622007-08-10 17:21:45 +02001619#ifdef CONFIG_SND_HDA_POWER_SAVE
1620static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid)
1621{
1622 struct via_spec *spec = codec->spec;
1623 return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
1624}
1625#endif
1626
Joseph Chanc577b8a2006-11-29 15:29:40 +01001627/*
1628 */
Takashi Iwai5d417622011-06-20 11:32:27 +02001629
1630static int via_init(struct hda_codec *codec);
1631
Takashi Iwai90dd48a2011-05-02 12:38:19 +02001632static const struct hda_codec_ops via_patch_ops = {
Joseph Chanc577b8a2006-11-29 15:29:40 +01001633 .build_controls = via_build_controls,
1634 .build_pcms = via_build_pcms,
1635 .init = via_init,
1636 .free = via_free,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001637 .unsol_event = via_unsol_event,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08001638#ifdef SND_HDA_NEEDS_RESUME
1639 .suspend = via_suspend,
1640#endif
Takashi Iwaicb53c622007-08-10 17:21:45 +02001641#ifdef CONFIG_SND_HDA_POWER_SAVE
1642 .check_power_status = via_check_power_status,
1643#endif
Joseph Chanc577b8a2006-11-29 15:29:40 +01001644};
1645
Takashi Iwai4a796162011-06-17 17:53:38 +02001646static bool is_empty_dac(struct hda_codec *codec, hda_nid_t dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001647{
Takashi Iwai4a796162011-06-17 17:53:38 +02001648 struct via_spec *spec = codec->spec;
1649 int i;
1650
1651 for (i = 0; i < spec->multiout.num_dacs; i++) {
1652 if (spec->multiout.dac_nids[i] == dac)
1653 return false;
1654 }
Takashi Iwaiece8d042011-06-19 16:24:21 +02001655 if (spec->hp_dac_nid == dac)
Takashi Iwai4a796162011-06-17 17:53:38 +02001656 return false;
1657 return true;
1658}
1659
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001660static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001661 hda_nid_t target_dac, int with_aa_mix,
1662 struct nid_path *path, int depth)
Takashi Iwai4a796162011-06-17 17:53:38 +02001663{
Takashi Iwai3214b962011-07-18 12:49:25 +02001664 struct via_spec *spec = codec->spec;
Takashi Iwai4a796162011-06-17 17:53:38 +02001665 hda_nid_t conn[8];
1666 int i, nums;
1667
Takashi Iwai3214b962011-07-18 12:49:25 +02001668 if (nid == spec->aa_mix_nid) {
1669 if (!with_aa_mix)
1670 return false;
1671 with_aa_mix = 2; /* mark aa-mix is included */
1672 }
1673
Takashi Iwai4a796162011-06-17 17:53:38 +02001674 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 Iwai3214b962011-07-18 12:49:25 +02001678 if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
1679 /* aa-mix is requested but not included? */
1680 if (!(spec->aa_mix_nid && with_aa_mix == 1))
1681 goto found;
1682 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001683 }
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001684 if (depth >= MAX_NID_PATH_DEPTH)
Takashi Iwai4a796162011-06-17 17:53:38 +02001685 return false;
1686 for (i = 0; i < nums; i++) {
1687 unsigned int type;
1688 type = get_wcaps_type(get_wcaps(codec, conn[i]));
Takashi Iwai3214b962011-07-18 12:49:25 +02001689 if (type == AC_WID_AUD_OUT)
Takashi Iwai4a796162011-06-17 17:53:38 +02001690 continue;
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001691 if (__parse_output_path(codec, conn[i], target_dac,
Takashi Iwai3214b962011-07-18 12:49:25 +02001692 with_aa_mix, path, depth + 1))
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001693 goto found;
Takashi Iwai4a796162011-06-17 17:53:38 +02001694 }
1695 return false;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001696
1697 found:
1698 path->path[path->depth] = conn[i];
1699 path->idx[path->depth] = i;
1700 if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
1701 path->multi[path->depth] = 1;
1702 path->depth++;
1703 return true;
Takashi Iwai4a796162011-06-17 17:53:38 +02001704}
1705
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001706static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
Takashi Iwai3214b962011-07-18 12:49:25 +02001707 hda_nid_t target_dac, int with_aa_mix,
1708 struct nid_path *path)
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001709{
Takashi Iwai3214b962011-07-18 12:49:25 +02001710 if (__parse_output_path(codec, nid, target_dac, with_aa_mix, path, 1)) {
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001711 path->path[path->depth] = nid;
1712 path->depth++;
Takashi Iwai3214b962011-07-18 12:49:25 +02001713 snd_printdd("output-path: depth=%d, %02x/%02x/%02x/%02x/%02x\n",
1714 path->depth, path->path[0], path->path[1],
1715 path->path[2], path->path[3], path->path[4]);
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001716 return true;
1717 }
1718 return false;
1719}
1720
Takashi Iwai4a796162011-06-17 17:53:38 +02001721static int via_auto_fill_dac_nids(struct hda_codec *codec)
1722{
1723 struct via_spec *spec = codec->spec;
1724 const struct auto_pin_cfg *cfg = &spec->autocfg;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001725 int i, dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001726 hda_nid_t nid;
1727
Joseph Chanc577b8a2006-11-29 15:29:40 +01001728 spec->multiout.dac_nids = spec->private_dac_nids;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001729 dac_num = 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001730 for (i = 0; i < cfg->line_outs; i++) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001731 hda_nid_t dac = 0;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001732 nid = cfg->line_out_pins[i];
Takashi Iwai4a796162011-06-17 17:53:38 +02001733 if (!nid)
1734 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001735 if (parse_output_path(codec, nid, 0, 0, &spec->out_path[i]))
1736 dac = spec->out_path[i].path[0];
1737 if (!i && parse_output_path(codec, nid, dac, 1,
1738 &spec->out_mix_path))
1739 dac = spec->out_mix_path.path[0];
1740 if (dac) {
1741 spec->private_dac_nids[i] = dac;
Lydia Wang5c9a5612011-07-08 14:03:43 +08001742 dac_num++;
1743 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001744 }
Takashi Iwai3214b962011-07-18 12:49:25 +02001745 if (!spec->out_path[0].depth && spec->out_mix_path.depth) {
1746 spec->out_path[0] = spec->out_mix_path;
1747 spec->out_mix_path.depth = 0;
1748 }
Lydia Wang5c9a5612011-07-08 14:03:43 +08001749 spec->multiout.num_dacs = dac_num;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001750 return 0;
1751}
1752
Takashi Iwai4a796162011-06-17 17:53:38 +02001753static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001754 int chs, bool check_dac, struct nid_path *path)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001755{
Takashi Iwai4a796162011-06-17 17:53:38 +02001756 struct via_spec *spec = codec->spec;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001757 char name[32];
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001758 hda_nid_t dac, pin, sel, nid;
1759 int err;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001760
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001761 dac = check_dac ? path->path[0] : 0;
1762 pin = path->path[path->depth - 1];
1763 sel = path->depth > 1 ? path->path[1] : 0;
Takashi Iwai4a796162011-06-17 17:53:38 +02001764
Takashi Iwai8df2a312011-06-21 11:48:29 +02001765 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001766 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001767 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
Takashi Iwai4a796162011-06-17 17:53:38 +02001768 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001769 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
1770 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001771 else
1772 nid = 0;
1773 if (nid) {
1774 sprintf(name, "%s Playback Volume", pfx);
1775 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Lydia Wanga00a5fa2011-06-21 16:11:11 +08001776 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
Takashi Iwai4a796162011-06-17 17:53:38 +02001777 if (err < 0)
1778 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001779 path->vol_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001780 }
1781
Takashi Iwai8df2a312011-06-21 11:48:29 +02001782 if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001783 nid = dac;
Takashi Iwai8df2a312011-06-21 11:48:29 +02001784 else if (check_amp_caps(codec, pin, HDA_OUTPUT, AC_AMPCAP_MUTE))
Takashi Iwai4a796162011-06-17 17:53:38 +02001785 nid = pin;
Takashi Iwaia934d5a2011-06-21 14:22:14 +02001786 else if (check_amp_caps(codec, sel, HDA_OUTPUT, AC_AMPCAP_MUTE))
1787 nid = sel;
Takashi Iwai4a796162011-06-17 17:53:38 +02001788 else
1789 nid = 0;
1790 if (nid) {
1791 sprintf(name, "%s Playback Switch", pfx);
1792 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
1793 HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
1794 if (err < 0)
1795 return err;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001796 path->mute_ctl = nid;
Takashi Iwai4a796162011-06-17 17:53:38 +02001797 }
1798 return 0;
1799}
1800
Takashi Iwaif4a78282011-06-17 18:46:48 +02001801static void mangle_smart51(struct hda_codec *codec)
1802{
1803 struct via_spec *spec = codec->spec;
1804 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001805 struct auto_pin_cfg_item *ins = cfg->inputs;
1806 int i, j, nums, attr;
1807 int pins[AUTO_CFG_MAX_INS];
Takashi Iwaif4a78282011-06-17 18:46:48 +02001808
Takashi Iwai0f98c242011-06-21 12:51:33 +02001809 for (attr = INPUT_PIN_ATTR_REAR; attr >= INPUT_PIN_ATTR_NORMAL; attr--) {
1810 nums = 0;
1811 for (i = 0; i < cfg->num_inputs; i++) {
1812 unsigned int def;
1813 if (ins[i].type > AUTO_PIN_LINE_IN)
1814 continue;
1815 def = snd_hda_codec_get_pincfg(codec, ins[i].pin);
1816 if (snd_hda_get_input_pin_attr(def) != attr)
1817 continue;
1818 for (j = 0; j < nums; j++)
1819 if (ins[pins[j]].type < ins[i].type) {
1820 memmove(pins + j + 1, pins + j,
Takashi Iwai21d45d22011-07-08 11:35:11 +02001821 (nums - j) * sizeof(int));
Takashi Iwai0f98c242011-06-21 12:51:33 +02001822 break;
1823 }
1824 pins[j] = i;
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001825 nums++;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001826 }
1827 if (cfg->line_outs + nums < 3)
Takashi Iwaif4a78282011-06-17 18:46:48 +02001828 continue;
Takashi Iwai0f98c242011-06-21 12:51:33 +02001829 for (i = 0; i < nums; i++) {
1830 hda_nid_t pin = ins[pins[i]].pin;
1831 spec->smart51_pins[spec->smart51_nums++] = pin;
1832 cfg->line_out_pins[cfg->line_outs++] = pin;
1833 if (cfg->line_outs == 3)
1834 break;
1835 }
1836 return;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001837 }
1838}
1839
Takashi Iwai4a796162011-06-17 17:53:38 +02001840/* add playback controls from the parsed DAC table */
1841static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
1842{
1843 struct via_spec *spec = codec->spec;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001844 struct auto_pin_cfg *cfg = &spec->autocfg;
Takashi Iwai3214b962011-07-18 12:49:25 +02001845 struct nid_path *path;
Takashi Iwaiea734962011-01-17 11:29:34 +01001846 static const char * const chname[4] = {
1847 "Front", "Surround", "C/LFE", "Side"
1848 };
Takashi Iwai4a796162011-06-17 17:53:38 +02001849 int i, idx, err;
Takashi Iwaif4a78282011-06-17 18:46:48 +02001850 int old_line_outs;
1851
1852 /* check smart51 */
1853 old_line_outs = cfg->line_outs;
1854 if (cfg->line_outs == 1)
1855 mangle_smart51(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001856
Takashi Iwaie3d7a142011-06-20 13:52:33 +02001857 err = via_auto_fill_dac_nids(codec);
1858 if (err < 0)
1859 return err;
1860
Lydia Wang5c9a5612011-07-08 14:03:43 +08001861 if (spec->multiout.num_dacs < 3) {
1862 spec->smart51_nums = 0;
1863 cfg->line_outs = old_line_outs;
1864 }
Takashi Iwai4a796162011-06-17 17:53:38 +02001865 for (i = 0; i < cfg->line_outs; i++) {
1866 hda_nid_t pin, dac;
1867 pin = cfg->line_out_pins[i];
1868 dac = spec->multiout.dac_nids[i];
1869 if (!pin || !dac)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001870 continue;
Takashi Iwai3214b962011-07-18 12:49:25 +02001871 path = spec->out_path + i;
Takashi Iwai0fe0adf2011-06-19 16:27:53 +02001872 if (i == HDA_CLFE) {
Takashi Iwai3214b962011-07-18 12:49:25 +02001873 err = create_ch_ctls(codec, "Center", 1, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001874 if (err < 0)
1875 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02001876 err = create_ch_ctls(codec, "LFE", 2, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001877 if (err < 0)
1878 return err;
1879 } else {
Takashi Iwai6aadf412011-06-20 14:09:02 +02001880 const char *pfx = chname[i];
1881 if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
1882 cfg->line_outs == 1)
1883 pfx = "Speaker";
Takashi Iwai3214b962011-07-18 12:49:25 +02001884 err = create_ch_ctls(codec, pfx, 3, true, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001885 if (err < 0)
1886 return err;
1887 }
Takashi Iwai3214b962011-07-18 12:49:25 +02001888 if (path != spec->out_path + i) {
1889 spec->out_path[i].vol_ctl = path->vol_ctl;
1890 spec->out_path[i].mute_ctl = path->mute_ctl;
1891 }
1892 if (path == spec->out_path && spec->out_mix_path.depth) {
1893 spec->out_mix_path.vol_ctl = path->vol_ctl;
1894 spec->out_mix_path.mute_ctl = path->mute_ctl;
1895 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001896 }
1897
Takashi Iwai4a796162011-06-17 17:53:38 +02001898 idx = get_connection_index(codec, spec->aa_mix_nid,
1899 spec->multiout.dac_nids[0]);
1900 if (idx >= 0) {
1901 /* add control to mixer */
Takashi Iwai3214b962011-07-18 12:49:25 +02001902 const char *name;
1903 name = spec->out_mix_path.depth ?
1904 "PCM Loopback Playback Volume" : "PCM Playback Volume";
1905 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02001906 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1907 idx, HDA_INPUT));
1908 if (err < 0)
1909 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02001910 name = spec->out_mix_path.depth ?
1911 "PCM Loopback Playback Switch" : "PCM Playback Switch";
1912 err = via_add_control(spec, VIA_CTL_WIDGET_MUTE, name,
Takashi Iwai4a796162011-06-17 17:53:38 +02001913 HDA_COMPOSE_AMP_VAL(spec->aa_mix_nid, 3,
1914 idx, HDA_INPUT));
1915 if (err < 0)
1916 return err;
1917 }
1918
Takashi Iwaif4a78282011-06-17 18:46:48 +02001919 cfg->line_outs = old_line_outs;
1920
Joseph Chanc577b8a2006-11-29 15:29:40 +01001921 return 0;
1922}
1923
Takashi Iwai4a796162011-06-17 17:53:38 +02001924static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01001925{
Takashi Iwai4a796162011-06-17 17:53:38 +02001926 struct via_spec *spec = codec->spec;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001927 struct nid_path *path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001928 bool check_dac;
Takashi Iwai3214b962011-07-18 12:49:25 +02001929 int i, err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01001930
1931 if (!pin)
1932 return 0;
1933
Takashi Iwai3214b962011-07-18 12:49:25 +02001934 if (!parse_output_path(codec, pin, 0, 0, &spec->hp_indep_path)) {
1935 for (i = HDA_SIDE; i >= HDA_CLFE; i--) {
1936 if (i < spec->multiout.num_dacs &&
1937 parse_output_path(codec, pin,
1938 spec->multiout.dac_nids[i], 0,
1939 &spec->hp_indep_path)) {
1940 spec->hp_indep_shared = i;
1941 break;
1942 }
1943 }
Takashi Iwai25250502011-06-30 17:24:47 +02001944 }
Takashi Iwai3214b962011-07-18 12:49:25 +02001945 if (spec->hp_indep_path.depth) {
1946 spec->hp_dac_nid = spec->hp_indep_path.path[0];
1947 if (!spec->hp_indep_shared)
1948 spec->hp_path = spec->hp_indep_path;
1949 }
1950 /* optionally check front-path w/o AA-mix */
1951 if (!spec->hp_path.depth)
1952 parse_output_path(codec, pin,
1953 spec->multiout.dac_nids[HDA_FRONT], 0,
1954 &spec->hp_path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001955
Takashi Iwaiece8d042011-06-19 16:24:21 +02001956 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
Takashi Iwai3214b962011-07-18 12:49:25 +02001957 1, &spec->hp_mix_path) && !spec->hp_path.depth)
Takashi Iwaiece8d042011-06-19 16:24:21 +02001958 return 0;
1959
Takashi Iwai3214b962011-07-18 12:49:25 +02001960 if (spec->hp_path.depth) {
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001961 path = &spec->hp_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001962 check_dac = true;
1963 } else {
Takashi Iwai3214b962011-07-18 12:49:25 +02001964 path = &spec->hp_mix_path;
Takashi Iwai18bd2c42011-07-04 15:55:44 +02001965 check_dac = false;
1966 }
1967 err = create_ch_ctls(codec, "Headphone", 3, check_dac, path);
Joseph Chanc577b8a2006-11-29 15:29:40 +01001968 if (err < 0)
1969 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02001970 if (check_dac) {
1971 spec->hp_mix_path.vol_ctl = path->vol_ctl;
1972 spec->hp_mix_path.mute_ctl = path->mute_ctl;
1973 } else {
1974 spec->hp_path.vol_ctl = path->vol_ctl;
1975 spec->hp_path.mute_ctl = path->mute_ctl;
Takashi Iwai09a9ad692011-06-21 15:57:44 +02001976 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01001977 return 0;
1978}
1979
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001980static int via_auto_create_speaker_ctls(struct hda_codec *codec)
1981{
1982 struct via_spec *spec = codec->spec;
Takashi Iwai3214b962011-07-18 12:49:25 +02001983 struct nid_path *path;
1984 bool check_dac;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001985 hda_nid_t pin, dac;
Takashi Iwai3214b962011-07-18 12:49:25 +02001986 int err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02001987
1988 pin = spec->autocfg.speaker_pins[0];
1989 if (!spec->autocfg.speaker_outs || !pin)
1990 return 0;
1991
Takashi Iwai3214b962011-07-18 12:49:25 +02001992 if (parse_output_path(codec, pin, 0, 0, &spec->speaker_path))
Takashi Iwai8e3679d2011-06-21 09:01:36 +02001993 dac = spec->speaker_path.path[0];
Takashi Iwai3214b962011-07-18 12:49:25 +02001994 if (!dac)
1995 parse_output_path(codec, pin,
1996 spec->multiout.dac_nids[HDA_FRONT], 0,
1997 &spec->speaker_path);
1998 if (!parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
1999 1, &spec->speaker_mix_path) && !dac)
2000 return 0;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002001
Takashi Iwai3214b962011-07-18 12:49:25 +02002002 /* no AA-path for front? */
2003 if (!spec->out_mix_path.depth && spec->speaker_mix_path.depth)
2004 dac = 0;
2005
2006 spec->speaker_dac_nid = dac;
2007 spec->multiout.extra_out_nid[0] = dac;
2008 if (dac) {
2009 path = &spec->speaker_path;
2010 check_dac = true;
2011 } else {
2012 path = &spec->speaker_mix_path;
2013 check_dac = false;
2014 }
2015 err = create_ch_ctls(codec, "Speaker", 3, check_dac, path);
2016 if (err < 0)
2017 return err;
2018 if (check_dac) {
2019 spec->speaker_mix_path.vol_ctl = path->vol_ctl;
2020 spec->speaker_mix_path.mute_ctl = path->mute_ctl;
2021 } else {
2022 spec->speaker_path.vol_ctl = path->vol_ctl;
2023 spec->speaker_path.mute_ctl = path->mute_ctl;
2024 }
2025 return 0;
2026}
2027
2028#define via_aamix_ctl_info via_pin_power_ctl_info
2029
2030static int via_aamix_ctl_get(struct snd_kcontrol *kcontrol,
2031 struct snd_ctl_elem_value *ucontrol)
2032{
2033 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2034 struct via_spec *spec = codec->spec;
2035 ucontrol->value.enumerated.item[0] = spec->aamix_mode;
2036 return 0;
2037}
2038
2039static void update_aamix_paths(struct hda_codec *codec, int do_mix,
2040 struct nid_path *nomix, struct nid_path *mix)
2041{
2042 if (do_mix) {
2043 activate_output_path(codec, nomix, false, false);
2044 activate_output_path(codec, mix, true, false);
2045 } else {
2046 activate_output_path(codec, mix, false, false);
2047 activate_output_path(codec, nomix, true, false);
2048 }
2049}
2050
2051static int via_aamix_ctl_put(struct snd_kcontrol *kcontrol,
2052 struct snd_ctl_elem_value *ucontrol)
2053{
2054 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2055 struct via_spec *spec = codec->spec;
2056 unsigned int val = ucontrol->value.enumerated.item[0];
2057
2058 if (val == spec->aamix_mode)
2059 return 0;
2060 spec->aamix_mode = val;
2061 /* update front path */
2062 update_aamix_paths(codec, val, &spec->out_path[0], &spec->out_mix_path);
2063 /* update HP path */
2064 if (!spec->hp_independent_mode) {
2065 update_aamix_paths(codec, val, &spec->hp_path,
2066 &spec->hp_mix_path);
2067 }
2068 /* update speaker path */
2069 update_aamix_paths(codec, val, &spec->speaker_path,
2070 &spec->speaker_mix_path);
2071 return 1;
2072}
2073
2074static const struct snd_kcontrol_new via_aamix_ctl_enum = {
2075 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2076 .name = "Loopback Mixing",
2077 .info = via_aamix_ctl_info,
2078 .get = via_aamix_ctl_get,
2079 .put = via_aamix_ctl_put,
2080};
2081
2082static int via_auto_create_loopback_switch(struct hda_codec *codec)
2083{
2084 struct via_spec *spec = codec->spec;
2085
2086 if (!spec->aa_mix_nid || !spec->out_mix_path.depth)
2087 return 0; /* no loopback switching available */
2088 if (!via_clone_control(spec, &via_aamix_ctl_enum))
2089 return -ENOMEM;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002090 return 0;
2091}
2092
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002093/* look for ADCs */
2094static int via_fill_adcs(struct hda_codec *codec)
2095{
2096 struct via_spec *spec = codec->spec;
2097 hda_nid_t nid = codec->start_nid;
2098 int i;
2099
2100 for (i = 0; i < codec->num_nodes; i++, nid++) {
2101 unsigned int wcaps = get_wcaps(codec, nid);
2102 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2103 continue;
2104 if (wcaps & AC_WCAP_DIGITAL)
2105 continue;
2106 if (!(wcaps & AC_WCAP_CONN_LIST))
2107 continue;
2108 if (spec->num_adc_nids >= ARRAY_SIZE(spec->adc_nids))
2109 return -ENOMEM;
2110 spec->adc_nids[spec->num_adc_nids++] = nid;
2111 }
2112 return 0;
2113}
2114
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002115/* input-src control */
2116static int via_mux_enum_info(struct snd_kcontrol *kcontrol,
2117 struct snd_ctl_elem_info *uinfo)
2118{
2119 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2120 struct via_spec *spec = codec->spec;
2121
2122 uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
2123 uinfo->count = 1;
2124 uinfo->value.enumerated.items = spec->num_inputs;
2125 if (uinfo->value.enumerated.item >= spec->num_inputs)
2126 uinfo->value.enumerated.item = spec->num_inputs - 1;
2127 strcpy(uinfo->value.enumerated.name,
2128 spec->inputs[uinfo->value.enumerated.item].label);
2129 return 0;
2130}
2131
2132static int via_mux_enum_get(struct snd_kcontrol *kcontrol,
2133 struct snd_ctl_elem_value *ucontrol)
2134{
2135 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2136 struct via_spec *spec = codec->spec;
2137 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2138
2139 ucontrol->value.enumerated.item[0] = spec->cur_mux[idx];
2140 return 0;
2141}
2142
2143static int via_mux_enum_put(struct snd_kcontrol *kcontrol,
2144 struct snd_ctl_elem_value *ucontrol)
2145{
2146 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2147 struct via_spec *spec = codec->spec;
2148 unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
2149 hda_nid_t mux;
2150 int cur;
2151
2152 cur = ucontrol->value.enumerated.item[0];
2153 if (cur < 0 || cur >= spec->num_inputs)
2154 return -EINVAL;
2155 if (spec->cur_mux[idx] == cur)
2156 return 0;
2157 spec->cur_mux[idx] = cur;
2158 if (spec->dyn_adc_switch) {
2159 int adc_idx = spec->inputs[cur].adc_idx;
2160 mux = spec->mux_nids[adc_idx];
2161 via_dyn_adc_pcm_resetup(codec, cur);
2162 } else {
2163 mux = spec->mux_nids[idx];
2164 if (snd_BUG_ON(!mux))
2165 return -EINVAL;
2166 }
2167
2168 if (mux) {
2169 /* switch to D0 beofre change index */
2170 if (snd_hda_codec_read(codec, mux, 0,
2171 AC_VERB_GET_POWER_STATE, 0x00) != AC_PWRST_D0)
2172 snd_hda_codec_write(codec, mux, 0,
2173 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
2174 snd_hda_codec_write(codec, mux, 0,
2175 AC_VERB_SET_CONNECT_SEL,
2176 spec->inputs[cur].mux_idx);
2177 }
2178
2179 /* update jack power state */
2180 set_widgets_power_state(codec);
2181 return 0;
2182}
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002183
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002184static const struct snd_kcontrol_new via_input_src_ctl = {
2185 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2186 /* The multiple "Capture Source" controls confuse alsamixer
2187 * So call somewhat different..
2188 */
2189 /* .name = "Capture Source", */
2190 .name = "Input Source",
2191 .info = via_mux_enum_info,
2192 .get = via_mux_enum_get,
2193 .put = via_mux_enum_put,
2194};
2195
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002196static int create_input_src_ctls(struct hda_codec *codec, int count)
2197{
2198 struct via_spec *spec = codec->spec;
2199 struct snd_kcontrol_new *knew;
2200
2201 if (spec->num_inputs <= 1 || !count)
2202 return 0; /* no need for single src */
2203
2204 knew = via_clone_control(spec, &via_input_src_ctl);
2205 if (!knew)
2206 return -ENOMEM;
2207 knew->count = count;
2208 return 0;
2209}
2210
2211/* add the powersave loopback-list entry */
Takashi Iwai13af8e72011-06-20 14:05:46 +02002212static void add_loopback_list(struct via_spec *spec, hda_nid_t mix, int idx)
2213{
2214 struct hda_amp_list *list;
2215
2216 if (spec->num_loopbacks >= ARRAY_SIZE(spec->loopback_list) - 1)
2217 return;
2218 list = spec->loopback_list + spec->num_loopbacks;
2219 list->nid = mix;
2220 list->dir = HDA_INPUT;
2221 list->idx = idx;
2222 spec->num_loopbacks++;
2223 spec->loopback.amplist = spec->loopback_list;
2224}
Takashi Iwai13af8e72011-06-20 14:05:46 +02002225
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002226static bool is_reachable_nid(struct hda_codec *codec, hda_nid_t src,
Takashi Iwai8d087c72011-06-28 12:45:47 +02002227 hda_nid_t dst)
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002228{
Takashi Iwai8d087c72011-06-28 12:45:47 +02002229 return snd_hda_get_conn_index(codec, src, dst, 1) >= 0;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002230}
2231
2232/* add the input-route to the given pin */
2233static bool add_input_route(struct hda_codec *codec, hda_nid_t pin)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002234{
Takashi Iwai10a20af2010-09-09 16:28:02 +02002235 struct via_spec *spec = codec->spec;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002236 int c, idx;
2237
2238 spec->inputs[spec->num_inputs].adc_idx = -1;
2239 spec->inputs[spec->num_inputs].pin = pin;
2240 for (c = 0; c < spec->num_adc_nids; c++) {
2241 if (spec->mux_nids[c]) {
2242 idx = get_connection_index(codec, spec->mux_nids[c],
2243 pin);
2244 if (idx < 0)
2245 continue;
2246 spec->inputs[spec->num_inputs].mux_idx = idx;
2247 } else {
Takashi Iwai8d087c72011-06-28 12:45:47 +02002248 if (!is_reachable_nid(codec, spec->adc_nids[c], pin))
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002249 continue;
2250 }
2251 spec->inputs[spec->num_inputs].adc_idx = c;
2252 /* Can primary ADC satisfy all inputs? */
2253 if (!spec->dyn_adc_switch &&
2254 spec->num_inputs > 0 && spec->inputs[0].adc_idx != c) {
2255 snd_printd(KERN_INFO
2256 "via: dynamic ADC switching enabled\n");
2257 spec->dyn_adc_switch = 1;
2258 }
2259 return true;
2260 }
2261 return false;
2262}
2263
2264static int get_mux_nids(struct hda_codec *codec);
2265
2266/* parse input-routes; fill ADCs, MUXs and input-src entries */
2267static int parse_analog_inputs(struct hda_codec *codec)
2268{
2269 struct via_spec *spec = codec->spec;
2270 const struct auto_pin_cfg *cfg = &spec->autocfg;
2271 int i, err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002272
2273 err = via_fill_adcs(codec);
2274 if (err < 0)
2275 return err;
2276 err = get_mux_nids(codec);
2277 if (err < 0)
2278 return err;
Takashi Iwaia766d0d2011-06-17 09:01:29 +02002279
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002280 /* fill all input-routes */
2281 for (i = 0; i < cfg->num_inputs; i++) {
2282 if (add_input_route(codec, cfg->inputs[i].pin))
2283 spec->inputs[spec->num_inputs++].label =
2284 hda_get_autocfg_input_label(codec, cfg, i);
Takashi Iwaif3268512010-08-30 11:00:19 +02002285 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002286
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002287 /* check for internal loopback recording */
2288 if (spec->aa_mix_nid &&
2289 add_input_route(codec, spec->aa_mix_nid))
2290 spec->inputs[spec->num_inputs++].label = "Stereo Mixer";
2291
2292 return 0;
2293}
2294
2295/* create analog-loopback volume/switch controls */
2296static int create_loopback_ctls(struct hda_codec *codec)
2297{
2298 struct via_spec *spec = codec->spec;
2299 const struct auto_pin_cfg *cfg = &spec->autocfg;
2300 const char *prev_label = NULL;
2301 int type_idx = 0;
2302 int i, j, err, idx;
2303
2304 if (!spec->aa_mix_nid)
2305 return 0;
2306
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002307 for (i = 0; i < cfg->num_inputs; i++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002308 hda_nid_t pin = cfg->inputs[i].pin;
2309 const char *label = hda_get_autocfg_input_label(codec, cfg, i);
2310
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002311 if (prev_label && !strcmp(label, prev_label))
Takashi Iwai7b315bb2010-08-30 13:06:30 +02002312 type_idx++;
2313 else
2314 type_idx = 0;
Takashi Iwai1e11cae2011-06-21 12:57:22 +02002315 prev_label = label;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002316 idx = get_connection_index(codec, spec->aa_mix_nid, pin);
2317 if (idx >= 0) {
Lydia Wang16922282011-03-22 16:24:10 +08002318 err = via_new_analog_input(spec, label, type_idx,
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002319 idx, spec->aa_mix_nid);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002320 if (err < 0)
2321 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002322 add_loopback_list(spec, spec->aa_mix_nid, idx);
Takashi Iwai13af8e72011-06-20 14:05:46 +02002323 }
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002324
2325 /* remember the label for smart51 control */
2326 for (j = 0; j < spec->smart51_nums; j++) {
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002327 if (spec->smart51_pins[j] == pin) {
Takashi Iwaie3d7a142011-06-20 13:52:33 +02002328 spec->smart51_idxs[j] = idx;
2329 spec->smart51_labels[j] = label;
2330 break;
2331 }
2332 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002333 }
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002334 return 0;
2335}
2336
2337/* create mic-boost controls (if present) */
2338static int create_mic_boost_ctls(struct hda_codec *codec)
2339{
2340 struct via_spec *spec = codec->spec;
2341 const struct auto_pin_cfg *cfg = &spec->autocfg;
2342 int i, err;
2343
2344 for (i = 0; i < cfg->num_inputs; i++) {
2345 hda_nid_t pin = cfg->inputs[i].pin;
2346 unsigned int caps;
2347 const char *label;
2348 char name[32];
2349
2350 if (cfg->inputs[i].type != AUTO_PIN_MIC)
2351 continue;
2352 caps = query_amp_caps(codec, pin, HDA_INPUT);
2353 if (caps == -1 || !(caps & AC_AMPCAP_NUM_STEPS))
2354 continue;
2355 label = hda_get_autocfg_input_label(codec, cfg, i);
2356 snprintf(name, sizeof(name), "%s Boost Volume", label);
2357 err = via_add_control(spec, VIA_CTL_WIDGET_VOL, name,
2358 HDA_COMPOSE_AMP_VAL(pin, 3, 0, HDA_INPUT));
2359 if (err < 0)
2360 return err;
2361 }
2362 return 0;
2363}
2364
2365/* create capture and input-src controls for multiple streams */
2366static int create_multi_adc_ctls(struct hda_codec *codec)
2367{
2368 struct via_spec *spec = codec->spec;
2369 int i, err;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002370
2371 /* create capture mixer elements */
2372 for (i = 0; i < spec->num_adc_nids; i++) {
2373 hda_nid_t adc = spec->adc_nids[i];
2374 err = __via_add_control(spec, VIA_CTL_WIDGET_VOL,
2375 "Capture Volume", i,
2376 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2377 HDA_INPUT));
2378 if (err < 0)
2379 return err;
2380 err = __via_add_control(spec, VIA_CTL_WIDGET_MUTE,
2381 "Capture Switch", i,
2382 HDA_COMPOSE_AMP_VAL(adc, 3, 0,
2383 HDA_INPUT));
2384 if (err < 0)
2385 return err;
2386 }
2387
2388 /* input-source control */
2389 for (i = 0; i < spec->num_adc_nids; i++)
2390 if (!spec->mux_nids[i])
2391 break;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002392 err = create_input_src_ctls(codec, i);
2393 if (err < 0)
2394 return err;
2395 return 0;
2396}
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002397
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002398/* bind capture volume/switch */
2399static struct snd_kcontrol_new via_bind_cap_vol_ctl =
2400 HDA_BIND_VOL("Capture Volume", 0);
2401static struct snd_kcontrol_new via_bind_cap_sw_ctl =
2402 HDA_BIND_SW("Capture Switch", 0);
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002403
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002404static int init_bind_ctl(struct via_spec *spec, struct hda_bind_ctls **ctl_ret,
2405 struct hda_ctl_ops *ops)
2406{
2407 struct hda_bind_ctls *ctl;
2408 int i;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002409
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002410 ctl = kzalloc(sizeof(*ctl) + sizeof(long) * 4, GFP_KERNEL);
2411 if (!ctl)
2412 return -ENOMEM;
2413 ctl->ops = ops;
2414 for (i = 0; i < spec->num_adc_nids; i++)
2415 ctl->values[i] =
2416 HDA_COMPOSE_AMP_VAL(spec->adc_nids[i], 3, 0, HDA_INPUT);
2417 *ctl_ret = ctl;
2418 return 0;
2419}
2420
2421/* create capture and input-src controls for dynamic ADC-switch case */
2422static int create_dyn_adc_ctls(struct hda_codec *codec)
2423{
2424 struct via_spec *spec = codec->spec;
2425 struct snd_kcontrol_new *knew;
2426 int err;
2427
2428 /* set up the bind capture ctls */
2429 err = init_bind_ctl(spec, &spec->bind_cap_vol, &snd_hda_bind_vol);
2430 if (err < 0)
2431 return err;
2432 err = init_bind_ctl(spec, &spec->bind_cap_sw, &snd_hda_bind_sw);
2433 if (err < 0)
2434 return err;
2435
2436 /* create capture mixer elements */
2437 knew = via_clone_control(spec, &via_bind_cap_vol_ctl);
2438 if (!knew)
2439 return -ENOMEM;
2440 knew->private_value = (long)spec->bind_cap_vol;
2441
2442 knew = via_clone_control(spec, &via_bind_cap_sw_ctl);
2443 if (!knew)
2444 return -ENOMEM;
2445 knew->private_value = (long)spec->bind_cap_sw;
2446
2447 /* input-source control */
2448 err = create_input_src_ctls(codec, 1);
2449 if (err < 0)
2450 return err;
2451 return 0;
2452}
2453
2454/* parse and create capture-related stuff */
2455static int via_auto_create_analog_input_ctls(struct hda_codec *codec)
2456{
2457 struct via_spec *spec = codec->spec;
2458 int err;
2459
2460 err = parse_analog_inputs(codec);
2461 if (err < 0)
2462 return err;
2463 if (spec->dyn_adc_switch)
2464 err = create_dyn_adc_ctls(codec);
2465 else
2466 err = create_multi_adc_ctls(codec);
2467 if (err < 0)
2468 return err;
2469 err = create_loopback_ctls(codec);
2470 if (err < 0)
2471 return err;
2472 err = create_mic_boost_ctls(codec);
2473 if (err < 0)
2474 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002475 return 0;
2476}
2477
Harald Welte76d9b0d2008-09-09 15:50:37 +08002478static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid)
2479{
2480 unsigned int def_conf;
2481 unsigned char seqassoc;
2482
Takashi Iwai2f334f92009-02-20 14:37:42 +01002483 def_conf = snd_hda_codec_get_pincfg(codec, nid);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002484 seqassoc = (unsigned char) get_defcfg_association(def_conf);
2485 seqassoc = (seqassoc << 4) | get_defcfg_sequence(def_conf);
Lydia Wang82ef9e42009-10-10 19:08:19 +08002486 if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE
2487 && (seqassoc == 0xf0 || seqassoc == 0xff)) {
2488 def_conf = def_conf & (~(AC_JACK_PORT_BOTH << 30));
2489 snd_hda_codec_set_pincfg(codec, nid, def_conf);
Harald Welte76d9b0d2008-09-09 15:50:37 +08002490 }
2491
2492 return;
2493}
2494
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002495static int vt1708_jack_detect_get(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002496 struct snd_ctl_elem_value *ucontrol)
2497{
2498 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2499 struct via_spec *spec = codec->spec;
2500
2501 if (spec->codec_type != VT1708)
2502 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002503 spec->vt1708_jack_detect =
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002504 !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1);
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002505 ucontrol->value.integer.value[0] = spec->vt1708_jack_detect;
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002506 return 0;
2507}
2508
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002509static int vt1708_jack_detect_put(struct snd_kcontrol *kcontrol,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002510 struct snd_ctl_elem_value *ucontrol)
2511{
2512 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
2513 struct via_spec *spec = codec->spec;
2514 int change;
2515
2516 if (spec->codec_type != VT1708)
2517 return 0;
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002518 spec->vt1708_jack_detect = ucontrol->value.integer.value[0];
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002519 change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8))
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002520 == !spec->vt1708_jack_detect;
2521 if (spec->vt1708_jack_detect) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002522 mute_aa_path(codec, 1);
2523 notify_aa_path_ctls(codec);
2524 }
2525 return change;
2526}
2527
Takashi Iwaie06e5a22011-06-17 15:46:13 +02002528static const struct snd_kcontrol_new vt1708_jack_detect_ctl = {
2529 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
2530 .name = "Jack Detect",
2531 .count = 1,
2532 .info = snd_ctl_boolean_mono_info,
2533 .get = vt1708_jack_detect_get,
2534 .put = vt1708_jack_detect_put,
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002535};
2536
Takashi Iwai12daef62011-06-18 17:45:49 +02002537static void fill_dig_outs(struct hda_codec *codec);
2538static void fill_dig_in(struct hda_codec *codec);
2539
2540static int via_parse_auto_config(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002541{
2542 struct via_spec *spec = codec->spec;
2543 int err;
2544
2545 err = snd_hda_parse_pin_def_config(codec, &spec->autocfg, NULL);
2546 if (err < 0)
2547 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002548 if (!spec->autocfg.line_outs && !spec->autocfg.hp_pins[0])
Takashi Iwai7f0df882011-06-18 17:33:34 +02002549 return -EINVAL;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002550
Takashi Iwai4a796162011-06-17 17:53:38 +02002551 err = via_auto_create_multi_out_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002552 if (err < 0)
2553 return err;
Takashi Iwai4a796162011-06-17 17:53:38 +02002554 err = via_auto_create_hp_ctls(codec, spec->autocfg.hp_pins[0]);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002555 if (err < 0)
2556 return err;
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002557 err = via_auto_create_speaker_ctls(codec);
2558 if (err < 0)
2559 return err;
Takashi Iwai3214b962011-07-18 12:49:25 +02002560 err = via_auto_create_loopback_switch(codec);
2561 if (err < 0)
2562 return err;
Takashi Iwaia86a88e2011-06-22 15:23:25 +02002563 err = via_auto_create_analog_input_ctls(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002564 if (err < 0)
2565 return err;
2566
2567 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
2568
Takashi Iwai12daef62011-06-18 17:45:49 +02002569 fill_dig_outs(codec);
2570 fill_dig_in(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002571
Takashi Iwai603c4012008-07-30 15:01:44 +02002572 if (spec->kctls.list)
2573 spec->mixers[spec->num_mixers++] = spec->kctls.list;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002574
Joseph Chanc577b8a2006-11-29 15:29:40 +01002575
Takashi Iwai3214b962011-07-18 12:49:25 +02002576 if (spec->hp_dac_nid && spec->hp_mix_path.depth) {
Takashi Iwaiece8d042011-06-19 16:24:21 +02002577 err = via_hp_build(codec);
2578 if (err < 0)
2579 return err;
2580 }
Joseph Chanc577b8a2006-11-29 15:29:40 +01002581
Takashi Iwaif4a78282011-06-17 18:46:48 +02002582 err = via_smart51_build(codec);
2583 if (err < 0)
2584 return err;
2585
Takashi Iwai5d417622011-06-20 11:32:27 +02002586 /* assign slave outs */
2587 if (spec->slave_dig_outs[0])
2588 codec->slave_dig_outs = spec->slave_dig_outs;
2589
Joseph Chanc577b8a2006-11-29 15:29:40 +01002590 return 1;
2591}
2592
Takashi Iwai5d417622011-06-20 11:32:27 +02002593static void via_auto_init_dig_outs(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002594{
Lydia Wang25eaba22009-10-10 19:08:43 +08002595 struct via_spec *spec = codec->spec;
Takashi Iwai5d417622011-06-20 11:32:27 +02002596 if (spec->multiout.dig_out_nid)
2597 init_output_pin(codec, spec->autocfg.dig_out_pins[0], PIN_OUT);
2598 if (spec->slave_dig_outs[0])
2599 init_output_pin(codec, spec->autocfg.dig_out_pins[1], PIN_OUT);
2600}
Lydia Wang25eaba22009-10-10 19:08:43 +08002601
Takashi Iwai5d417622011-06-20 11:32:27 +02002602static void via_auto_init_dig_in(struct hda_codec *codec)
2603{
2604 struct via_spec *spec = codec->spec;
2605 if (!spec->dig_in_nid)
2606 return;
2607 snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
2608 AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
2609}
2610
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002611/* initialize the unsolicited events */
2612static void via_auto_init_unsol_event(struct hda_codec *codec)
2613{
2614 struct via_spec *spec = codec->spec;
2615 struct auto_pin_cfg *cfg = &spec->autocfg;
2616 unsigned int ev;
2617 int i;
2618
2619 if (cfg->hp_pins[0] && is_jack_detectable(codec, cfg->hp_pins[0]))
2620 snd_hda_codec_write(codec, cfg->hp_pins[0], 0,
2621 AC_VERB_SET_UNSOLICITED_ENABLE,
2622 AC_USRSP_EN | VIA_HP_EVENT | VIA_JACK_EVENT);
2623
2624 if (cfg->speaker_pins[0])
2625 ev = VIA_LINE_EVENT;
2626 else
2627 ev = 0;
2628 for (i = 0; i < cfg->line_outs; i++) {
2629 if (cfg->line_out_pins[i] &&
2630 is_jack_detectable(codec, cfg->line_out_pins[i]))
Lydia Wang63f10d22011-06-28 17:29:10 +08002631 snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002632 AC_VERB_SET_UNSOLICITED_ENABLE,
2633 AC_USRSP_EN | ev | VIA_JACK_EVENT);
2634 }
2635
2636 for (i = 0; i < cfg->num_inputs; i++) {
2637 if (is_jack_detectable(codec, cfg->inputs[i].pin))
2638 snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
2639 AC_VERB_SET_UNSOLICITED_ENABLE,
2640 AC_USRSP_EN | VIA_JACK_EVENT);
2641 }
2642}
2643
Takashi Iwai5d417622011-06-20 11:32:27 +02002644static int via_init(struct hda_codec *codec)
2645{
2646 struct via_spec *spec = codec->spec;
2647 int i;
2648
2649 for (i = 0; i < spec->num_iverbs; i++)
2650 snd_hda_sequence_write(codec, spec->init_verbs[i]);
2651
Joseph Chanc577b8a2006-11-29 15:29:40 +01002652 via_auto_init_multi_out(codec);
2653 via_auto_init_hp_out(codec);
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002654 via_auto_init_speaker_out(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002655 via_auto_init_analog_input(codec);
Takashi Iwai5d417622011-06-20 11:32:27 +02002656 via_auto_init_dig_outs(codec);
2657 via_auto_init_dig_in(codec);
Lydia Wang118909562011-03-23 17:57:34 +08002658
Takashi Iwai4a918ff2011-06-20 12:39:26 +02002659 via_auto_init_unsol_event(codec);
2660
2661 via_hp_automute(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08002662
Joseph Chanc577b8a2006-11-29 15:29:40 +01002663 return 0;
2664}
2665
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002666static void vt1708_update_hp_jack_state(struct work_struct *work)
2667{
2668 struct via_spec *spec = container_of(work, struct via_spec,
2669 vt1708_hp_work.work);
2670 if (spec->codec_type != VT1708)
2671 return;
2672 /* if jack state toggled */
2673 if (spec->vt1708_hp_present
Takashi Iwaid56757a2009-11-18 08:00:14 +01002674 != snd_hda_jack_detect(spec->codec, spec->autocfg.hp_pins[0])) {
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002675 spec->vt1708_hp_present ^= 1;
2676 via_hp_automute(spec->codec);
2677 }
2678 vt1708_start_hp_work(spec);
2679}
2680
Takashi Iwai337b9d02009-07-07 18:18:59 +02002681static int get_mux_nids(struct hda_codec *codec)
2682{
2683 struct via_spec *spec = codec->spec;
2684 hda_nid_t nid, conn[8];
2685 unsigned int type;
2686 int i, n;
2687
2688 for (i = 0; i < spec->num_adc_nids; i++) {
2689 nid = spec->adc_nids[i];
2690 while (nid) {
Takashi Iwaia22d5432009-07-27 12:54:26 +02002691 type = get_wcaps_type(get_wcaps(codec, nid));
Takashi Iwai1c55d522009-07-08 07:45:46 +02002692 if (type == AC_WID_PIN)
2693 break;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002694 n = snd_hda_get_connections(codec, nid, conn,
2695 ARRAY_SIZE(conn));
2696 if (n <= 0)
2697 break;
2698 if (n > 1) {
2699 spec->mux_nids[i] = nid;
2700 break;
2701 }
2702 nid = conn[0];
2703 }
2704 }
Takashi Iwai1c55d522009-07-08 07:45:46 +02002705 return 0;
Takashi Iwai337b9d02009-07-07 18:18:59 +02002706}
2707
Joseph Chanc577b8a2006-11-29 15:29:40 +01002708static int patch_vt1708(struct hda_codec *codec)
2709{
2710 struct via_spec *spec;
2711 int err;
2712
2713 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002714 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002715 if (spec == NULL)
2716 return -ENOMEM;
2717
Takashi Iwai620e2b22011-06-17 17:19:19 +02002718 spec->aa_mix_nid = 0x17;
2719
Takashi Iwai12daef62011-06-18 17:45:49 +02002720 /* Add HP and CD pin config connect bit re-config action */
2721 vt1708_set_pinconfig_connect(codec, VT1708_HP_PIN_NID);
2722 vt1708_set_pinconfig_connect(codec, VT1708_CD_PIN_NID);
2723
Joseph Chanc577b8a2006-11-29 15:29:40 +01002724 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002725 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002726 if (err < 0) {
2727 via_free(codec);
2728 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002729 }
2730
Takashi Iwai12daef62011-06-18 17:45:49 +02002731 /* add jack detect on/off control */
2732 if (!via_clone_control(spec, &vt1708_jack_detect_ctl))
2733 return -ENOMEM;
2734
Takashi Iwaibc9b562382008-05-23 17:50:27 +02002735 /* disable 32bit format on VT1708 */
2736 if (codec->vendor_id == 0x11061708)
2737 spec->stream_analog_playback = &vt1708_pcm_analog_s16_playback;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002738
Lydia Wange322a362011-06-29 13:52:02 +08002739 spec->init_verbs[spec->num_iverbs++] = vt1708_init_verbs;
2740
Joseph Chanc577b8a2006-11-29 15:29:40 +01002741 codec->patch_ops = via_patch_ops;
2742
Lydia Wang1f2e99f2009-10-10 19:08:17 +08002743 INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002744 return 0;
2745}
2746
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002747static int patch_vt1709(struct hda_codec *codec)
Joseph Chanc577b8a2006-11-29 15:29:40 +01002748{
2749 struct via_spec *spec;
2750 int err;
2751
2752 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002753 spec = via_new_spec(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002754 if (spec == NULL)
2755 return -ENOMEM;
2756
Takashi Iwai620e2b22011-06-17 17:19:19 +02002757 spec->aa_mix_nid = 0x18;
2758
Takashi Iwai12daef62011-06-18 17:45:49 +02002759 err = via_parse_auto_config(codec);
Joseph Chanc577b8a2006-11-29 15:29:40 +01002760 if (err < 0) {
2761 via_free(codec);
2762 return err;
Joseph Chanc577b8a2006-11-29 15:29:40 +01002763 }
2764
Joseph Chanc577b8a2006-11-29 15:29:40 +01002765 codec->patch_ops = via_patch_ops;
2766
Josepch Chanf7278fd2007-12-13 16:40:40 +01002767 return 0;
2768}
2769
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002770static void set_widgets_power_state_vt1708B(struct hda_codec *codec)
2771{
2772 struct via_spec *spec = codec->spec;
2773 int imux_is_smixer;
2774 unsigned int parm;
2775 int is_8ch = 0;
Lydia Wangbc92df72011-03-23 17:56:05 +08002776 if ((spec->codec_type != VT1708B_4CH) &&
2777 (codec->vendor_id != 0x11064397))
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002778 is_8ch = 1;
2779
2780 /* SW0 (17h) = stereo mixer */
2781 imux_is_smixer =
2782 (snd_hda_codec_read(codec, 0x17, 0, AC_VERB_GET_CONNECT_SEL, 0x00)
2783 == ((spec->codec_type == VT1708S) ? 5 : 0));
2784 /* inputs */
2785 /* PW 1/2/5 (1ah/1bh/1eh) */
2786 parm = AC_PWRST_D3;
2787 set_pin_power_state(codec, 0x1a, &parm);
2788 set_pin_power_state(codec, 0x1b, &parm);
2789 set_pin_power_state(codec, 0x1e, &parm);
2790 if (imux_is_smixer)
2791 parm = AC_PWRST_D0;
2792 /* SW0 (17h), AIW 0/1 (13h/14h) */
2793 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
2794 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
2795 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
2796
2797 /* outputs */
2798 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
2799 parm = AC_PWRST_D3;
2800 set_pin_power_state(codec, 0x19, &parm);
2801 if (spec->smart51_enabled)
2802 set_pin_power_state(codec, 0x1b, &parm);
2803 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
2804 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
2805
2806 /* PW6 (22h), SW2 (26h), AOW2 (24h) */
2807 if (is_8ch) {
2808 parm = AC_PWRST_D3;
2809 set_pin_power_state(codec, 0x22, &parm);
2810 if (spec->smart51_enabled)
2811 set_pin_power_state(codec, 0x1a, &parm);
2812 snd_hda_codec_write(codec, 0x26, 0,
2813 AC_VERB_SET_POWER_STATE, parm);
2814 snd_hda_codec_write(codec, 0x24, 0,
2815 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002816 } else if (codec->vendor_id == 0x11064397) {
2817 /* PW7(23h), SW2(27h), AOW2(25h) */
2818 parm = AC_PWRST_D3;
2819 set_pin_power_state(codec, 0x23, &parm);
2820 if (spec->smart51_enabled)
2821 set_pin_power_state(codec, 0x1a, &parm);
2822 snd_hda_codec_write(codec, 0x27, 0,
2823 AC_VERB_SET_POWER_STATE, parm);
2824 snd_hda_codec_write(codec, 0x25, 0,
2825 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002826 }
2827
2828 /* PW 3/4/7 (1ch/1dh/23h) */
2829 parm = AC_PWRST_D3;
2830 /* force to D0 for internal Speaker */
2831 set_pin_power_state(codec, 0x1c, &parm);
2832 set_pin_power_state(codec, 0x1d, &parm);
2833 if (is_8ch)
2834 set_pin_power_state(codec, 0x23, &parm);
2835
2836 /* MW0 (16h), Sw3 (27h), AOW 0/3 (10h/25h) */
2837 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
2838 imux_is_smixer ? AC_PWRST_D0 : parm);
2839 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
2840 if (is_8ch) {
2841 snd_hda_codec_write(codec, 0x25, 0,
2842 AC_VERB_SET_POWER_STATE, parm);
2843 snd_hda_codec_write(codec, 0x27, 0,
2844 AC_VERB_SET_POWER_STATE, parm);
Lydia Wangbc92df72011-03-23 17:56:05 +08002845 } else if (codec->vendor_id == 0x11064397 && spec->hp_independent_mode)
2846 snd_hda_codec_write(codec, 0x25, 0,
2847 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002848}
2849
Lydia Wang518bf3b2009-10-10 19:07:29 +08002850static int patch_vt1708S(struct hda_codec *codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002851static int patch_vt1708B(struct hda_codec *codec)
Josepch Chanf7278fd2007-12-13 16:40:40 +01002852{
2853 struct via_spec *spec;
2854 int err;
2855
Lydia Wang518bf3b2009-10-10 19:07:29 +08002856 if (get_codec_type(codec) == VT1708BCE)
2857 return patch_vt1708S(codec);
Takashi Iwaiddd304d2011-06-21 16:33:55 +02002858
Josepch Chanf7278fd2007-12-13 16:40:40 +01002859 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002860 spec = via_new_spec(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002861 if (spec == NULL)
2862 return -ENOMEM;
2863
Takashi Iwai620e2b22011-06-17 17:19:19 +02002864 spec->aa_mix_nid = 0x16;
2865
Josepch Chanf7278fd2007-12-13 16:40:40 +01002866 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002867 err = via_parse_auto_config(codec);
Josepch Chanf7278fd2007-12-13 16:40:40 +01002868 if (err < 0) {
2869 via_free(codec);
2870 return err;
Josepch Chanf7278fd2007-12-13 16:40:40 +01002871 }
2872
Josepch Chanf7278fd2007-12-13 16:40:40 +01002873 codec->patch_ops = via_patch_ops;
2874
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002875 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
2876
Josepch Chanf7278fd2007-12-13 16:40:40 +01002877 return 0;
2878}
2879
Harald Welted949cac2008-09-09 15:56:01 +08002880/* Patch for VT1708S */
Takashi Iwai096a8852011-06-20 12:09:02 +02002881static const struct hda_verb vt1708S_init_verbs[] = {
Harald Welted7426322008-09-15 22:43:23 +08002882 /* Enable Mic Boost Volume backdoor */
2883 {0x1, 0xf98, 0x1},
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002884 /* don't bybass mixer */
2885 {0x1, 0xf88, 0xc0},
Harald Welted949cac2008-09-09 15:56:01 +08002886 { }
2887};
2888
Takashi Iwai9da29272009-05-07 16:31:14 +02002889/* fill out digital output widgets; one for master and one for slave outputs */
2890static void fill_dig_outs(struct hda_codec *codec)
2891{
2892 struct via_spec *spec = codec->spec;
2893 int i;
2894
2895 for (i = 0; i < spec->autocfg.dig_outs; i++) {
2896 hda_nid_t nid;
2897 int conn;
2898
2899 nid = spec->autocfg.dig_out_pins[i];
2900 if (!nid)
2901 continue;
2902 conn = snd_hda_get_connections(codec, nid, &nid, 1);
2903 if (conn < 1)
2904 continue;
2905 if (!spec->multiout.dig_out_nid)
2906 spec->multiout.dig_out_nid = nid;
2907 else {
2908 spec->slave_dig_outs[0] = nid;
2909 break; /* at most two dig outs */
2910 }
2911 }
2912}
2913
Takashi Iwai12daef62011-06-18 17:45:49 +02002914static void fill_dig_in(struct hda_codec *codec)
Harald Welted949cac2008-09-09 15:56:01 +08002915{
2916 struct via_spec *spec = codec->spec;
Takashi Iwai12daef62011-06-18 17:45:49 +02002917 hda_nid_t dig_nid;
2918 int i, err;
Harald Welted949cac2008-09-09 15:56:01 +08002919
Takashi Iwai12daef62011-06-18 17:45:49 +02002920 if (!spec->autocfg.dig_in_pin)
2921 return;
Harald Welted949cac2008-09-09 15:56:01 +08002922
Takashi Iwai12daef62011-06-18 17:45:49 +02002923 dig_nid = codec->start_nid;
2924 for (i = 0; i < codec->num_nodes; i++, dig_nid++) {
2925 unsigned int wcaps = get_wcaps(codec, dig_nid);
2926 if (get_wcaps_type(wcaps) != AC_WID_AUD_IN)
2927 continue;
2928 if (!(wcaps & AC_WCAP_DIGITAL))
2929 continue;
2930 if (!(wcaps & AC_WCAP_CONN_LIST))
2931 continue;
2932 err = get_connection_index(codec, dig_nid,
2933 spec->autocfg.dig_in_pin);
2934 if (err >= 0) {
2935 spec->dig_in_nid = dig_nid;
2936 break;
2937 }
2938 }
Harald Welted949cac2008-09-09 15:56:01 +08002939}
2940
Lydia Wang6369bcf2009-10-10 19:08:31 +08002941static void override_mic_boost(struct hda_codec *codec, hda_nid_t pin,
2942 int offset, int num_steps, int step_size)
2943{
2944 snd_hda_override_amp_caps(codec, pin, HDA_INPUT,
2945 (offset << AC_AMPCAP_OFFSET_SHIFT) |
2946 (num_steps << AC_AMPCAP_NUM_STEPS_SHIFT) |
2947 (step_size << AC_AMPCAP_STEP_SIZE_SHIFT) |
2948 (0 << AC_AMPCAP_MUTE_SHIFT));
2949}
2950
Harald Welted949cac2008-09-09 15:56:01 +08002951static int patch_vt1708S(struct hda_codec *codec)
2952{
2953 struct via_spec *spec;
2954 int err;
2955
2956 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01002957 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002958 if (spec == NULL)
2959 return -ENOMEM;
2960
Takashi Iwai620e2b22011-06-17 17:19:19 +02002961 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02002962 override_mic_boost(codec, 0x1a, 0, 3, 40);
2963 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02002964
Harald Welted949cac2008-09-09 15:56:01 +08002965 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02002966 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08002967 if (err < 0) {
2968 via_free(codec);
2969 return err;
Harald Welted949cac2008-09-09 15:56:01 +08002970 }
2971
Takashi Iwai096a8852011-06-20 12:09:02 +02002972 spec->init_verbs[spec->num_iverbs++] = vt1708S_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08002973
Harald Welted949cac2008-09-09 15:56:01 +08002974 codec->patch_ops = via_patch_ops;
2975
Lydia Wang518bf3b2009-10-10 19:07:29 +08002976 /* correct names for VT1708BCE */
2977 if (get_codec_type(codec) == VT1708BCE) {
2978 kfree(codec->chip_name);
2979 codec->chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
2980 snprintf(codec->bus->card->mixername,
2981 sizeof(codec->bus->card->mixername),
2982 "%s %s", codec->vendor_name, codec->chip_name);
Lydia Wang970f630f2011-03-22 16:25:56 +08002983 }
Lydia Wangbc92df72011-03-23 17:56:05 +08002984 /* correct names for VT1705 */
2985 if (codec->vendor_id == 0x11064397) {
2986 kfree(codec->chip_name);
2987 codec->chip_name = kstrdup("VT1705", GFP_KERNEL);
2988 snprintf(codec->bus->card->mixername,
2989 sizeof(codec->bus->card->mixername),
2990 "%s %s", codec->vendor_name, codec->chip_name);
2991 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08002992 spec->set_widgets_power_state = set_widgets_power_state_vt1708B;
Harald Welted949cac2008-09-09 15:56:01 +08002993 return 0;
2994}
2995
2996/* Patch for VT1702 */
2997
Takashi Iwai096a8852011-06-20 12:09:02 +02002998static const struct hda_verb vt1702_init_verbs[] = {
Lydia Wangbc7e7e52009-10-10 19:08:32 +08002999 /* mixer enable */
3000 {0x1, 0xF88, 0x3},
3001 /* GPIO 0~2 */
3002 {0x1, 0xF82, 0x3F},
Harald Welted949cac2008-09-09 15:56:01 +08003003 { }
3004};
3005
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003006static void set_widgets_power_state_vt1702(struct hda_codec *codec)
3007{
3008 int imux_is_smixer =
3009 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3010 unsigned int parm;
3011 /* inputs */
3012 /* PW 1/2/5 (14h/15h/18h) */
3013 parm = AC_PWRST_D3;
3014 set_pin_power_state(codec, 0x14, &parm);
3015 set_pin_power_state(codec, 0x15, &parm);
3016 set_pin_power_state(codec, 0x18, &parm);
3017 if (imux_is_smixer)
3018 parm = AC_PWRST_D0; /* SW0 (13h) = stereo mixer (idx 3) */
3019 /* SW0 (13h), AIW 0/1/2 (12h/1fh/20h) */
3020 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3021 snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_POWER_STATE, parm);
3022 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3023 snd_hda_codec_write(codec, 0x20, 0, AC_VERB_SET_POWER_STATE, parm);
3024
3025 /* outputs */
3026 /* PW 3/4 (16h/17h) */
3027 parm = AC_PWRST_D3;
3028 set_pin_power_state(codec, 0x17, &parm);
3029 set_pin_power_state(codec, 0x16, &parm);
3030 /* MW0 (1ah), AOW 0/1 (10h/1dh) */
3031 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE,
3032 imux_is_smixer ? AC_PWRST_D0 : parm);
3033 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3034 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3035}
3036
Harald Welted949cac2008-09-09 15:56:01 +08003037static int patch_vt1702(struct hda_codec *codec)
3038{
3039 struct via_spec *spec;
3040 int err;
Harald Welted949cac2008-09-09 15:56:01 +08003041
3042 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003043 spec = via_new_spec(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003044 if (spec == NULL)
3045 return -ENOMEM;
3046
Takashi Iwai620e2b22011-06-17 17:19:19 +02003047 spec->aa_mix_nid = 0x1a;
3048
Takashi Iwai12daef62011-06-18 17:45:49 +02003049 /* limit AA path volume to 0 dB */
3050 snd_hda_override_amp_caps(codec, 0x1A, HDA_INPUT,
3051 (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
3052 (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
3053 (0x5 << AC_AMPCAP_STEP_SIZE_SHIFT) |
3054 (1 << AC_AMPCAP_MUTE_SHIFT));
3055
Harald Welted949cac2008-09-09 15:56:01 +08003056 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003057 err = via_parse_auto_config(codec);
Harald Welted949cac2008-09-09 15:56:01 +08003058 if (err < 0) {
3059 via_free(codec);
3060 return err;
Harald Welted949cac2008-09-09 15:56:01 +08003061 }
3062
Takashi Iwai096a8852011-06-20 12:09:02 +02003063 spec->init_verbs[spec->num_iverbs++] = vt1702_init_verbs;
Harald Welted949cac2008-09-09 15:56:01 +08003064
Harald Welted949cac2008-09-09 15:56:01 +08003065 codec->patch_ops = via_patch_ops;
3066
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003067 spec->set_widgets_power_state = set_widgets_power_state_vt1702;
Harald Welted949cac2008-09-09 15:56:01 +08003068 return 0;
3069}
3070
Lydia Wangeb7188c2009-10-10 19:08:34 +08003071/* Patch for VT1718S */
3072
Takashi Iwai096a8852011-06-20 12:09:02 +02003073static const struct hda_verb vt1718S_init_verbs[] = {
Lydia Wang4ab2d532011-03-24 12:42:03 +08003074 /* Enable MW0 adjust Gain 5 */
3075 {0x1, 0xfb2, 0x10},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003076 /* Enable Boost Volume backdoor */
3077 {0x1, 0xf88, 0x8},
Takashi Iwai5d417622011-06-20 11:32:27 +02003078
Lydia Wangeb7188c2009-10-10 19:08:34 +08003079 { }
3080};
3081
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003082static void set_widgets_power_state_vt1718S(struct hda_codec *codec)
3083{
3084 struct via_spec *spec = codec->spec;
3085 int imux_is_smixer;
3086 unsigned int parm;
3087 /* MUX6 (1eh) = stereo mixer */
3088 imux_is_smixer =
3089 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3090 /* inputs */
3091 /* PW 5/6/7 (29h/2ah/2bh) */
3092 parm = AC_PWRST_D3;
3093 set_pin_power_state(codec, 0x29, &parm);
3094 set_pin_power_state(codec, 0x2a, &parm);
3095 set_pin_power_state(codec, 0x2b, &parm);
3096 if (imux_is_smixer)
3097 parm = AC_PWRST_D0;
3098 /* MUX6/7 (1eh/1fh), AIW 0/1 (10h/11h) */
3099 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3100 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3101 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3102 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3103
3104 /* outputs */
3105 /* PW3 (27h), MW2 (1ah), AOW3 (bh) */
3106 parm = AC_PWRST_D3;
3107 set_pin_power_state(codec, 0x27, &parm);
3108 snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_POWER_STATE, parm);
3109 snd_hda_codec_write(codec, 0xb, 0, AC_VERB_SET_POWER_STATE, parm);
3110
3111 /* PW2 (26h), AOW2 (ah) */
3112 parm = AC_PWRST_D3;
3113 set_pin_power_state(codec, 0x26, &parm);
3114 if (spec->smart51_enabled)
3115 set_pin_power_state(codec, 0x2b, &parm);
3116 snd_hda_codec_write(codec, 0xa, 0, AC_VERB_SET_POWER_STATE, parm);
3117
3118 /* PW0 (24h), AOW0 (8h) */
3119 parm = AC_PWRST_D3;
3120 set_pin_power_state(codec, 0x24, &parm);
3121 if (!spec->hp_independent_mode) /* check for redirected HP */
3122 set_pin_power_state(codec, 0x28, &parm);
3123 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3124 /* MW9 (21h), Mw2 (1ah), AOW0 (8h) */
3125 snd_hda_codec_write(codec, 0x21, 0, AC_VERB_SET_POWER_STATE,
3126 imux_is_smixer ? AC_PWRST_D0 : parm);
3127
3128 /* PW1 (25h), AOW1 (9h) */
3129 parm = AC_PWRST_D3;
3130 set_pin_power_state(codec, 0x25, &parm);
3131 if (spec->smart51_enabled)
3132 set_pin_power_state(codec, 0x2a, &parm);
3133 snd_hda_codec_write(codec, 0x9, 0, AC_VERB_SET_POWER_STATE, parm);
3134
3135 if (spec->hp_independent_mode) {
3136 /* PW4 (28h), MW3 (1bh), MUX1(34h), AOW4 (ch) */
3137 parm = AC_PWRST_D3;
3138 set_pin_power_state(codec, 0x28, &parm);
3139 snd_hda_codec_write(codec, 0x1b, 0,
3140 AC_VERB_SET_POWER_STATE, parm);
3141 snd_hda_codec_write(codec, 0x34, 0,
3142 AC_VERB_SET_POWER_STATE, parm);
3143 snd_hda_codec_write(codec, 0xc, 0,
3144 AC_VERB_SET_POWER_STATE, parm);
3145 }
3146}
3147
Takashi Iwai30b45032011-07-11 17:05:04 +02003148/* Add a connection to the primary DAC from AA-mixer for some codecs
3149 * This isn't listed from the raw info, but the chip has a secret connection.
3150 */
3151static int add_secret_dac_path(struct hda_codec *codec)
3152{
3153 struct via_spec *spec = codec->spec;
3154 int i, nums;
3155 hda_nid_t conn[8];
3156 hda_nid_t nid;
3157
3158 if (!spec->aa_mix_nid)
3159 return 0;
3160 nums = snd_hda_get_connections(codec, spec->aa_mix_nid, conn,
3161 ARRAY_SIZE(conn) - 1);
3162 for (i = 0; i < nums; i++) {
3163 if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
3164 return 0;
3165 }
3166
3167 /* find the primary DAC and add to the connection list */
3168 nid = codec->start_nid;
3169 for (i = 0; i < codec->num_nodes; i++, nid++) {
3170 unsigned int caps = get_wcaps(codec, nid);
3171 if (get_wcaps_type(caps) == AC_WID_AUD_OUT &&
3172 !(caps & AC_WCAP_DIGITAL)) {
3173 conn[nums++] = nid;
3174 return snd_hda_override_conn_list(codec,
3175 spec->aa_mix_nid,
3176 nums, conn);
3177 }
3178 }
3179 return 0;
3180}
3181
3182
Lydia Wangeb7188c2009-10-10 19:08:34 +08003183static int patch_vt1718S(struct hda_codec *codec)
3184{
3185 struct via_spec *spec;
3186 int err;
3187
3188 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003189 spec = via_new_spec(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003190 if (spec == NULL)
3191 return -ENOMEM;
3192
Takashi Iwai620e2b22011-06-17 17:19:19 +02003193 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003194 override_mic_boost(codec, 0x2b, 0, 3, 40);
3195 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003196 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003197
Lydia Wangeb7188c2009-10-10 19:08:34 +08003198 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003199 err = via_parse_auto_config(codec);
Lydia Wangeb7188c2009-10-10 19:08:34 +08003200 if (err < 0) {
3201 via_free(codec);
3202 return err;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003203 }
3204
Takashi Iwai096a8852011-06-20 12:09:02 +02003205 spec->init_verbs[spec->num_iverbs++] = vt1718S_init_verbs;
Lydia Wangeb7188c2009-10-10 19:08:34 +08003206
Lydia Wangeb7188c2009-10-10 19:08:34 +08003207 codec->patch_ops = via_patch_ops;
3208
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003209 spec->set_widgets_power_state = set_widgets_power_state_vt1718S;
3210
Lydia Wangeb7188c2009-10-10 19:08:34 +08003211 return 0;
3212}
Lydia Wangf3db4232009-10-10 19:08:41 +08003213
3214/* Patch for VT1716S */
3215
3216static int vt1716s_dmic_info(struct snd_kcontrol *kcontrol,
3217 struct snd_ctl_elem_info *uinfo)
3218{
3219 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
3220 uinfo->count = 1;
3221 uinfo->value.integer.min = 0;
3222 uinfo->value.integer.max = 1;
3223 return 0;
3224}
3225
3226static int vt1716s_dmic_get(struct snd_kcontrol *kcontrol,
3227 struct snd_ctl_elem_value *ucontrol)
3228{
3229 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3230 int index = 0;
3231
3232 index = snd_hda_codec_read(codec, 0x26, 0,
3233 AC_VERB_GET_CONNECT_SEL, 0);
3234 if (index != -1)
3235 *ucontrol->value.integer.value = index;
3236
3237 return 0;
3238}
3239
3240static int vt1716s_dmic_put(struct snd_kcontrol *kcontrol,
3241 struct snd_ctl_elem_value *ucontrol)
3242{
3243 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
3244 struct via_spec *spec = codec->spec;
3245 int index = *ucontrol->value.integer.value;
3246
3247 snd_hda_codec_write(codec, 0x26, 0,
3248 AC_VERB_SET_CONNECT_SEL, index);
3249 spec->dmic_enabled = index;
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003250 set_widgets_power_state(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003251 return 1;
3252}
3253
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003254static const struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003255 HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x22, 0x0, HDA_INPUT),
3256 {
3257 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
3258 .name = "Digital Mic Capture Switch",
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003259 .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
Lydia Wangf3db4232009-10-10 19:08:41 +08003260 .count = 1,
3261 .info = vt1716s_dmic_info,
3262 .get = vt1716s_dmic_get,
3263 .put = vt1716s_dmic_put,
3264 },
3265 {} /* end */
3266};
3267
3268
3269/* mono-out mixer elements */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003270static const struct snd_kcontrol_new vt1716S_mono_out_mixer[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003271 HDA_CODEC_MUTE("Mono Playback Switch", 0x2a, 0x0, HDA_OUTPUT),
3272 { } /* end */
3273};
3274
Takashi Iwai096a8852011-06-20 12:09:02 +02003275static const struct hda_verb vt1716S_init_verbs[] = {
Lydia Wangf3db4232009-10-10 19:08:41 +08003276 /* Enable Boost Volume backdoor */
3277 {0x1, 0xf8a, 0x80},
3278 /* don't bybass mixer */
3279 {0x1, 0xf88, 0xc0},
3280 /* Enable mono output */
3281 {0x1, 0xf90, 0x08},
3282 { }
3283};
3284
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003285static void set_widgets_power_state_vt1716S(struct hda_codec *codec)
3286{
3287 struct via_spec *spec = codec->spec;
3288 int imux_is_smixer;
3289 unsigned int parm;
3290 unsigned int mono_out, present;
3291 /* SW0 (17h) = stereo mixer */
3292 imux_is_smixer =
3293 (snd_hda_codec_read(codec, 0x17, 0,
3294 AC_VERB_GET_CONNECT_SEL, 0x00) == 5);
3295 /* inputs */
3296 /* PW 1/2/5 (1ah/1bh/1eh) */
3297 parm = AC_PWRST_D3;
3298 set_pin_power_state(codec, 0x1a, &parm);
3299 set_pin_power_state(codec, 0x1b, &parm);
3300 set_pin_power_state(codec, 0x1e, &parm);
3301 if (imux_is_smixer)
3302 parm = AC_PWRST_D0;
3303 /* SW0 (17h), AIW0(13h) */
3304 snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_POWER_STATE, parm);
3305 snd_hda_codec_write(codec, 0x13, 0, AC_VERB_SET_POWER_STATE, parm);
3306
3307 parm = AC_PWRST_D3;
3308 set_pin_power_state(codec, 0x1e, &parm);
3309 /* PW11 (22h) */
3310 if (spec->dmic_enabled)
3311 set_pin_power_state(codec, 0x22, &parm);
3312 else
3313 snd_hda_codec_write(codec, 0x22, 0,
3314 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3315
3316 /* SW2(26h), AIW1(14h) */
3317 snd_hda_codec_write(codec, 0x26, 0, AC_VERB_SET_POWER_STATE, parm);
3318 snd_hda_codec_write(codec, 0x14, 0, AC_VERB_SET_POWER_STATE, parm);
3319
3320 /* outputs */
3321 /* PW0 (19h), SW1 (18h), AOW1 (11h) */
3322 parm = AC_PWRST_D3;
3323 set_pin_power_state(codec, 0x19, &parm);
3324 /* Smart 5.1 PW2(1bh) */
3325 if (spec->smart51_enabled)
3326 set_pin_power_state(codec, 0x1b, &parm);
3327 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3328 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3329
3330 /* PW7 (23h), SW3 (27h), AOW3 (25h) */
3331 parm = AC_PWRST_D3;
3332 set_pin_power_state(codec, 0x23, &parm);
3333 /* Smart 5.1 PW1(1ah) */
3334 if (spec->smart51_enabled)
3335 set_pin_power_state(codec, 0x1a, &parm);
3336 snd_hda_codec_write(codec, 0x27, 0, AC_VERB_SET_POWER_STATE, parm);
3337
3338 /* Smart 5.1 PW5(1eh) */
3339 if (spec->smart51_enabled)
3340 set_pin_power_state(codec, 0x1e, &parm);
3341 snd_hda_codec_write(codec, 0x25, 0, AC_VERB_SET_POWER_STATE, parm);
3342
3343 /* Mono out */
3344 /* SW4(28h)->MW1(29h)-> PW12 (2ah)*/
3345 present = snd_hda_jack_detect(codec, 0x1c);
3346
3347 if (present)
3348 mono_out = 0;
3349 else {
3350 present = snd_hda_jack_detect(codec, 0x1d);
3351 if (!spec->hp_independent_mode && present)
3352 mono_out = 0;
3353 else
3354 mono_out = 1;
3355 }
3356 parm = mono_out ? AC_PWRST_D0 : AC_PWRST_D3;
3357 snd_hda_codec_write(codec, 0x28, 0, AC_VERB_SET_POWER_STATE, parm);
3358 snd_hda_codec_write(codec, 0x29, 0, AC_VERB_SET_POWER_STATE, parm);
3359 snd_hda_codec_write(codec, 0x2a, 0, AC_VERB_SET_POWER_STATE, parm);
3360
3361 /* PW 3/4 (1ch/1dh) */
3362 parm = AC_PWRST_D3;
3363 set_pin_power_state(codec, 0x1c, &parm);
3364 set_pin_power_state(codec, 0x1d, &parm);
3365 /* HP Independent Mode, power on AOW3 */
3366 if (spec->hp_independent_mode)
3367 snd_hda_codec_write(codec, 0x25, 0,
3368 AC_VERB_SET_POWER_STATE, parm);
3369
3370 /* force to D0 for internal Speaker */
3371 /* MW0 (16h), AOW0 (10h) */
3372 snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_POWER_STATE,
3373 imux_is_smixer ? AC_PWRST_D0 : parm);
3374 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE,
3375 mono_out ? AC_PWRST_D0 : parm);
3376}
3377
Lydia Wangf3db4232009-10-10 19:08:41 +08003378static int patch_vt1716S(struct hda_codec *codec)
3379{
3380 struct via_spec *spec;
3381 int err;
3382
3383 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003384 spec = via_new_spec(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003385 if (spec == NULL)
3386 return -ENOMEM;
3387
Takashi Iwai620e2b22011-06-17 17:19:19 +02003388 spec->aa_mix_nid = 0x16;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003389 override_mic_boost(codec, 0x1a, 0, 3, 40);
3390 override_mic_boost(codec, 0x1e, 0, 3, 40);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003391
Lydia Wangf3db4232009-10-10 19:08:41 +08003392 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003393 err = via_parse_auto_config(codec);
Lydia Wangf3db4232009-10-10 19:08:41 +08003394 if (err < 0) {
3395 via_free(codec);
3396 return err;
Lydia Wangf3db4232009-10-10 19:08:41 +08003397 }
3398
Takashi Iwai096a8852011-06-20 12:09:02 +02003399 spec->init_verbs[spec->num_iverbs++] = vt1716S_init_verbs;
Lydia Wangf3db4232009-10-10 19:08:41 +08003400
Lydia Wangf3db4232009-10-10 19:08:41 +08003401 spec->mixers[spec->num_mixers] = vt1716s_dmic_mixer;
3402 spec->num_mixers++;
3403
3404 spec->mixers[spec->num_mixers++] = vt1716S_mono_out_mixer;
3405
3406 codec->patch_ops = via_patch_ops;
3407
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003408 spec->set_widgets_power_state = set_widgets_power_state_vt1716S;
Lydia Wangf3db4232009-10-10 19:08:41 +08003409 return 0;
3410}
Lydia Wang25eaba22009-10-10 19:08:43 +08003411
3412/* for vt2002P */
3413
Takashi Iwai096a8852011-06-20 12:09:02 +02003414static const struct hda_verb vt2002P_init_verbs[] = {
Lydia Wangeadb9a82011-03-24 12:43:02 +08003415 /* Class-D speaker related verbs */
3416 {0x1, 0xfe0, 0x4},
3417 {0x1, 0xfe9, 0x80},
3418 {0x1, 0xfe2, 0x22},
Lydia Wang25eaba22009-10-10 19:08:43 +08003419 /* Enable Boost Volume backdoor */
3420 {0x1, 0xfb9, 0x24},
Lydia Wang25eaba22009-10-10 19:08:43 +08003421 /* Enable AOW0 to MW9 */
3422 {0x1, 0xfb8, 0x88},
3423 { }
3424};
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003425
Takashi Iwai096a8852011-06-20 12:09:02 +02003426static const struct hda_verb vt1802_init_verbs[] = {
Lydia Wang118909562011-03-23 17:57:34 +08003427 /* Enable Boost Volume backdoor */
3428 {0x1, 0xfb9, 0x24},
Lydia Wang118909562011-03-23 17:57:34 +08003429 /* Enable AOW0 to MW9 */
3430 {0x1, 0xfb8, 0x88},
3431 { }
3432};
Lydia Wang25eaba22009-10-10 19:08:43 +08003433
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003434static void set_widgets_power_state_vt2002P(struct hda_codec *codec)
3435{
3436 struct via_spec *spec = codec->spec;
3437 int imux_is_smixer;
3438 unsigned int parm;
3439 unsigned int present;
3440 /* MUX9 (1eh) = stereo mixer */
3441 imux_is_smixer =
3442 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3443 /* inputs */
3444 /* PW 5/6/7 (29h/2ah/2bh) */
3445 parm = AC_PWRST_D3;
3446 set_pin_power_state(codec, 0x29, &parm);
3447 set_pin_power_state(codec, 0x2a, &parm);
3448 set_pin_power_state(codec, 0x2b, &parm);
3449 parm = AC_PWRST_D0;
3450 /* MUX9/10 (1eh/1fh), AIW 0/1 (10h/11h) */
3451 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3452 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3453 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3454 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3455
3456 /* outputs */
3457 /* AOW0 (8h)*/
3458 snd_hda_codec_write(codec, 0x8, 0, AC_VERB_SET_POWER_STATE, parm);
3459
Lydia Wang118909562011-03-23 17:57:34 +08003460 if (spec->codec_type == VT1802) {
3461 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3462 parm = AC_PWRST_D3;
3463 set_pin_power_state(codec, 0x28, &parm);
3464 snd_hda_codec_write(codec, 0x18, 0,
3465 AC_VERB_SET_POWER_STATE, parm);
3466 snd_hda_codec_write(codec, 0x38, 0,
3467 AC_VERB_SET_POWER_STATE, parm);
3468 } else {
3469 /* PW4 (26h), MW4 (1ch), MUX4(37h) */
3470 parm = AC_PWRST_D3;
3471 set_pin_power_state(codec, 0x26, &parm);
3472 snd_hda_codec_write(codec, 0x1c, 0,
3473 AC_VERB_SET_POWER_STATE, parm);
3474 snd_hda_codec_write(codec, 0x37, 0,
3475 AC_VERB_SET_POWER_STATE, parm);
3476 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003477
Lydia Wang118909562011-03-23 17:57:34 +08003478 if (spec->codec_type == VT1802) {
3479 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3480 parm = AC_PWRST_D3;
3481 set_pin_power_state(codec, 0x25, &parm);
3482 snd_hda_codec_write(codec, 0x15, 0,
3483 AC_VERB_SET_POWER_STATE, parm);
3484 snd_hda_codec_write(codec, 0x35, 0,
3485 AC_VERB_SET_POWER_STATE, parm);
3486 } else {
3487 /* PW1 (25h), MW1 (19h), MUX1(35h), AOW1 (9h) */
3488 parm = AC_PWRST_D3;
3489 set_pin_power_state(codec, 0x25, &parm);
3490 snd_hda_codec_write(codec, 0x19, 0,
3491 AC_VERB_SET_POWER_STATE, parm);
3492 snd_hda_codec_write(codec, 0x35, 0,
3493 AC_VERB_SET_POWER_STATE, parm);
3494 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003495
3496 if (spec->hp_independent_mode)
3497 snd_hda_codec_write(codec, 0x9, 0,
3498 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3499
3500 /* Class-D */
3501 /* PW0 (24h), MW0(18h/14h), MUX0(34h) */
3502 present = snd_hda_jack_detect(codec, 0x25);
3503
3504 parm = AC_PWRST_D3;
3505 set_pin_power_state(codec, 0x24, &parm);
3506 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003507 if (spec->codec_type == VT1802)
3508 snd_hda_codec_write(codec, 0x14, 0,
3509 AC_VERB_SET_POWER_STATE, parm);
3510 else
3511 snd_hda_codec_write(codec, 0x18, 0,
3512 AC_VERB_SET_POWER_STATE, parm);
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003513 snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_POWER_STATE, parm);
3514
3515 /* Mono Out */
3516 present = snd_hda_jack_detect(codec, 0x26);
3517
3518 parm = present ? AC_PWRST_D3 : AC_PWRST_D0;
Lydia Wang118909562011-03-23 17:57:34 +08003519 if (spec->codec_type == VT1802) {
3520 /* PW15 (33h), MW8(1ch), MUX8(3ch) */
3521 snd_hda_codec_write(codec, 0x33, 0,
3522 AC_VERB_SET_POWER_STATE, parm);
3523 snd_hda_codec_write(codec, 0x1c, 0,
3524 AC_VERB_SET_POWER_STATE, parm);
3525 snd_hda_codec_write(codec, 0x3c, 0,
3526 AC_VERB_SET_POWER_STATE, parm);
3527 } else {
3528 /* PW15 (31h), MW8(17h), MUX8(3bh) */
3529 snd_hda_codec_write(codec, 0x31, 0,
3530 AC_VERB_SET_POWER_STATE, parm);
3531 snd_hda_codec_write(codec, 0x17, 0,
3532 AC_VERB_SET_POWER_STATE, parm);
3533 snd_hda_codec_write(codec, 0x3b, 0,
3534 AC_VERB_SET_POWER_STATE, parm);
3535 }
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003536 /* MW9 (21h) */
3537 if (imux_is_smixer || !is_aa_path_mute(codec))
3538 snd_hda_codec_write(codec, 0x21, 0,
3539 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3540 else
3541 snd_hda_codec_write(codec, 0x21, 0,
3542 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3543}
Lydia Wang25eaba22009-10-10 19:08:43 +08003544
3545/* patch for vt2002P */
3546static int patch_vt2002P(struct hda_codec *codec)
3547{
3548 struct via_spec *spec;
3549 int err;
3550
3551 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003552 spec = via_new_spec(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003553 if (spec == NULL)
3554 return -ENOMEM;
3555
Takashi Iwai620e2b22011-06-17 17:19:19 +02003556 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003557 override_mic_boost(codec, 0x2b, 0, 3, 40);
3558 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003559 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003560
Lydia Wang25eaba22009-10-10 19:08:43 +08003561 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003562 err = via_parse_auto_config(codec);
Lydia Wang25eaba22009-10-10 19:08:43 +08003563 if (err < 0) {
3564 via_free(codec);
3565 return err;
Lydia Wang25eaba22009-10-10 19:08:43 +08003566 }
3567
Lydia Wang118909562011-03-23 17:57:34 +08003568 if (spec->codec_type == VT1802)
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003569 spec->init_verbs[spec->num_iverbs++] = vt1802_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003570 else
Takashi Iwai4a918ff2011-06-20 12:39:26 +02003571 spec->init_verbs[spec->num_iverbs++] = vt2002P_init_verbs;
Lydia Wang118909562011-03-23 17:57:34 +08003572
Lydia Wang25eaba22009-10-10 19:08:43 +08003573 codec->patch_ops = via_patch_ops;
3574
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003575 spec->set_widgets_power_state = set_widgets_power_state_vt2002P;
Lydia Wang25eaba22009-10-10 19:08:43 +08003576 return 0;
3577}
Lydia Wangab6734e2009-10-10 19:08:46 +08003578
3579/* for vt1812 */
3580
Takashi Iwai096a8852011-06-20 12:09:02 +02003581static const struct hda_verb vt1812_init_verbs[] = {
Lydia Wangab6734e2009-10-10 19:08:46 +08003582 /* Enable Boost Volume backdoor */
3583 {0x1, 0xfb9, 0x24},
Lydia Wangab6734e2009-10-10 19:08:46 +08003584 /* Enable AOW0 to MW9 */
3585 {0x1, 0xfb8, 0xa8},
3586 { }
3587};
3588
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003589static void set_widgets_power_state_vt1812(struct hda_codec *codec)
3590{
3591 struct via_spec *spec = codec->spec;
3592 int imux_is_smixer =
3593 snd_hda_codec_read(codec, 0x13, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 3;
3594 unsigned int parm;
3595 unsigned int present;
3596 /* MUX10 (1eh) = stereo mixer */
3597 imux_is_smixer =
3598 snd_hda_codec_read(codec, 0x1e, 0, AC_VERB_GET_CONNECT_SEL, 0x00) == 5;
3599 /* inputs */
3600 /* PW 5/6/7 (29h/2ah/2bh) */
3601 parm = AC_PWRST_D3;
3602 set_pin_power_state(codec, 0x29, &parm);
3603 set_pin_power_state(codec, 0x2a, &parm);
3604 set_pin_power_state(codec, 0x2b, &parm);
3605 parm = AC_PWRST_D0;
3606 /* MUX10/11 (1eh/1fh), AIW 0/1 (10h/11h) */
3607 snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_POWER_STATE, parm);
3608 snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_POWER_STATE, parm);
3609 snd_hda_codec_write(codec, 0x10, 0, AC_VERB_SET_POWER_STATE, parm);
3610 snd_hda_codec_write(codec, 0x11, 0, AC_VERB_SET_POWER_STATE, parm);
3611
3612 /* outputs */
3613 /* AOW0 (8h)*/
3614 snd_hda_codec_write(codec, 0x8, 0,
3615 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3616
3617 /* PW4 (28h), MW4 (18h), MUX4(38h) */
3618 parm = AC_PWRST_D3;
3619 set_pin_power_state(codec, 0x28, &parm);
3620 snd_hda_codec_write(codec, 0x18, 0, AC_VERB_SET_POWER_STATE, parm);
3621 snd_hda_codec_write(codec, 0x38, 0, AC_VERB_SET_POWER_STATE, parm);
3622
3623 /* PW1 (25h), MW1 (15h), MUX1(35h), AOW1 (9h) */
3624 parm = AC_PWRST_D3;
3625 set_pin_power_state(codec, 0x25, &parm);
3626 snd_hda_codec_write(codec, 0x15, 0, AC_VERB_SET_POWER_STATE, parm);
3627 snd_hda_codec_write(codec, 0x35, 0, AC_VERB_SET_POWER_STATE, parm);
3628 if (spec->hp_independent_mode)
3629 snd_hda_codec_write(codec, 0x9, 0,
3630 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3631
3632 /* Internal Speaker */
3633 /* PW0 (24h), MW0(14h), MUX0(34h) */
3634 present = snd_hda_jack_detect(codec, 0x25);
3635
3636 parm = AC_PWRST_D3;
3637 set_pin_power_state(codec, 0x24, &parm);
3638 if (present) {
3639 snd_hda_codec_write(codec, 0x14, 0,
3640 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3641 snd_hda_codec_write(codec, 0x34, 0,
3642 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3643 } else {
3644 snd_hda_codec_write(codec, 0x14, 0,
3645 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3646 snd_hda_codec_write(codec, 0x34, 0,
3647 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3648 }
3649
3650
3651 /* Mono Out */
3652 /* PW13 (31h), MW13(1ch), MUX13(3ch), MW14(3eh) */
3653 present = snd_hda_jack_detect(codec, 0x28);
3654
3655 parm = AC_PWRST_D3;
3656 set_pin_power_state(codec, 0x31, &parm);
3657 if (present) {
3658 snd_hda_codec_write(codec, 0x1c, 0,
3659 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3660 snd_hda_codec_write(codec, 0x3c, 0,
3661 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3662 snd_hda_codec_write(codec, 0x3e, 0,
3663 AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
3664 } else {
3665 snd_hda_codec_write(codec, 0x1c, 0,
3666 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3667 snd_hda_codec_write(codec, 0x3c, 0,
3668 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3669 snd_hda_codec_write(codec, 0x3e, 0,
3670 AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
3671 }
3672
3673 /* PW15 (33h), MW15 (1dh), MUX15(3dh) */
3674 parm = AC_PWRST_D3;
3675 set_pin_power_state(codec, 0x33, &parm);
3676 snd_hda_codec_write(codec, 0x1d, 0, AC_VERB_SET_POWER_STATE, parm);
3677 snd_hda_codec_write(codec, 0x3d, 0, AC_VERB_SET_POWER_STATE, parm);
3678
3679}
Lydia Wangab6734e2009-10-10 19:08:46 +08003680
3681/* patch for vt1812 */
3682static int patch_vt1812(struct hda_codec *codec)
3683{
3684 struct via_spec *spec;
3685 int err;
3686
3687 /* create a codec specific record */
Jaroslav Kysela5b0cb1d2009-12-08 16:13:32 +01003688 spec = via_new_spec(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003689 if (spec == NULL)
3690 return -ENOMEM;
3691
Takashi Iwai620e2b22011-06-17 17:19:19 +02003692 spec->aa_mix_nid = 0x21;
Takashi Iwaid7a99cc2011-06-18 17:24:46 +02003693 override_mic_boost(codec, 0x2b, 0, 3, 40);
3694 override_mic_boost(codec, 0x29, 0, 3, 40);
Takashi Iwai30b45032011-07-11 17:05:04 +02003695 add_secret_dac_path(codec);
Takashi Iwai620e2b22011-06-17 17:19:19 +02003696
Lydia Wangab6734e2009-10-10 19:08:46 +08003697 /* automatic parse from the BIOS config */
Takashi Iwai12daef62011-06-18 17:45:49 +02003698 err = via_parse_auto_config(codec);
Lydia Wangab6734e2009-10-10 19:08:46 +08003699 if (err < 0) {
3700 via_free(codec);
3701 return err;
Lydia Wangab6734e2009-10-10 19:08:46 +08003702 }
3703
Takashi Iwai096a8852011-06-20 12:09:02 +02003704 spec->init_verbs[spec->num_iverbs++] = vt1812_init_verbs;
Lydia Wangab6734e2009-10-10 19:08:46 +08003705
Lydia Wangab6734e2009-10-10 19:08:46 +08003706 codec->patch_ops = via_patch_ops;
3707
Lydia Wang3e95b9a2011-03-23 15:13:28 +08003708 spec->set_widgets_power_state = set_widgets_power_state_vt1812;
Lydia Wangab6734e2009-10-10 19:08:46 +08003709 return 0;
3710}
3711
Joseph Chanc577b8a2006-11-29 15:29:40 +01003712/*
3713 * patch entries
3714 */
Takashi Iwai90dd48a2011-05-02 12:38:19 +02003715static const struct hda_codec_preset snd_hda_preset_via[] = {
Takashi Iwai3218c172008-12-18 09:17:56 +01003716 { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
3717 { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
3718 { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
3719 { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
3720 { .id = 0x1106e710, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003721 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003722 { .id = 0x1106e711, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003723 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003724 { .id = 0x1106e712, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003725 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003726 { .id = 0x1106e713, .name = "VT1709 10-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003727 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003728 { .id = 0x1106e714, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003729 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003730 { .id = 0x1106e715, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003731 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003732 { .id = 0x1106e716, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003733 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003734 { .id = 0x1106e717, .name = "VT1709 6-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003735 .patch = patch_vt1709},
Takashi Iwai3218c172008-12-18 09:17:56 +01003736 { .id = 0x1106e720, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003737 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003738 { .id = 0x1106e721, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003739 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003740 { .id = 0x1106e722, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003741 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003742 { .id = 0x1106e723, .name = "VT1708B 8-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003743 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003744 { .id = 0x1106e724, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003745 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003746 { .id = 0x1106e725, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003747 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003748 { .id = 0x1106e726, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003749 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003750 { .id = 0x1106e727, .name = "VT1708B 4-Ch",
Takashi Iwaiddd304d2011-06-21 16:33:55 +02003751 .patch = patch_vt1708B},
Takashi Iwai3218c172008-12-18 09:17:56 +01003752 { .id = 0x11060397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003753 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003754 { .id = 0x11061397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003755 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003756 { .id = 0x11062397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003757 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003758 { .id = 0x11063397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003759 .patch = patch_vt1708S},
Lydia Wangbc92df72011-03-23 17:56:05 +08003760 { .id = 0x11064397, .name = "VT1705",
Harald Welted949cac2008-09-09 15:56:01 +08003761 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003762 { .id = 0x11065397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003763 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003764 { .id = 0x11066397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003765 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003766 { .id = 0x11067397, .name = "VT1708S",
Harald Welted949cac2008-09-09 15:56:01 +08003767 .patch = patch_vt1708S},
Takashi Iwai3218c172008-12-18 09:17:56 +01003768 { .id = 0x11060398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003769 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003770 { .id = 0x11061398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003771 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003772 { .id = 0x11062398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003773 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003774 { .id = 0x11063398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003775 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003776 { .id = 0x11064398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003777 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003778 { .id = 0x11065398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003779 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003780 { .id = 0x11066398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003781 .patch = patch_vt1702},
Takashi Iwai3218c172008-12-18 09:17:56 +01003782 { .id = 0x11067398, .name = "VT1702",
Harald Welted949cac2008-09-09 15:56:01 +08003783 .patch = patch_vt1702},
Lydia Wangeb7188c2009-10-10 19:08:34 +08003784 { .id = 0x11060428, .name = "VT1718S",
3785 .patch = patch_vt1718S},
3786 { .id = 0x11064428, .name = "VT1718S",
3787 .patch = patch_vt1718S},
Lydia Wangbb3c6bfc2009-10-10 19:08:39 +08003788 { .id = 0x11060441, .name = "VT2020",
3789 .patch = patch_vt1718S},
3790 { .id = 0x11064441, .name = "VT1828S",
3791 .patch = patch_vt1718S},
Lydia Wangf3db4232009-10-10 19:08:41 +08003792 { .id = 0x11060433, .name = "VT1716S",
3793 .patch = patch_vt1716S},
3794 { .id = 0x1106a721, .name = "VT1716S",
3795 .patch = patch_vt1716S},
Lydia Wang25eaba22009-10-10 19:08:43 +08003796 { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
3797 { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
Lydia Wangab6734e2009-10-10 19:08:46 +08003798 { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
Lydia Wang36dd5c42009-10-20 13:18:04 +08003799 { .id = 0x11060440, .name = "VT1818S",
3800 .patch = patch_vt1708S},
Lydia Wang118909562011-03-23 17:57:34 +08003801 { .id = 0x11060446, .name = "VT1802",
3802 .patch = patch_vt2002P},
3803 { .id = 0x11068446, .name = "VT1802",
3804 .patch = patch_vt2002P},
Joseph Chanc577b8a2006-11-29 15:29:40 +01003805 {} /* terminator */
3806};
Takashi Iwai1289e9e2008-11-27 15:47:11 +01003807
3808MODULE_ALIAS("snd-hda-codec-id:1106*");
3809
3810static struct hda_codec_preset_list via_list = {
3811 .preset = snd_hda_preset_via,
3812 .owner = THIS_MODULE,
3813};
3814
3815MODULE_LICENSE("GPL");
3816MODULE_DESCRIPTION("VIA HD-audio codec");
3817
3818static int __init patch_via_init(void)
3819{
3820 return snd_hda_add_codec_preset(&via_list);
3821}
3822
3823static void __exit patch_via_exit(void)
3824{
3825 snd_hda_delete_codec_preset(&via_list);
3826}
3827
3828module_init(patch_via_init)
3829module_exit(patch_via_exit)